#!/usr/bin/env python3
"""
学生证件生成系统 — 使用 Tkinter 构建
功能：录入学生信息 → 选择照片 → 生成学生证件（预览 + 保存为图片）
"""

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from PIL import Image, ImageDraw, ImageFont, ImageTk
import os
import shutil
from datetime import datetime

# ============ 全局配色 ============
COLORS = {
    "bg":         "#F0F4F8",
    "card_bg":    "#FFFFFF",
    "primary":    "#1A5276",
    "accent":     "#2E86C1",
    "light_blue": "#D6EAF8",
    "text_dark":  "#2C3E50",
    "text_light": "#7F8C8D",
    "white":      "#FFFFFF",
    "border":     "#BDC3C7",
    "success":    "#27AE60",
    "danger":     "#E74C3C",
}


# ============ 主应用类 ============
class StudentIDApp:
    def __init__(self, root):
        self.root = root
        self.root.title("学生证件生成系统")
        self.root.geometry("1100x720")
        self.root.configure(bg=COLORS["bg"])
        self.root.minsize(1000, 650)

        # 数据存储
        self.photo_path = None
        self.preview_image = None

        # 字体
        self._init_fonts()

        # 构建界面
        self._build_ui()

    def _init_fonts(self):
        """初始化字体"""
        try:
            self.font_title = ("Microsoft YaHei", 22, "bold")
            self.font_subtitle = ("Microsoft YaHei", 13, "bold")
            self.font_label = ("Microsoft YaHei", 11)
            self.font_entry = ("Microsoft YaHei", 11)
            self.font_card_title = ("Microsoft YaHei", 18, "bold")
            self.font_card_field = ("Microsoft YaHei", 10)
            self.font_card_small = ("Microsoft YaHei", 9)
        except:
            self.font_title = ("Arial", 22, "bold")
            self.font_subtitle = ("Arial", 13, "bold")
            self.font_label = ("Arial", 11)
            self.font_entry = ("Arial", 11)
            self.font_card_title = ("Arial", 18, "bold")
            self.font_card_field = ("Arial", 10)
            self.font_card_small = ("Arial", 9)

    def _build_ui(self):
        """构建主界面"""
        # ---- 顶部标题栏 ----
        header = tk.Frame(self.root, bg=COLORS["primary"], height=70)
        header.pack(fill="x")
        header.pack_propagate(False)
        tk.Label(
            header, text="🎓  学生证件生成系统",
            font=self.font_title, fg=COLORS["white"], bg=COLORS["primary"]
        ).pack(side="left", padx=30, pady=15)
        tk.Label(
            header, text="Student ID Card Generator",
            font=("Arial", 11, "italic"), fg="#AED6F1", bg=COLORS["primary"]
        ).pack(side="right", padx=30, pady=20)

        # ---- 主内容区（左右两栏） ----
        main = tk.Frame(self.root, bg=COLORS["bg"])
        main.pack(fill="both", expand=True, padx=20, pady=15)
        main.columnconfigure(0, weight=1)
        main.columnconfigure(1, weight=1)
        main.rowconfigure(0, weight=1)

        # 左栏：信息录入
        left = tk.Frame(main, bg=COLORS["bg"])
        left.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
        self._build_input_panel(left)

        # 右栏：证件预览
        right = tk.Frame(main, bg=COLORS["bg"])
        right.grid(row=0, column=1, sticky="nsew", padx=(10, 0))
        self._build_preview_panel(right)

    def _build_input_panel(self, parent):
        """构建左侧信息录入面板"""
        # 卡片容器
        card = tk.Frame(parent, bg=COLORS["card_bg"], highlightbackground=COLORS["border"],
                        highlightthickness=1, padx=25, pady=20)
        card.pack(fill="both", expand=True)

        tk.Label(card, text="📋 学生信息录入", font=self.font_subtitle,
                 fg=COLORS["primary"], bg=COLORS["card_bg"]).pack(anchor="w", pady=(0, 15))

        # 表单字段
        fields = [
            ("姓名",     "name",     "请输入学生姓名"),
            ("学号",     "student_id", "请输入学号"),
            ("性别",     "gender",   None),
            ("学院",     "college",  "请输入学院名称"),
            ("专业",     "major",    "请输入专业名称"),
            ("班级",     "class",    "请输入班级"),
            ("入学年份", "year",     "如：2024"),
        ]

        self.entries = {}

        for label_text, key, placeholder in fields:
            frame = tk.Frame(card, bg=COLORS["card_bg"])
            frame.pack(fill="x", pady=6)

            tk.Label(frame, text=label_text, font=self.font_label,
                     fg=COLORS["text_dark"], bg=COLORS["card_bg"], width=10, anchor="w").pack(side="left")

            if key == "gender":
                gender_var = tk.StringVar(value="男")
                self.entries["gender"] = gender_var
                cb = ttk.Combobox(frame, textvariable=gender_var, values=["男", "女"],
                                  state="readonly", width=18, font=self.font_entry)
                cb.pack(side="left", padx=5, fill="x", expand=True)
            else:
                entry = tk.Entry(frame, font=self.font_entry, relief="solid",
                                 highlightbackground=COLORS["border"],
                                 highlightthickness=1, insertbackground=COLORS["primary"])
                entry.pack(side="left", padx=5, fill="x", expand=True)
                if placeholder:
                    entry.insert(0, placeholder)
                    entry.config(fg=COLORS["text_light"])
                    entry.bind("<FocusIn>", lambda e, ent=entry, ph=placeholder: self._clear_placeholder(ent, ph))
                    entry.bind("<FocusOut>", lambda e, ent=entry, ph=placeholder: self._restore_placeholder(ent, ph))
                self.entries[key] = entry

        # 照片选择
        photo_frame = tk.Frame(card, bg=COLORS["card_bg"])
        photo_frame.pack(fill="x", pady=(15, 8))
        tk.Label(photo_frame, text="证件照片", font=self.font_label,
                 fg=COLORS["text_dark"], bg=COLORS["card_bg"], width=10, anchor="w").pack(side="left")

        self.photo_label = tk.Label(photo_frame, text="未选择照片", font=self.font_card_small,
                                     fg=COLORS["text_light"], bg=COLORS["card_bg"])
        self.photo_label.pack(side="left", padx=5)

        tk.Button(photo_frame, text="选择照片", font=("Arial", 9, "bold"),
                  bg=COLORS["accent"], fg=COLORS["white"], activebackground=COLORS["primary"],
                  relief="flat", padx=12, pady=3, cursor="hand2",
                  command=self._select_photo).pack(side="right")

        # 按钮区域
        btn_frame = tk.Frame(card, bg=COLORS["card_bg"])
        btn_frame.pack(fill="x", pady=(20, 5))

        tk.Button(btn_frame, text="🔄 重置", font=("Arial", 11, "bold"),
                  bg="#95A5A6", fg=COLORS["white"], activebackground="#7F8C8D",
                  relief="flat", padx=20, pady=8, cursor="hand2",
                  command=self._reset_form).pack(side="left", padx=(0, 10))

        tk.Button(btn_frame, text="👁 预览证件", font=("Arial", 11, "bold"),
                  bg=COLORS["accent"], fg=COLORS["white"], activebackground=COLORS["primary"],
                  relief="flat", padx=20, pady=8, cursor="hand2",
                  command=self._generate_preview).pack(side="left", padx=10)

        tk.Button(btn_frame, text="💾 保存证件", font=("Arial", 11, "bold"),
                  bg=COLORS["success"], fg=COLORS["white"], activebackground="#1E8449",
                  relief="flat", padx=20, pady=8, cursor="hand2",
                  command=self._save_id_card).pack(side="left", padx=10)

    def _build_preview_panel(self, parent):
        """构建右侧证件预览面板"""
        card = tk.Frame(parent, bg=COLORS["card_bg"], highlightbackground=COLORS["border"],
                        highlightthickness=1, padx=15, pady=15)
        card.pack(fill="both", expand=True)

        tk.Label(card, text="🪪 证件预览", font=self.font_subtitle,
                 fg=COLORS["primary"], bg=COLORS["card_bg"]).pack(anchor="w", pady=(0, 10))

        # 预览区域（用 Canvas 模拟卡片）
        self.preview_canvas = tk.Canvas(card, bg=COLORS["light_blue"],
                                        highlightbackground=COLORS["accent"],
                                        highlightthickness=2, width=380, height=520)
        self.preview_canvas.pack(pady=10)

        # 初始提示
        self.preview_canvas.create_text(190, 260, text="填写信息后点击「预览证件」",
                                        font=("Arial", 12), fill=COLORS["text_light"])

        # 提示信息
        tk.Label(card, text="提示：预览区域展示生成的证件效果，点击「保存证件」可导出为图片",
                 font=("Arial", 9), fg=COLORS["text_light"], bg=COLORS["card_bg"]).pack(pady=(5, 0))

    # ============ 事件处理 ============

    def _clear_placeholder(self, entry, placeholder):
        if entry.get() == placeholder:
            entry.delete(0, "end")
            entry.config(fg=COLORS["text_dark"])

    def _restore_placeholder(self, entry, placeholder):
        if not entry.get().strip():
            entry.insert(0, placeholder)
            entry.config(fg=COLORS["text_light"])

    def _select_photo(self):
        """选择证件照片"""
        path = filedialog.askopenfilename(
            title="选择证件照片",
            filetypes=[("图片文件", "*.png *.jpg *.jpeg *.bmp *.gif")]
        )
        if path:
            self.photo_path = path
            filename = os.path.basename(path)
            self.photo_label.config(text=filename, fg=COLORS["success"])

    def _get_form_data(self):
        """获取表单数据"""
        data = {}
        for key, widget in self.entries.items():
            if isinstance(widget, tk.StringVar):
                data[key] = widget.get().strip()
            else:
                val = widget.get().strip()
                # 检查是否是 placeholder
                placeholders = {"请输入学生姓名", "请输入学号", "请输入学院名称",
                                "请输入专业名称", "请输入班级", "如：2024"}
                if val in placeholders:
                    val = ""
                data[key] = val
        return data

    def _validate(self, data):
        """校验必填字段"""
        required = [("name", "姓名"), ("student_id", "学号"), ("college", "学院"),
                    ("major", "专业"), ("class", "班级"), ("year", "入学年份")]
        for key, label in required:
            if not data.get(key):
                messagebox.showwarning("输入不完整", f"请填写「{label}」字段！")
                return False
        if not self.photo_path:
            messagebox.showwarning("缺少照片", "请先选择一张证件照片！")
            return False
        return True

    def _generate_preview(self):
        """生成证件预览"""
        data = self._get_form_data()
        if not self._validate(data):
            return

        img = self._render_id_card(data)
        if img:
            self.preview_image = img
            # 缩放到预览大小
            img_preview = img.copy()
            img_preview.thumbnail((380, 520), Image.LANCZOS)
            tk_img = ImageTk.PhotoImage(img_preview)
            self.preview_canvas.delete("all")
            # 居中显示
            x = (380 - img_preview.width) // 2
            y = (520 - img_preview.height) // 2
            self.preview_canvas.create_image(x, y, anchor="nw", image=tk_img)
            self.preview_canvas._tk_img = tk_img  # 保持引用

    def _save_id_card(self):
        """保存证件为图片"""
        if self.preview_image is None:
            messagebox.showinfo("提示", "请先预览证件后再保存！")
            return

        save_path = filedialog.asksaveasfilename(
            title="保存学生证件",
            defaultextension=".png",
            filetypes=[("PNG 图片", "*.png"), ("JPEG 图片", "*.jpg")],
            initialfile=f"学生证件_{self._get_form_data().get('name', '未知')}"
        )
        if save_path:
            self.preview_image.save(save_path, quality=95)
            messagebox.showinfo("保存成功", f"学生证件已保存到：\n{save_path}")

    def _reset_form(self):
        """重置表单"""
        for key, widget in self.entries.items():
            if isinstance(widget, tk.StringVar):
                widget.set("男")
            else:
                widget.delete(0, "end")
                placeholders = {"name": "请输入学生姓名", "student_id": "请输入学号",
                                "college": "请输入学院名称", "major": "请输入专业名称",
                                "class": "请输入班级", "year": "如：2024"}
                if key in placeholders:
                    widget.insert(0, placeholders[key])
                    widget.config(fg=COLORS["text_light"])
        self.photo_path = None
        self.photo_label.config(text="未选择照片", fg=COLORS["text_light"])
        self.preview_canvas.delete("all")
        self.preview_canvas.create_text(190, 260, text="填写信息后点击「预览证件」",
                                        font=("Arial", 12), fill=COLORS["text_light"])
        self.preview_image = None

    # ============ 证件渲染核心逻辑 ============

    def _render_id_card(self, data):
        """渲染学生证件为 PIL Image"""
        # 卡片尺寸 640x960 (竖版)
        W, H = 640, 960
        img = Image.new("RGB", (W, H), COLORS["white"])
        draw = ImageDraw.Draw(img)

        # ---- 顶部色带 ----
        draw.rectangle([(0, 0), (W, 160)], fill=COLORS["primary"])
        # 学校名称
        try:
            font_school = ImageFont.truetype("Microsoft YaHei", 36, index=0)
        except:
            font_school = ImageFont.truetype("Arial", 36)
        school_text = "XX大学"
        bbox = draw.textbbox((0, 0), school_text, font=font_school)
        tw = bbox[2] - bbox[0]
        draw.text(((W - tw) // 2, 30), school_text, fill=COLORS["white"], font=font_school)

        try:
            font_en = ImageFont.truetype("Arial", 14)
        except:
            font_en = ImageFont.truetype("Arial", 14)
        en_text = "STUDENT IDENTIFICATION CARD"
        bbox2 = draw.textbbox((0, 0), en_text, font=font_en)
        tw2 = bbox2[2] - bbox2[0]
        draw.text(((W - tw2) // 2, 80), en_text, fill="#AED6F1", font=font_en)

        # 分割线
        draw.line([(50, 115), (W - 50, 115)], fill="#AED6F1", width=2)

        # "学生证" 标题
        try:
            font_title = ImageFont.truetype("Microsoft YaHei", 28, index=0)
        except:
            font_title = ImageFont.truetype("Arial", 28)
        title_text = "学 生 证"
        bbox3 = draw.textbbox((0, 0), title_text, font=font_title)
        tw3 = bbox3[2] - bbox3[0]
        draw.text(((W - tw3) // 2, 125), title_text, fill=COLORS["white"], font=font_title)

        # ---- 照片区域 ----
        photo_y = 185
        photo_x = (W - 220) // 2
        # 照片边框
        draw.rectangle([(photo_x - 4, photo_y - 4), (photo_x + 224, photo_y + 284)],
                       outline=COLORS["accent"], width=3)
        draw.rectangle([(photo_x, photo_y), (photo_x + 220, photo_y + 280)],
                       fill="#F5F5F5")

        # 加载照片
        try:
            photo = Image.open(self.photo_path).convert("RGB")
            photo = photo.resize((220, 280), Image.LANCZOS)
            img.paste(photo, (photo_x, photo_y))
        except Exception as e:
            # 照片加载失败，画占位符
            draw.text((photo_x + 60, photo_y + 120), "[照片]", fill=COLORS["text_light"],
                      font=font_en)

        # ---- 信息区域 ----
        info_y_start = 500
        info_x_label = 60
        info_x_value = 200
        line_gap = 52

        try:
            font_field = ImageFont.truetype("Microsoft YaHei", 17, index=0)
            font_value = ImageFont.truetype("Microsoft YaHei", 17, index=0)
        except:
            font_field = ImageFont.truetype("Arial", 17)
            font_value = ImageFont.truetype("Arial", 17)

        info_items = [
            ("姓    名：", data.get("name", "")),
            ("学    号：", data.get("student_id", "")),
            ("性    别：", data.get("gender", "")),
            ("学    院：", data.get("college", "")),
            ("专    业：", data.get("major", "")),
            ("班    级：", data.get("class", "")),
            ("入学时间：", data.get("year", "") + "年"),
        ]

        for i, (label, value) in enumerate(info_items):
            y = info_y_start + i * line_gap
            # 标签
            draw.text((info_x_label, y), label, fill=COLORS["text_light"], font=font_field)
            # 值
            draw.text((info_x_value, y), value, fill=COLORS["text_dark"], font=font_value)
            # 下划线
            draw.line([(info_x_label, y + 32), (W - 60, y + 32)], fill=COLORS["border"], width=1)

        # ---- 底部 ----
        # 分割线
        draw.line([(50, 890), (W - 50, 890)], fill=COLORS["light_blue"], width=2)

        # 有效期
        try:
            font_small = ImageFont.truetype("Microsoft YaHei", 13, index=0)
        except:
            font_small = ImageFont.truetype("Arial", 13)

        current_year = datetime.now().year
        valid_text = f"有效期：{data.get('year', current_year)}年9月 — {int(data.get('year', current_year)) + 4}年7月"
        bbox_v = draw.textbbox((0, 0), valid_text, font=font_small)
        draw.text(((W - (bbox_v[2] - bbox_v[0])) // 2, 910), valid_text,
                  fill=COLORS["text_light"], font=font_small)

        # 发证日期
        issue_text = f"发证日期：{datetime.now().strftime('%Y年%m月%d日')}"
        bbox_i = draw.textbbox((0, 0), issue_text, font=font_small)
        draw.text(((W - (bbox_i[2] - bbox_i[0])) // 2, 940), issue_text,
                  fill=COLORS["text_light"], font=font_small)

        # 底部色带
        draw.rectangle([(0, H - 20), (W, H)], fill=COLORS["primary"])

        return img


# ============ 启动应用 ============
if __name__ == "__main__":
    root = tk.Tk()
    app = StudentIDApp(root)
    root.mainloop()
