import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
from datetime import datetime
import json
import os

class GradeTrackerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("学生成绩走势分析系统")
        self.root.geometry("1200x700")
        self.root.configure(bg='#f0f0f0')
        
        # 数据存储
        self.subjects = ["语文", "数学", "英语", "物理", "化学", "生物", "历史", "地理", "政治"]
        self.grades_data = {}  # 存储成绩数据 {科目: [成绩列表]}
        self.exam_names = []   # 考试名称列表
        self.data_file = "grades_data.json"
        
        # 加载已有数据
        self.load_data()
        
        # 创建界面
        self.create_widgets()
        
        # 初始化图表
        self.update_chart()
    
    def load_data(self):
        """加载保存的数据"""
        if os.path.exists(self.data_file):
            try:
                with open(self.data_file, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.grades_data = data.get('grades_data', {})
                    self.exam_names = data.get('exam_names', [])
            except:
                pass
        
        # 如果没有数据，初始化示例数据
        if not self.grades_data:
            self.init_sample_data()
    
    def init_sample_data(self):
        """初始化示例数据"""
        self.exam_names = ["期中考试", "期末考试", "月考1", "月考2", "模拟考1", "模拟考2"]
        
        # 为每个科目生成示例成绩（展示不同的趋势）
        sample_data = {
            "语文": [85, 88, 86, 89, 92, 90],
            "数学": [78, 82, 85, 88, 90, 93],
            "英语": [82, 84, 87, 86, 89, 91],
            "物理": [75, 78, 82, 85, 88, 90],
            "化学": [80, 79, 83, 86, 88, 89],
            "生物": [88, 87, 89, 90, 92, 91],
            "历史": [76, 80, 79, 82, 85, 88],
            "地理": [84, 86, 85, 88, 87, 90],
            "政治": [79, 81, 80, 83, 85, 87]
        }
        self.grades_data = sample_data
    
    def save_data(self):
        """保存数据到文件"""
        data = {
            'grades_data': self.grades_data,
            'exam_names': self.exam_names
        }
        try:
            with open(self.data_file, 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=2)
            messagebox.showinfo("成功", "数据已保存！")
        except Exception as e:
            messagebox.showerror("错误", f"保存失败：{str(e)}")
    
    def create_widgets(self):
        """创建界面组件"""
        # 左侧控制面板
        left_frame = tk.Frame(self.root, bg='#f0f0f0', width=300)
        left_frame.pack(side=tk.LEFT, fill=tk.Y, padx=10, pady=10)
        
        # 标题
        title_label = tk.Label(left_frame, text="📊 成绩走势分析系统", 
                               font=("微软雅黑", 16, "bold"),
                               bg='#f0f0f0', fg='#2c3e50')
        title_label.pack(pady=10)
        
        # 添加成绩区域
        add_frame = tk.LabelFrame(left_frame, text="添加/编辑成绩", 
                                  font=("微软雅黑", 12, "bold"),
                                  bg='#f0f0f0', fg='#2c3e50')
        add_frame.pack(fill=tk.X, pady=10)
        
        # 考试名称
        tk.Label(add_frame, text="考试名称:", bg='#f0f0f0').grid(row=0, column=0, sticky='w', padx=5, pady=5)
        self.exam_name_entry = tk.Entry(add_frame, width=15)
        self.exam_name_entry.grid(row=0, column=1, padx=5, pady=5)
        
        # 选择科目
        tk.Label(add_frame, text="选择科目:", bg='#f0f0f0').grid(row=1, column=0, sticky='w', padx=5, pady=5)
        self.subject_combo = ttk.Combobox(add_frame, values=self.subjects, width=12)
        self.subject_combo.set("语文")
        self.subject_combo.grid(row=1, column=1, padx=5, pady=5)
        
        # 成绩输入
        tk.Label(add_frame, text="成绩:", bg='#f0f0f0').grid(row=2, column=0, sticky='w', padx=5, pady=5)
        self.grade_entry = tk.Entry(add_frame, width=15)
        self.grade_entry.grid(row=2, column=1, padx=5, pady=5)
        
        # 按钮
        btn_frame = tk.Frame(add_frame, bg='#f0f0f0')
        btn_frame.grid(row=3, column=0, columnspan=2, pady=10)
        
        add_btn = tk.Button(btn_frame, text="添加成绩", command=self.add_grade,
                           bg='#27ae60', fg='white', font=("微软雅黑", 10),
                           cursor="hand2", width=10)
        add_btn.pack(side=tk.LEFT, padx=5)
        
        update_btn = tk.Button(btn_frame, text="更新成绩", command=self.update_grade,
                              bg='#3498db', fg='white', font=("微软雅黑", 10),
                              cursor="hand2", width=10)
        update_btn.pack(side=tk.LEFT, padx=5)
        
        # 成绩列表区域
        list_frame = tk.LabelFrame(left_frame, text="成绩列表", 
                                   font=("微软雅黑", 12, "bold"),
                                   bg='#f0f0f0', fg='#2c3e50')
        list_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        # 创建Treeview显示成绩
        columns = ("考试", "科目", "成绩")
        self.grade_tree = ttk.Treeview(list_frame, columns=columns, show='headings', height=15)
        
        for col in columns:
            self.grade_tree.heading(col, text=col)
            self.grade_tree.column(col, width=80)
        
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.grade_tree.yview)
        self.grade_tree.configure(yscrollcommand=scrollbar.set)
        
        self.grade_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 绑定选择事件
        self.grade_tree.bind('<<TreeviewSelect>>', self.on_grade_select)
        
        # 删除按钮
        del_btn = tk.Button(list_frame, text="删除选中", command=self.delete_grade,
                           bg='#e74c3c', fg='white', font=("微软雅黑", 10),
                           cursor="hand2")
        del_btn.pack(pady=5)
        
        # 数据管理按钮
        manage_frame = tk.Frame(left_frame, bg='#f0f0f0')
        manage_frame.pack(fill=tk.X, pady=5)
        
        save_btn = tk.Button(manage_frame, text="💾 保存数据", command=self.save_data,
                            bg='#2c3e50', fg='white', font=("微软雅黑", 10),
                            cursor="hand2")
        save_btn.pack(side=tk.LEFT, padx=5)
        
        load_btn = tk.Button(manage_frame, text="📂 导入数据", command=self.import_data,
                            bg='#2c3e50', fg='white', font=("微软雅黑", 10),
                            cursor="hand2")
        load_btn.pack(side=tk.LEFT, padx=5)
        
        export_btn = tk.Button(manage_frame, text="📊 导出图表", command=self.export_chart,
                              bg='#2c3e50', fg='white', font=("微软雅黑", 10),
                              cursor="hand2")
        export_btn.pack(side=tk.LEFT, padx=5)
        
        # 右侧图表区域
        right_frame = tk.Frame(self.root, bg='white')
        right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 图表控制栏
        chart_control = tk.Frame(right_frame, bg='white')
        chart_control.pack(fill=tk.X, pady=5)
        
        tk.Label(chart_control, text="选择科目:", bg='white', font=("微软雅黑", 11)).pack(side=tk.LEFT, padx=5)
        
        # 多选科目列表
        self.subject_listbox = tk.Listbox(chart_control, selectmode=tk.MULTIPLE,
                                          height=2, width=20, font=("微软雅黑", 9))
        for subject in self.subjects:
            self.subject_listbox.insert(tk.END, subject)
        # 默认全选
        self.subject_listbox.selection_set(0, tk.END)
        self.subject_listbox.pack(side=tk.LEFT, padx=5)
        
        update_chart_btn = tk.Button(chart_control, text="更新图表", command=self.update_chart,
                                    bg='#3498db', fg='white', font=("微软雅黑", 10),
                                    cursor="hand2")
        update_chart_btn.pack(side=tk.LEFT, padx=10)
        
        show_stats_btn = tk.Button(chart_control, text="显示统计", command=self.show_statistics,
                                  bg='#e67e22', fg='white', font=("微软雅黑", 10),
                                  cursor="hand2")
        show_stats_btn.pack(side=tk.LEFT, padx=5)
        
        # 图表显示区域
        self.figure = plt.Figure(figsize=(8, 5), dpi=100)
        self.ax = self.figure.add_subplot(111)
        self.canvas = FigureCanvasTkAgg(self.figure, right_frame)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        
        # 刷新成绩列表显示
        self.refresh_grade_list()
    
    def refresh_grade_list(self):
        """刷新成绩列表显示"""
        # 清空现有数据
        for item in self.grade_tree.get_children():
            self.grade_tree.delete(item)
        
        # 添加数据
        for i, exam in enumerate(self.exam_names):
            for subject, grades in self.grades_data.items():
                if i < len(grades):
                    self.grade_tree.insert('', tk.END, values=(exam, subject, grades[i]))
    
    def add_grade(self):
        """添加新成绩"""
        exam_name = self.exam_name_entry.get().strip()
        subject = self.subject_combo.get()
        
        try:
            grade = float(self.grade_entry.get())
            if grade < 0 or grade > 100:
                messagebox.showwarning("警告", "成绩应在0-100之间！")
                return
        except ValueError:
            messagebox.showwarning("警告", "请输入有效的成绩！")
            return
        
        if not exam_name:
            messagebox.showwarning("警告", "请输入考试名称！")
            return
        
        # 检查考试名称是否已存在
        if exam_name not in self.exam_names:
            self.exam_names.append(exam_name)
            # 为所有科目添加None占位
            for subj in self.grades_data:
                self.grades_data[subj].append(None)
        
        # 获取考试索引
        exam_index = self.exam_names.index(exam_name)
        
        # 添加成绩
        if subject not in self.grades_data:
            self.grades_data[subject] = [None] * len(self.exam_names)
        
        self.grades_data[subject][exam_index] = grade
        
        # 清空输入框
        self.exam_name_entry.delete(0, tk.END)
        self.grade_entry.delete(0, tk.END)
        
        # 刷新显示
        self.refresh_grade_list()
        self.update_chart()
        messagebox.showinfo("成功", f"已添加 {subject} 的 {exam_name} 成绩：{grade}")
    
    def update_grade(self):
        """更新成绩"""
        selected = self.grade_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择要更新的成绩！")
            return
        
        exam_name = self.exam_name_entry.get().strip()
        subject = self.subject_combo.get()
        
        try:
            grade = float(self.grade_entry.get())
            if grade < 0 or grade > 100:
                messagebox.showwarning("警告", "成绩应在0-100之间！")
                return
        except ValueError:
            messagebox.showwarning("警告", "请输入有效的成绩！")
            return
        
        if not exam_name:
            messagebox.showwarning("警告", "请输入考试名称！")
            return
        
        # 查找考试索引
        if exam_name in self.exam_names:
            exam_index = self.exam_names.index(exam_name)
            if subject in self.grades_data:
                self.grades_data[subject][exam_index] = grade
                self.refresh_grade_list()
                self.update_chart()
                messagebox.showinfo("成功", f"已更新 {subject} 的 {exam_name} 成绩为：{grade}")
            else:
                messagebox.showwarning("警告", "科目不存在！")
        else:
            messagebox.showwarning("警告", "考试名称不存在！")
    
    def delete_grade(self):
        """删除选中的成绩"""
        selected = self.grade_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择要删除的成绩！")
            return
        
        if messagebox.askyesno("确认", "确定要删除选中的成绩吗？"):
            for item in selected:
                values = self.grade_tree.item(item, 'values')
                exam_name, subject, grade = values
                
                if exam_name in self.exam_names:
                    exam_index = self.exam_names.index(exam_name)
                    if subject in self.grades_data:
                        self.grades_data[subject][exam_index] = None
            
            self.refresh_grade_list()
            self.update_chart()
            messagebox.showinfo("成功", "已删除选中的成绩！")
    
    def on_grade_select(self, event):
        """当选择成绩时，填充到输入框"""
        selected = self.grade_tree.selection()
        if selected:
            values = self.grade_tree.item(selected[0], 'values')
            if values:
                exam_name, subject, grade = values
                self.exam_name_entry.delete(0, tk.END)
                self.exam_name_entry.insert(0, exam_name)
                self.subject_combo.set(subject)
                self.grade_entry.delete(0, tk.END)
                self.grade_entry.insert(0, grade)
    
    def update_chart(self):
        """更新图表"""
        self.ax.clear()
        
        # 获取选中的科目
        selected_indices = self.subject_listbox.curselection()
        if not selected_indices:
            self.ax.text(0.5, 0.5, '请选择要显示的科目', 
                        ha='center', va='center', transform=self.ax.transAxes)
            self.canvas.draw()
            return
        
        # 绘制成绩趋势
        colors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6', 
                  '#1abc9c', '#e67e22', '#34495e', '#95a5a6']
        
        for idx, subject_idx in enumerate(selected_indices):
            subject = self.subjects[subject_idx]
            if subject in self.grades_data:
                grades = self.grades_data[subject]
                # 过滤掉None值
                valid_grades = [(i, g) for i, g in enumerate(grades) if g is not None]
                if valid_grades:
                    indices, valid_grade_values = zip(*valid_grades)
                    exam_labels = [self.exam_names[i] for i in indices]
                    
                    color = colors[idx % len(colors)]
                    self.ax.plot(exam_labels, valid_grade_values, 
                               marker='o', linewidth=2, markersize=6,
                               label=subject, color=color)
        
        # 设置图表属性
        self.ax.set_xlabel('考试名称', fontsize=12, fontproperties='SimHei')
        self.ax.set_ylabel('成绩（分）', fontsize=12, fontproperties='SimHei')
        self.ax.set_title('学生成绩走势图', fontsize=14, fontweight='bold', fontproperties='SimHei')
        self.ax.legend(loc='best', prop={'family': 'SimHei'})
        self.ax.grid(True, alpha=0.3)
        self.ax.set_ylim(0, 100)
        
        # 旋转x轴标签，避免重叠
        plt.setp(self.ax.get_xticklabels(), rotation=45, ha='right')
        
        self.figure.tight_layout()
        self.canvas.draw()
    
    def show_statistics(self):
        """显示统计信息"""
        stats_window = tk.Toplevel(self.root)
        stats_window.title("成绩统计分析")
        stats_window.geometry("600x400")
        stats_window.configure(bg='#f0f0f0')
        
        # 创建文本框
        text_widget = scrolledtext.ScrolledText(stats_window, wrap=tk.WORD,
                                                font=("微软雅黑", 11))
        text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 计算统计数据
        stats_text = "📊 成绩统计分析报告\n\n"
        
        for subject in self.subjects:
            if subject in self.grades_data:
                grades = [g for g in self.grades_data[subject] if g is not None]
                if grades:
                    avg_grade = np.mean(grades)
                    max_grade = max(grades)
                    min_grade = min(grades)
                    std_grade = np.std(grades)
                    trend = "上升" if grades[-1] > grades[0] else "下降" if grades[-1] < grades[0] else "平稳"
                    
                    stats_text += f"【{subject}】\n"
                    stats_text += f"  平均分：{avg_grade:.1f}分\n"
                    stats_text += f"  最高分：{max_grade}分\n"
                    stats_text += f"  最低分：{min_grade}分\n"
                    stats_text += f"  标准差：{std_grade:.2f}\n"
                    stats_text += f"  趋势：{trend}\n"
                    stats_text += f"  进步：{grades[-1] - grades[0]:+.1f}分\n\n"
        
        # 添加总体分析
        all_grades = []
        for subject in self.subjects:
            if subject in self.grades_data:
                grades = [g for g in self.grades_data[subject] if g is not None]
                all_grades.extend(grades)
        
        if all_grades:
            stats_text += "📈 总体分析\n"
            stats_text += f"  总体平均分：{np.mean(all_grades):.1f}分\n"
            stats_text += f"  最高分：{max(all_grades)}分\n"
            stats_text += f"  最低分：{min(all_grades)}分\n"
        
        text_widget.insert(1.0, stats_text)
        text_widget.config(state=tk.DISABLED)
    
    def import_data(self):
        """导入数据"""
        filename = filedialog.askopenfilename(
            title="选择数据文件",
            filetypes=[("JSON files", "*.json"), ("All files", "*.*")]
        )
        
        if filename:
            try:
                with open(filename, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.grades_data = data.get('grades_data', {})
                    self.exam_names = data.get('exam_names', [])
                    self.refresh_grade_list()
                    self.update_chart()
                    messagebox.showinfo("成功", "数据导入成功！")
            except Exception as e:
                messagebox.showerror("错误", f"导入失败：{str(e)}")
    
    def export_chart(self):
        """导出图表"""
        filename = filedialog.asksaveasfilename(
            title="保存图表",
            defaultextension=".png",
            filetypes=[("PNG files", "*.png"), ("JPG files", "*.jpg"), ("All files", "*.*")]
        )
        
        if filename:
            try:
                self.figure.savefig(filename, dpi=300, bbox_inches='tight')
                messagebox.showinfo("成功", f"图表已保存到：{filename}")
            except Exception as e:
                messagebox.showerror("错误", f"保存失败：{str(e)}")

def main():
    root = tk.Tk()
    app = GradeTrackerApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()