import tkinter as tk
from tkinter import ttk, filedialog, colorchooser, messagebox
from PIL import Image, ImageTk, ImageOps, ImageDraw, ImageFont
import os
import datetime
import sys

CARD_WIDTH = 400
CARD_HEIGHT = 250
AVATAR_SIZE = 120

# 常见中文字体候选路径（会按顺序查找第一个存在的）
FONT_CANDIDATES = [
    # Windows 常见
    r"C:\Windows\Fonts\msyh.ttf",        # 微软雅黑
    r"C:\Windows\Fonts\msyhbd.ttf",
    r"C:\Windows\Fonts\simsun.ttc",      # 宋体
    r"C:\Windows\Fonts\simhei.ttf",      # 黑体
    r"C:\Windows\Fonts\STZHONGS.TTF",
    # macOS 常见
    "/System/Library/Fonts/STHeiti Medium.ttc",
    "/System/Library/Fonts/STHeiti Light.ttc",
    "/Library/Fonts/Arial Unicode.ttf",
    # 常见 Linux 路径（不同发行版可能不同）
    "/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
    "/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
    "/usr/share/fonts/truetype/arphic/ukai.ttc",
    "/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc",
    "/usr/share/fonts/truetype/noto/NotoSansCJKsc-Regular.otf",
]


def find_font():
    for p in FONT_CANDIDATES:
        if os.path.exists(p):
            return p
    # 还可以尝试使用系统 fontconfig（Linux）通过 fc-match 查找，若可用
    try:
        if sys.platform.startswith("linux"):
            import subprocess
            out = subprocess.check_output(
                ["fc-match", "-f", "%{file}\n", "Noto Sans CJK"], stderr=subprocess.DEVNULL)
            candidate = out.decode("utf-8").strip().splitlines()[0]
            if os.path.exists(candidate):
                return candidate
    except Exception:
        pass
    return None


# 在程序启动时查找字体
FONT_PATH = find_font()
if FONT_PATH:
    print("使用字体：", FONT_PATH)
else:
    print("未找到常见中文字体，程序将回退到默认字体，中文可能显示异常。")
    # 可选：提醒用户
    try:
        # 如果在交互式或 GUI 环境，弹窗提示
        root_tmp = tk.Tk()
        root_tmp.withdraw()
        messagebox.showwarning("字体提示", "未找到系统中文字体。若中文显示为方块，请安装中文字体或手动指定字体路径。")
        root_tmp.destroy()
    except Exception:
        pass


class StudentIDApp:
    def __init__(self, root):
        self.root = root
        root.title("学生证件生成器")
        self.avatar_image = None
        self.preview_image_tk = None

        frm_left = ttk.Frame(root, padding=8)
        frm_left.grid(row=0, column=0, sticky="ns")
        frm_right = ttk.Frame(root, padding=8)
        frm_right.grid(row=0, column=1, sticky="nsew")
        root.columnconfigure(1, weight=1)
        root.rowconfigure(0, weight=1)

        self.entries = {}
        fields = [("姓名", "name"), ("学号", "id"), ("班级", "class"),
                  ("专业", "major"), ("学院", "college"), ("出生日期", "birthday")]
        for i, (label, key) in enumerate(fields):
            ttk.Label(frm_left, text=label + "：").grid(row=i,
                                                       column=0, sticky="w", pady=4)
            ent = ttk.Entry(frm_left, width=20)
            ent.grid(row=i, column=1, sticky="w", pady=4)
            self.entries[key] = ent

        ttk.Label(frm_left, text="头像：").grid(
            row=len(fields), column=0, sticky="w", pady=6)
        btn_avatar = ttk.Button(frm_left, text="选择图片...",
                                command=self.choose_avatar)
        btn_avatar.grid(row=len(fields), column=1, sticky="w", pady=6)

        ttk.Label(frm_left, text="背景颜色：").grid(
            row=len(fields)+1, column=0, sticky="w", pady=4)
        self.bg_color_btn = ttk.Button(
            frm_left, text="选择", command=self.choose_bg_color)
        self.bg_color_btn.grid(row=len(fields)+1, column=1, sticky="w", pady=4)
        self.bg_color = "#ffffff"

        ttk.Label(frm_left, text="文字颜色：").grid(
            row=len(fields)+2, column=0, sticky="w", pady=4)
        self.fg_color_btn = ttk.Button(
            frm_left, text="选择", command=self.choose_fg_color)
        self.fg_color_btn.grid(row=len(fields)+2, column=1, sticky="w", pady=4)
        self.fg_color = "#000000"

        ttk.Label(frm_left, text="字体大小：").grid(
            row=len(fields)+3, column=0, sticky="w", pady=4)
        self.font_size_var = tk.IntVar(value=16)
        spin_font = tk.Spinbox(
            frm_left, from_=10, to=36, textvariable=self.font_size_var, width=6, command=self.update_preview)
        spin_font.grid(row=len(fields)+3, column=1, sticky="w", pady=4)

        btn_preview = ttk.Button(
            frm_left, text="刷新预览", command=self.update_preview)
        btn_preview.grid(row=len(fields)+4, column=0, pady=8)
        btn_save = ttk.Button(frm_left, text="保存证件为 PNG",
                              command=self.save_card)
        btn_save.grid(row=len(fields)+4, column=1, pady=8)

        self.canvas = tk.Canvas(
            frm_right, width=CARD_WIDTH, height=CARD_HEIGHT, bg="#ddd")
        self.canvas.grid(row=0, column=0, sticky="n")

        for ent in self.entries.values():
            ent.bind("<KeyRelease>", lambda e: self.update_preview())
            ent.bind("<FocusOut>", lambda e: self.update_preview())

        self.update_preview()

    def choose_avatar(self):
        f = filedialog.askopenfilename(title="选择头像图片", filetypes=[(
            "图片文件", "*.png;*.jpg;*.jpeg;*.bmp;*.gif"), ("所有文件", "*.*")])
        if not f:
            return
        try:
            im = Image.open(f).convert("RGBA")
        except Exception as e:
            messagebox.showerror("错误", f"无法打开图片：{e}")
            return
        w, h = im.size
        side = min(w, h)
        left = (w-side)//2
        top = (h-side)//2
        im_cropped = im.crop((left, top, left+side, top+side))
        im_resized = im_cropped.resize(
            (AVATAR_SIZE, AVATAR_SIZE), Image.LANCZOS)
        mask = Image.new("L", (AVATAR_SIZE, AVATAR_SIZE), 0)
        draw = ImageDraw.Draw(mask)
        draw.ellipse((0, 0, AVATAR_SIZE, AVATAR_SIZE), fill=255)
        im_circle = Image.new("RGBA", (AVATAR_SIZE, AVATAR_SIZE), (0, 0, 0, 0))
        im_circle.paste(im_resized, (0, 0), mask=mask)
        self.avatar_image = im_circle
        self.update_preview()

    def choose_bg_color(self):
        c = colorchooser.askcolor(self.bg_color, title="选择背景颜色")
        if c and c[1]:
            self.bg_color = c[1]
            self.update_preview()

    def choose_fg_color(self):
        c = colorchooser.askcolor(self.fg_color, title="选择文字颜色")
        if c and c[1]:
            self.fg_color = c[1]
            self.update_preview()

    def load_fonts(self, font_size):
        # 返回 (font_bold, font_regular) 两个 PIL ImageFont 对象
        try:
            if FONT_PATH and os.path.exists(FONT_PATH):
                font_bold = ImageFont.truetype(FONT_PATH, font_size+6)
                font = ImageFont.truetype(FONT_PATH, font_size)
            else:
                # 尝试常见英文字体作备用
                font_bold = ImageFont.truetype("arial.ttf", font_size+6)
                font = ImageFont.truetype("arial.ttf", font_size)
        except Exception:
            # 最后回退到 PIL 默认位图字体（不保证中文）
            font_bold = ImageFont.load_default()
            font = ImageFont.load_default()
        return font_bold, font

    def compose_card_image(self):
        card = Image.new("RGBA", (CARD_WIDTH, CARD_HEIGHT), self.bg_color)
        draw = ImageDraw.Draw(card)
        draw.rectangle((0, 0, CARD_WIDTH-1, CARD_HEIGHT-1),
                       outline="#cccccc", width=2)

        padding = 12
        avatar_x = padding
        avatar_y = padding
        if self.avatar_image:
            card.paste(self.avatar_image, (avatar_x,
                       avatar_y), self.avatar_image)
        else:
            placeholder = Image.new(
                "RGBA", (AVATAR_SIZE, AVATAR_SIZE), (200, 200, 200, 255))
            m = Image.new("L", (AVATAR_SIZE, AVATAR_SIZE), 0)
            dd = ImageDraw.Draw(m)
            dd.ellipse((0, 0, AVATAR_SIZE, AVATAR_SIZE), fill=255)
            placeholder.putalpha(m)
            card.paste(placeholder, (avatar_x, avatar_y), placeholder)

        font_size = max(12, self.font_size_var.get())
        font_bold, font = self.load_fonts(font_size)

        name = self.entries["name"].get().strip() or "姓名"
        sid = self.entries["id"].get().strip() or "学号"
        cls = self.entries["class"].get().strip() or "班级"
        major = self.entries["major"].get().strip() or "专业"
        college = self.entries["college"].get().strip() or "学院"
        birthday = self.entries["birthday"].get().strip() or ""

        text_x = avatar_x + AVATAR_SIZE + padding
        text_y = avatar_y
        draw.text((text_x, text_y), name, font=font_bold, fill=self.fg_color)
        text_y += font_size + 12
        draw.text((text_x, text_y), f"学号: {sid}",
                  font=font, fill=self.fg_color)
        text_y += font_size + 6
        draw.text((text_x, text_y), f"班级: {cls}",
                  font=font, fill=self.fg_color)
        text_y += font_size + 6
        draw.text((text_x, text_y),
                  f"专业: {major}", font=font, fill=self.fg_color)
        text_y += font_size + 6
        draw.text((text_x, text_y),
                  f"学院: {college}", font=font, fill=self.fg_color)
        if birthday:
            text_y += font_size + 6
            draw.text((text_x, text_y),
                      f"出生: {birthday}", font=font, fill=self.fg_color)

        footer_h = 34
        footer_y = CARD_HEIGHT - footer_h
        draw.rectangle((0, footer_y, CARD_WIDTH, CARD_HEIGHT), fill="#f0f0f0")
        draw.text((padding, footer_y + 6), "学校名称示例",
                  font=font, fill=self.fg_color)

        qr_size = 60
        qr_x = CARD_WIDTH - padding - qr_size
        qr_y = CARD_HEIGHT - padding - qr_size
        draw.rectangle((qr_x, qr_y, qr_x + qr_size, qr_y +
                       qr_size), outline="#999999", width=1)
        draw.line((qr_x+6, qr_y+6, qr_x+qr_size-6,
                  qr_y+qr_size-6), fill="#999999")
        draw.line((qr_x+qr_size-6, qr_y+6, qr_x+6,
                  qr_y+qr_size-6), fill="#999999")

        return card

    def update_preview(self):
        card = self.compose_card_image()
        tk_im = ImageTk.PhotoImage(card)
        self.preview_image_tk = tk_im
        self.canvas.delete("all")
        self.canvas.create_image(0, 0, anchor="nw", image=tk_im)

    def save_card(self):
        card = self.compose_card_image()
        default_name = f"{self.entries['name'].get().strip() or 'student'}_{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}.png"
        f = filedialog.asksaveasfilename(defaultextension=".png", filetypes=[(
            "PNG 文件", "*.png")], initialfile=default_name, title="保存证件为 PNG")
        if not f:
            return
        try:
            card.save(f)
            messagebox.showinfo("保存成功", f"已保存为：\n{f}")
        except Exception as e:
            messagebox.showerror("保存失败", f"保存时出错：{e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = StudentIDApp(root)
    root.mainloop()
