import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, simpledialog
import json
import os

# 配置文件路径（持久化存储公式）
FORMULA_FILE = "math_formulas.json"


class MathFormulaQueryApp:
    def __init__(self, root):
        self.root = root
        self.root.title("实用数学公式查询工具")
        self.root.geometry("900x700")
        self.root.minsize(800, 600)  # 设置最小窗口尺寸

        # 加载本地公式数据（首次运行自动创建默认数据）
        self.formulas = self.load_formulas()
        self.current_category = tk.StringVar()
        self.current_selected_formula = None  # 记录当前选中的公式

        # 创建界面
        self.create_widgets()
        # 初始化显示第一个分类
        self.init_category_display()

    def load_formulas(self):
        """加载本地公式文件，无文件则创建默认数据"""
        default_formulas = {
            "代数": {
                "一元二次方程求根公式": "对于方程 ax² + bx + c = 0 (a≠0)，根为：x = [-b ± √(b²-4ac)] / 2a",
                "平方差公式": "a² - b² = (a + b)(a - b)",
                "完全平方公式": "(a ± b)² = a² ± 2ab + b²"
            },
            "几何": {
                "圆的面积": "S = πr² (r为半径)",
                "圆的周长": "C = 2πr 或 C = πd (d为直径)",
                "三角形面积": "S = (1/2)ah (a为底，h为高) 或 海伦公式：S = √[p(p-a)(p-b)(p-c)]，p=(a+b+c)/2"
            },
            "微积分": {
                "导数定义": "f'(x₀) = lim(Δx→0) [f(x₀+Δx) - f(x₀)] / Δx",
                "基本求导公式(xⁿ)'": "(xⁿ)' = n·xⁿ⁻¹"
            }
        }

        if os.path.exists(FORMULA_FILE):
            try:
                with open(FORMULA_FILE, "r", encoding="utf-8") as f:
                    return json.load(f)
            except (json.JSONDecodeError, IOError):
                messagebox.warning("提示", "本地公式文件损坏，将使用默认数据！")
                return default_formulas
        else:
            # 首次运行保存默认数据
            self.save_formulas(default_formulas)
            return default_formulas

    def save_formulas(self, formulas=None):
        """保存公式到本地文件"""
        if formulas is None:
            formulas = self.formulas
        try:
            with open(FORMULA_FILE, "w", encoding="utf-8") as f:
                json.dump(formulas, f, ensure_ascii=False, indent=4)
            return True
        except IOError:
            messagebox.showerror("错误", "保存公式失败！")
            return False

    def create_widgets(self):
        """创建所有界面组件"""
        # ========== 顶部工具栏 ==========
        toolbar = ttk.Frame(self.root)
        toolbar.pack(fill=tk.X, padx=10, pady=5)

        # 搜索框
        ttk.Label(toolbar, text="搜索：").pack(side=tk.LEFT, padx=2)
        self.search_entry = ttk.Entry(toolbar, width=30)
        self.search_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        ttk.Button(toolbar, text="搜索", command=self.search_formula).pack(
            side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="重置", command=self.reset_search).pack(
            side=tk.LEFT, padx=2)

        # 功能按钮（靠右）
        ttk.Button(toolbar, text="添加公式", command=self.add_formula).pack(
            side=tk.RIGHT, padx=5)
        ttk.Button(toolbar, text="编辑公式", command=self.edit_formula).pack(
            side=tk.RIGHT, padx=5)
        ttk.Button(toolbar, text="删除公式", command=self.delete_formula).pack(
            side=tk.RIGHT, padx=5)
        ttk.Button(toolbar, text="复制公式", command=self.copy_formula).pack(
            side=tk.RIGHT, padx=5)

        # ========== 左侧分类面板 ==========
        left_frame = ttk.Frame(self.root)
        left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=5)

        ttk.Label(left_frame, text="公式分类", font=(
            "微软雅黑", 12, "bold")).pack(pady=5)

        # 分类列表（滚动）
        self.category_canvas = tk.Canvas(left_frame, width=150, height=400)
        category_scroll = ttk.Scrollbar(
            left_frame, orient=tk.VERTICAL, command=self.category_canvas.yview)
        self.category_frame = ttk.Frame(self.category_canvas)

        self.category_frame.bind("<Configure>", lambda e: self.category_canvas.configure(
            scrollregion=self.category_canvas.bbox("all")))
        self.category_canvas.create_window(
            (0, 0), window=self.category_frame, anchor="nw")
        self.category_canvas.configure(yscrollcommand=category_scroll.set)

        self.category_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        category_scroll.pack(side=tk.RIGHT, fill=tk.Y)

        # ========== 中间公式列表 + 右侧详情 ==========
        main_frame = ttk.Frame(self.root)
        main_frame.pack(side=tk.LEFT, fill=tk.BOTH,
                        expand=True, padx=10, pady=5)

        # 公式列表
        list_frame = ttk.Frame(main_frame)
        list_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        ttk.Label(list_frame, text="公式列表", font=(
            "微软雅黑", 12, "bold")).pack(pady=5)

        self.formula_listbox = tk.Listbox(
            list_frame, font=("微软雅黑", 10), selectmode=tk.SINGLE)
        self.formula_listbox.pack(fill=tk.BOTH, expand=True, padx=5)
        self.formula_listbox.bind(
            '<<ListboxSelect>>', self.show_formula_detail)

        # 公式详情
        detail_frame = ttk.Frame(main_frame)
        detail_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        ttk.Label(detail_frame, text="公式详情", font=(
            "微软雅黑", 12, "bold")).pack(pady=5)

        self.detail_text = scrolledtext.ScrolledText(
            detail_frame,
            font=("微软雅黑", 11),
            wrap=tk.WORD,
            state=tk.DISABLED,
            width=50
        )
        self.detail_text.pack(fill=tk.BOTH, expand=True, padx=5)

    def init_category_display(self):
        """初始化分类按钮显示"""
        # 清空原有分类
        for widget in self.category_frame.winfo_children():
            widget.destroy()

        # 创建分类单选按钮
        for category in self.formulas.keys():
            ttk.Radiobutton(
                self.category_frame,
                text=category,
                variable=self.current_category,
                value=category,
                command=self.show_category_formulas
            ).pack(anchor=tk.W, pady=2)

        # 默认选中第一个分类
        if self.formulas:
            first_category = list(self.formulas.keys())[0]
            self.current_category.set(first_category)
            self.show_category_formulas()

    def show_category_formulas(self):
        """显示选中分类下的所有公式"""
        self.formula_listbox.delete(0, tk.END)
        category = self.current_category.get()
        if category in self.formulas:
            for formula_name in self.formulas[category].keys():
                self.formula_listbox.insert(tk.END, formula_name)
        self.detail_text.config(state=tk.NORMAL)
        self.detail_text.delete(1.0, tk.END)
        self.detail_text.config(state=tk.DISABLED)
        self.current_selected_formula = None

    def show_formula_detail(self, event):
        """显示选中公式的详情"""
        selected = self.formula_listbox.curselection()
        if not selected:
            return

        index = selected[0]
        formula_name = self.formula_listbox.get(index)
        category = self.current_category.get()

        self.current_selected_formula = (category, formula_name)
        formula_detail = self.formulas[category][formula_name]

        # 显示详情
        self.detail_text.config(state=tk.NORMAL)
        self.detail_text.delete(1.0, tk.END)
        self.detail_text.insert(
            tk.END, f"【{formula_name}】\n\n{formula_detail}")
        self.detail_text.config(state=tk.DISABLED)

    def search_formula(self):
        """模糊搜索公式（名称/内容）"""
        keyword = self.search_entry.get().strip()
        if not keyword:
            messagebox.showinfo("提示", "请输入搜索关键词！")
            return

        self.formula_listbox.delete(0, tk.END)
        matched = []
        for category, formulas in self.formulas.items():
            for name, detail in formulas.items():
                if keyword in name or keyword in detail:
                    matched.append((category, name))

        if not matched:
            messagebox.showinfo("提示", f"未找到包含「{keyword}」的公式！")
            self.show_category_formulas()
            return

        # 显示匹配结果
        for category, name in matched:
            self.formula_listbox.insert(tk.END, name)
            self.current_category.set(category)  # 同步分类

    def reset_search(self):
        """重置搜索，恢复原分类显示"""
        self.search_entry.delete(0, tk.END)
        self.show_category_formulas()

    def copy_formula(self):
        """复制当前选中的公式到剪贴板"""
        if not self.current_selected_formula:
            messagebox.showwarning("提示", "请先选中要复制的公式！")
            return

        category, name = self.current_selected_formula
        formula_text = self.formulas[category][name]
        self.root.clipboard_clear()
        self.root.clipboard_append(f"{name}：{formula_text}")
        messagebox.showinfo("成功", "公式已复制到剪贴板！")

    def add_formula(self):
        """添加新公式"""
        # 输入分类（可新建）
        category = simpledialog.askstring("添加公式", "请输入公式分类（如：概率统计）：")
        if not category:
            return

        # 输入公式名称
        name = simpledialog.askstring("添加公式", "请输入公式名称：")
        if not name:
            return

        # 输入公式内容
        content = simpledialog.askmultiline("添加公式", "请输入公式详细内容：")
        if not content:
            return

        # 检查是否已存在
        if category in self.formulas and name in self.formulas[category]:
            if not messagebox.askyesno("确认", "该公式已存在，是否覆盖？"):
                return

        # 添加/更新公式
        if category not in self.formulas:
            self.formulas[category] = {}
        self.formulas[category][name] = content

        # 保存并刷新界面
        if self.save_formulas():
            self.init_category_display()
            self.current_category.set(category)
            self.show_category_formulas()
            messagebox.showinfo("成功", "公式添加成功！")

    def edit_formula(self):
        """编辑选中的公式"""
        if not self.current_selected_formula:
            messagebox.showwarning("提示", "请先选中要编辑的公式！")
            return

        category, name = self.current_selected_formula
        current_content = self.formulas[category][name]

        # 输入新内容
        new_content = simpledialog.askmultiline(
            "编辑公式", f"当前公式：{name}\n请修改内容：", initialvalue=current_content)
        if new_content is None:  # 取消编辑
            return

        if not new_content.strip():
            messagebox.showwarning("提示", "公式内容不能为空！")
            return

        # 更新并保存
        self.formulas[category][name] = new_content
        if self.save_formulas():
            self.show_formula_detail(None)  # 刷新详情
            messagebox.showinfo("成功", "公式编辑成功！")

    def delete_formula(self):
        """删除选中的公式"""
        if not self.current_selected_formula:
            messagebox.showwarning("提示", "请先选中要删除的公式！")
            return

        category, name = self.current_selected_formula
        if messagebox.askyesno("确认删除", f"确定要删除「{name}」吗？删除后无法恢复！"):
            del self.formulas[category][name]

            # 如果分类下无公式，删除该分类
            if not self.formulas[category]:
                del self.formulas[category]

            # 保存并刷新
            if self.save_formulas():
                self.init_category_display()
                self.show_category_formulas()
                messagebox.showinfo("成功", "公式已删除！")


if __name__ == "__main__":
    root = tk.Tk()
    app = MathFormulaQueryApp(root)
    root.mainloop()
