#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import platform
import colorsys
import random

if platform.system() == 'Windows':
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'KaiTi', 'FangSong']
elif platform.system() == 'Darwin':
    plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Heiti SC', 'STHeiti', 'Arial Unicode MS']
else:
    plt.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei', 'Noto Sans CJK SC', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False

STUDENTS_DATA = {}

class GradeApp:
    BG = '#F3F3F3'
    CARD = '#FFFFFF'
    PRIMARY = '#0078D4'
    PRIMARY_DARK = '#005A9E'
    PRIMARY_LIGHT = '#EFF6FC'
    TEXT = '#1A1A1A'
    TEXT_SEC = '#616161'
    DIVIDER = '#E0E0E0'
    ERROR = '#D13438'
    COLORS = ['#0078D4','#CA5010','#B7472A','#107C10','#E74856',
              '#8764B8','#498205','#767676','#C239B3','#038387',
              '#5C2D91','#008272']

    def __init__(self, root):
        self.root = root
        self.root.title("成绩走势图 - 学生成绩分析系统")
        self.root.geometry("1280x820")
        self.root.minsize(1000, 650)
        self.root.configure(bg=self.BG)
        self._setup_styles()
        self._build_ui()

    def _setup_styles(self):
        s = ttk.Style()
        s.theme_use('clam')
        s.configure('MS.TButton', font=('Microsoft YaHei', 10),
                    background=self.PRIMARY, foreground='white',
                    borderwidth=0, padding=(12, 6), relief='flat')
        s.map('MS.TButton', background=[('active', self.PRIMARY_DARK)])
        s.configure('Outline.TButton', font=('Microsoft YaHei', 10),
                    background=self.CARD, foreground=self.PRIMARY,
                    borderwidth=1, padding=(10, 5), relief='flat')
        s.map('Outline.TButton', background=[('active', self.PRIMARY_LIGHT)])
        s.configure('Danger.TButton', font=('Microsoft YaHei', 10),
                    background=self.ERROR, foreground='white',
                    borderwidth=0, padding=(10, 5), relief='flat')
        s.map('Danger.TButton', background=[('active', '#B91C1C')])

    def _build_ui(self):
        top = tk.Frame(self.root, bg='#0078D4', height=48)
        top.pack(fill=tk.X); top.pack_propagate(False)
        tk.Label(top, text="  📊  成绩走势图", font=('Microsoft YaHei', 14, 'bold'),
                 fg='white', bg='#0078D4').pack(side=tk.LEFT)
        tk.Label(top, text="学生成绩分析系统", font=('Microsoft YaHei', 10),
                 fg='#CCE4F7', bg='#0078D4').pack(side=tk.LEFT, padx=(0, 16))
        self.count_lbl = tk.Label(top, text="已添加 0 位学生",
                                  font=('Microsoft YaHei', 9), fg='#CCE4F7', bg='#0078D4')
        self.count_lbl.pack(side=tk.RIGHT, padx=16)

        body = tk.Frame(self.root, bg=self.BG)
        body.pack(fill=tk.BOTH, expand=True, padx=16, pady=(8, 16))
        self._build_left(body)
        self._build_right(body)

    def _build_left(self, parent):
        left = tk.Frame(parent, bg=self.CARD, width=340)
        left.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 12)); left.pack_propagate(False)

        tk.Frame(left, bg=self.BG, height=4).pack(fill=tk.X)
        tk.Label(left, text="  📋 学生管理", font=('Microsoft YaHei', 14, 'bold'),
                 fg=self.TEXT, bg=self.CARD).pack(anchor='w', pady=(12, 8), padx=16)

        nf = tk.Frame(left, bg=self.CARD, padx=16); nf.pack(fill=tk.X)

        tk.Label(nf, text="学生姓名", font=('Microsoft YaHei', 10),
                 fg=self.TEXT, bg=self.CARD).pack(anchor='w', pady=(0, 2))
        self.name_var = tk.StringVar()
        tk.Entry(nf, textvariable=self.name_var, font=('Microsoft YaHei', 11),
                 bg='white', fg=self.TEXT, relief='solid', bd=1).pack(fill=tk.X, ipady=6)

        tk.Label(nf, text="科目与成绩（每行：科目,成绩）", font=('Microsoft YaHei', 10),
                 fg=self.TEXT, bg=self.CARD).pack(anchor='w', pady=(10, 2))
        self.data_text = tk.Text(nf, font=('Microsoft YaHei', 10), bg='white', fg=self.TEXT,
                                 relief='solid', bd=1, height=7, wrap=tk.WORD, padx=8, pady=6)
        self.data_text.pack(fill=tk.X, pady=(2, 0))
        self.data_text.insert('1.0', "语文,92\n数学,88\n英语,95\n物理,78\n化学,90\n历史,85\n地理,80")

        bf = tk.Frame(nf, bg=self.CARD); bf.pack(fill=tk.X, pady=(12, 0))
        ttk.Button(bf, text="➕  添加学生", style='MS.TButton',
                   command=self._add_student).pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 4))
        ttk.Button(bf, text="🗑  清空全部", style='Danger.TButton',
                   command=self._clear_all).pack(side=tk.LEFT, fill=tk.X, expand=True)

        tk.Frame(left, bg=self.DIVIDER, height=1).pack(fill=tk.X, padx=16, pady=8)
        tk.Label(left, text="  👥 学生列表", font=('Microsoft YaHei', 14, 'bold'),
                 fg=self.TEXT, bg=self.CARD).pack(anchor='w', padx=16)
        self.stu_count_lbl = tk.Label(left, text="共 0 位学生",
                                     font=('Microsoft YaHei', 9), fg=self.TEXT_SEC, bg=self.CARD)
        self.stu_count_lbl.pack(anchor='w', padx=16, pady=(2, 0))

        lf = tk.Frame(left, bg=self.CARD, padx=8, pady=4)
        lf.pack(fill=tk.BOTH, expand=True, padx=8, pady=(0, 8))
        self.list_canvas = tk.Canvas(lf, bg=self.CARD, highlightthickness=0)
        lb = ttk.Scrollbar(lf, orient='vertical', command=self.list_canvas.yview)
        self.list_inner = tk.Frame(self.list_canvas, bg=self.CARD)
        self.list_inner.bind('<Configure>',
            lambda e: self.list_canvas.configure(scrollregion=self.list_canvas.bbox('all')))
        self.list_canvas.create_window((0, 0), window=self.list_inner, anchor='nw')
        self.list_canvas.configure(yscrollcommand=lb.set)
        self.list_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        lb.pack(side=tk.RIGHT, fill=tk.Y)
        self.list_canvas.bind('<Configure>',
            lambda e: self._resize_list(e))

    def _resize_list(self, event):
        for item in self.list_canvas.find_all():
            self.list_canvas.itemconfig(item, width=event.width - 4)

    def _build_right(self, parent):
        right = tk.Frame(parent, bg=self.CARD)
        right.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        tk.Label(right, text="📈 图表展示", font=('Microsoft YaHei', 14, 'bold'),
                 fg=self.TEXT, bg=self.CARD).pack(anchor='w', padx=20, pady=(4, 4))
        tk.Frame(right, bg=self.DIVIDER, height=1).pack(fill=tk.X, padx=20)

        ctrl = tk.Frame(right, bg=self.CARD, padx=20, pady=8); ctrl.pack(fill=tk.X)
        tk.Label(ctrl, text="图表类型：", font=('Microsoft YaHei', 10),
                 fg=self.TEXT, bg=self.CARD).pack(side=tk.LEFT, padx=(0, 6))
        self.chart_type = tk.StringVar(value='折线图')
        types = ['折线图', '条形图', '饼状图', '雷达图', '面积图', '散点图', '水平条形图', '柱状图']
        combo = ttk.Combobox(ctrl, textvariable=self.chart_type, values=types,
                             state='readonly', width=12, font=('Microsoft YaHei', 10))
        combo.pack(side=tk.LEFT, padx=(0, 12))
        combo.bind('<<ComboboxSelected>>', lambda e: self._draw_chart())
        ttk.Button(ctrl, text="🎨  生成图表", style='MS.TButton',
                   command=self._draw_chart).pack(side=tk.LEFT, padx=(0, 4))
        ttk.Button(ctrl, text="🔄  随机配色", style='Outline.TButton',
                   command=self._rand_colors).pack(side=tk.LEFT, padx=(0, 4))
        ttk.Button(ctrl, text="💾  导出图片", style='Outline.TButton',
                   command=self._export).pack(side=tk.LEFT)
        tk.Frame(right, bg=self.DIVIDER, height=1).pack(fill=tk.X, padx=20)

        self.fig = Figure(figsize=(8, 5.5), dpi=100, facecolor='#FAFAFA')
        self.ax = self.fig.add_subplot(111)
        self.fig.subplots_adjust(left=0.10, right=0.95, top=0.92, bottom=0.12)
        self.canvas = FigureCanvasTkAgg(self.fig, right)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True, padx=8, pady=(0, 8))
        self._placeholder()

    def _placeholder(self):
        self.fig.clear()
        self.ax = self.fig.add_subplot(111)
        self.fig.subplots_adjust(left=0.10, right=0.95, top=0.92, bottom=0.12)
        ax = self.ax
        ax.set_facecolor('#FAFAFA')
        ax.text(0.5, 0.5, '添加学生数据后\n点击「生成图表」查看成绩走势',
                ha='center', va='center', fontsize=16, color='#BDBDBD', linespacing=1.8)
        ax.set_xticks([]); ax.set_yticks([])
        for sp in ax.spines.values(): sp.set_visible(False)
        self.canvas.draw_idle()

    def _add_student(self):
        name = self.name_var.get().strip()
        if not name:
            messagebox.showwarning("提示", "请输入学生姓名"); return
        text = self.data_text.get('1.0', 'end').strip()
        if not text:
            messagebox.showwarning("提示", "请输入科目与成绩数据"); return
        if name in STUDENTS_DATA:
            messagebox.showwarning("提示", f"学生「{name}」已存在，请先删除后重新添加"); return
        scores = []
        for line in text.split('\n'):
            line = line.strip()
            if not line: continue
            parts = [p.strip() for p in line.split(',')]
            if len(parts) >= 2:
                try:
                    scores.append((parts[0], float(parts[1])))
                except ValueError:
                    messagebox.showerror("错误", f"成绩格式有误：{line}\n请使用格式：科目,成绩"); return
            else:
                messagebox.showerror("错误", f"成绩格式有误：{line}\n请使用格式：科目,成绩"); return
        if not scores:
            messagebox.showwarning("提示", "至少需要一个科目成绩"); return
        STUDENTS_DATA[name] = scores
        self.name_var.set('')
        self.data_text.delete('1.0', 'end')
        self.data_text.insert('1.0', "语文,92\n数学,88\n英语,95\n物理,78")
        self._refresh_list()
        self._draw_chart()

    def _remove(self, name):
        if name in STUDENTS_DATA:
            del STUDENTS_DATA[name]
            self._refresh_list()
            if STUDENTS_DATA:
                self._draw_chart()
            else:
                self._placeholder()

    def _clear_all(self):
        if STUDENTS_DATA and messagebox.askyesno("确认", "确定要清空所有学生数据吗？"):
            STUDENTS_DATA.clear()
            self._refresh_list()
            self._placeholder()

    def _refresh_list(self):
        for w in self.list_inner.winfo_children(): w.destroy()
        for name, scores in STUDENTS_DATA.items():
            avg = sum(s[1] for s in scores) / len(scores) if scores else 0
            item = tk.Frame(self.list_inner, bg='#FAFAFA', cursor='hand2')
            item.pack(fill=tk.X, padx=4, pady=3)
            bl = tk.Label(item, text=f"  {name}", font=('Microsoft YaHei', 11),
                         fg=self.TEXT, bg='#FAFAFA', anchor='w', padx=8, pady=4)
            bl.pack(side=tk.LEFT, fill=tk.X, expand=True)
            al = tk.Label(item, text=f"均{avg:.0f}", font=('Microsoft YaHei', 9),
                         fg=self.TEXT_SEC, bg='#FAFAFA', padx=8, pady=4)
            al.pack(side=tk.LEFT)
            dl = tk.Button(item, text="✕", font=('Arial', 10), fg=self.ERROR,
                           bg='#FAFAFA', bd=0, padx=6, cursor='hand2')
            dl.pack(side=tk.RIGHT)
            for w in [item, bl, al]:
                w.bind('<Enter>', lambda e, f=item: f.configure(bg='#F0F0F0'))
                w.bind('<Leave>', lambda e, f=item: f.configure(bg='#FAFAFA'))
            bl.bind('<Button-1>', lambda e: self._draw_chart())
            dl.bind('<Button-1>', lambda e, n=name: self._remove(n))
        self.stu_count_lbl.config(text=f"共 {len(STUDENTS_DATA)} 位学生")
        self.count_lbl.config(text=f"已添加 {len(STUDENTS_DATA)} 位学生")

    # ─── 工具方法 ───
    def _all_subs(self):
        seen = set()
        for scores in STUDENTS_DATA.values():
            for sub in scores:
                seen.add(sub[0])
        return list(seen)

    def _vals(self, scores, subs):
        sdict = {s[0]: s[1] for s in scores}
        return [sdict.get(sub, 0.0) for sub in subs]

    def _new_ax(self):
        self.fig.clear()
        ax = self.fig.add_subplot(111)
        self.fig.subplots_adjust(left=0.10, right=0.95, top=0.92, bottom=0.12)
        return ax

    # ─── 图表 ───
    def _draw_chart(self):
        if not STUDENTS_DATA:
            self._placeholder(); return
        subs = self._all_subs()
        C = self.COLORS
        t = self.chart_type.get()
        try:
            if   t == '折线图':     self._line(subs, C)
            elif t == '条形图':     self._bar(subs, C)
            elif t == '饼状图':     self._pie(subs, C)
            elif t == '雷达图':     self._radar(subs, C)
            elif t == '面积图':     self._area(subs, C)
            elif t == '散点图':     self._scatter(subs, C)
            elif t == '水平条形图': self._hbar(subs, C)
            elif t == '柱状图':     self._bar(subs, C)
        except Exception as e:
            self.fig.clear()
            ax = self.fig.add_subplot(111)
            ax.text(0.5, 0.5, f'绘制出错\n{e}', ha='center', va='center',
                    fontsize=14, color='#D13438')
            ax.set_xticks([]); ax.set_yticks([])
        self.canvas.draw_idle()

    def _line(self, subs, C):
        ax = self._new_ax()
        x = list(range(len(subs)))
        for i, (name, scores) in enumerate(STUDENTS_DATA.items()):
            v = self._vals(scores, subs)
            ax.plot(x, v, marker='o', markersize=7, linewidth=2.5,
                    color=C[i % len(C)], label=name, zorder=3)
        ax.set_xticks(x)
        ax.set_xticklabels(subs, fontsize=9)
        ax.set_ylim(0, 105)
        self._finish(ax, '📈 学生成绩走势对比图', 'y')

    def _bar(self, subs, C):
        ax = self._new_ax()
        x = list(range(len(subs)))
        n = len(STUDENTS_DATA)
        w = 0.8 / n if n else 0.8
        for i, (name, scores) in enumerate(STUDENTS_DATA.items()):
            v = self._vals(scores, subs)
            pos = [p + i * w - w * n / 2 + w / 2 for p in x]
            ax.bar(pos, v, width=w * 0.85, color=C[i % len(C)],
                   label=name, alpha=0.88, edgecolor='white', linewidth=0.5, zorder=3)
        ax.set_xticks(x)
        ax.set_xticklabels(subs, fontsize=9)
        ax.set_ylim(0, 105)
        self._finish(ax, '📊 各科目成绩对比', 'y')

    def _pie(self, subs, C):
        ax = self._new_ax()
        avgs = {}
        for scores in STUDENTS_DATA.values():
            for sub, sc in scores:
                avgs.setdefault(sub, []).append(sc)
        labels = list(avgs.keys())
        vals = [sum(avgs[s]) / len(avgs[s]) for s in labels]
        clrs = [C[i % len(C)] for i in range(len(labels))]
        texts = [f"{l}\n{v:.1f}分" for l, v in zip(labels, vals)]
        wedges, _, atexts = ax.pie(vals, labels=texts, autopct='%1.1f%%', colors=clrs,
                                    startangle=90, pctdistance=0.75,
                                    textprops={'fontsize': 9})
        ax.legend(texts, fontsize=8, loc='center left',
                   bbox_to_anchor=(1.05, 0.5), framealpha=0.9)
        ax.set_title('🥧 各科平均成绩分布', fontsize=14, fontweight='bold', pad=12)

    def _radar(self, subs, C):
        self.fig.clear()
        ax = self.fig.add_subplot(111, polar=True)
        self.fig.subplots_adjust(left=0.05, right=0.88, top=0.90, bottom=0.08)
        n = len(subs)
        if n < 3:
            ax.text(0.5, 0.5, '雷达图至少需要3个科目', ha='center', va='center',
                    fontsize=14, color='#D13438')
            return
        angles = [2 * 3.14159265 * i / n for i in range(n)]
        angles += angles[:1]
        for i, (name, scores) in enumerate(STUDENTS_DATA.items()):
            v = self._vals(scores, subs)
            ax.plot(angles, v + v[:1], 'o-', linewidth=2,
                    color=C[i % len(C)], label=name, markersize=5, zorder=3)
            ax.fill(angles, v + v[:1], alpha=0.08, color=C[i % len(C)])
        ax.set_thetagrids(angles[:-1], subs, fontsize=9)
        ax.set_ylim(0, 105)
        ax.set_title('🎯 各科能力雷达图', fontsize=14, fontweight='bold',
                      fontfamily='Microsoft YaHei', pad=20, y=1.08)
        ax.legend(fontsize=8, loc='upper right', bbox_to_anchor=(1.35, 1.1), framealpha=0.9)

    def _area(self, subs, C):
        ax = self._new_ax()
        x = list(range(len(subs)))
        for i, (name, scores) in enumerate(STUDENTS_DATA.items()):
            v = self._vals(scores, subs)
            ax.fill_between(x, v, alpha=0.15, color=C[i % len(C)], zorder=2)
            ax.plot(x, v, marker='o', markersize=6, linewidth=2.5,
                    color=C[i % len(C)], label=name, zorder=3)
        ax.set_xticks(x)
        ax.set_xticklabels(subs, fontsize=9)
        ax.set_ylim(0, 105)
        self._finish(ax, '📊 成绩趋势面积图', 'y')

    def _scatter(self, subs, C):
        ax = self._new_ax()
        x = list(range(len(subs)))
        for i, (name, scores) in enumerate(STUDENTS_DATA.items()):
            v = self._vals(scores, subs)
            for j, (xx, vv) in enumerate(zip(x, v)):
                ax.scatter(xx, vv, s=120, color=C[i % len(C)], alpha=0.7,
                           edgecolors='white', linewidth=1, zorder=3)
                ax.annotate(f'{vv:.0f}', (xx, vv), textcoords="offset points",
                            xytext=(6, 4), fontsize=8)
            ax.plot(x, v, linewidth=1.2, linestyle='--', alpha=0.4,
                    color=C[i % len(C)], label=name)
        ax.set_xticks(x)
        ax.set_xticklabels(subs, fontsize=9)
        ax.set_ylim(0, 105)
        self._finish(ax, '🔵 各科成绩分布', 'y')

    def _hbar(self, subs, C):
        ax = self._new_ax()
        y = list(range(len(subs)))
        n = len(STUDENTS_DATA)
        h = 0.7 / n if n else 0.7
        for i, (name, scores) in enumerate(STUDENTS_DATA.items()):
            v = self._vals(scores, subs)
            pos = [p + i * h - h * n / 2 + h / 2 for p in y]
            ax.barh(pos, v, height=h * 0.85, color=C[i % len(C)],
                    label=name, alpha=0.88, edgecolor='white', linewidth=0.5, zorder=3)
        ax.set_yticks(y)
        ax.set_yticklabels(subs, fontsize=9)
        ax.set_xlim(0, 105)
        self._finish(ax, '📊 各科成绩对比（横向）', 'x')

    def _finish(self, ax, title, grid_axis):
        ax.set_ylabel('成绩', fontsize=11)
        ax.set_title(title, fontsize=14, fontweight='bold', fontfamily='Microsoft YaHei', pad=12)
        if STUDENTS_DATA:
            ax.legend(fontsize=9, loc='best', framealpha=0.9,
                      edgecolor='#E0E0E0', fancybox=True)
        ax.grid(True, alpha=0.15, axis=grid_axis, color='#BDBDBD', linestyle='--')
        ax.set_facecolor('#FAFAFA')
        if grid_axis == 'y':
            ax.set_ylim(0, 105)

    def _rand_colors(self):
        h = random.random()
        self.COLORS = []
        for i in range(12):
            hue = (h + i * 0.0833) % 1.0
            r, g, b = colorsys.hsv_to_rgb(hue, 0.55, 0.85)
            self.COLORS.append(f'#{int(r * 255):02X}{int(g * 255):02X}{int(b * 255):02X}')
        self._draw_chart()

    def _export(self):
        f = filedialog.asksaveasfilename(
            defaultextension='.png',
            filetypes=[('PNG', '*.png'), ('SVG', '*.svg'), ('PDF', '*.pdf')])
        if f:
            self.fig.savefig(f, dpi=200, facecolor='#FAFAFA', bbox_inches='tight')
            messagebox.showinfo("导出成功", f"图表已保存到：\n{f}")


def main():
    root = tk.Tk()
    try:
        from ctypes import windll
        windll.shcore.SetProcessDpiAwareness(1)
    except Exception:
        pass
    root.update_idletasks()
    w, h = 1280, 820
    x = (root.winfo_screenwidth() - w) // 2
    y = (root.winfo_screenheight() - h) // 2
    root.geometry(f'{w}x{h}+{x}+{y}')
    GradeApp(root)
    root.mainloop()


if __name__ == '__main__':
    main()
