# -*- coding: utf-8 -*-
"""
墨舟 OS 字库生成器（07a 模拟版）
================================
和 lesson01 的 make_font.py 同一套原理（lesson01 已归档：归档_lesson时代_20260829/lesson01.zip）：把 Windows 黑体里的字符渲染成
点阵数组。区别：字表换成了墨舟 UI 需要的全部文案。

改 UI 文案？
  1. 改下面的 STRINGS_16 / STRINGS_32（所有要上屏的中英文都列全）
  2. 运行：.venv/Scripts/python.exe inkboat/make_inkboat_font.py
  3. inkboat_sim.py 重新启动即可吃到新字库（烧录阶段同一份 font.h 进固件）

字库格式与 lesson01/06 一脉相承（两目录均已归档）：
  ASCII16[95][16] / ZH16_IDS[] / ZH16_BITMAPS[][32]
  ASCII32[95][96] / ZH32_IDS[] / ZH32_BITMAPS[][128]
"""

from pathlib import Path
import re
import sys

from PIL import Image, ImageDraw, ImageFont

FONT_FILE = "C:/Windows/Fonts/simhei.ttf"   # 黑体：笔画粗，黑白屏上清晰
TXT_FILES = [Path(__file__).parent / n for n in          # 书的正文（字符集自动吸收）
             ("daodejing_grouped.txt", "xinjing.txt", "lunyu.txt",
              "zhuangzi.txt", "feiniao.txt")]            # 2026-08-29：卷版道德经 + 飞鸟集

# ---- 16px：菜单、正文、提示 ----
STRINGS_16 = [
    # 主菜单应用名
    "时钟 阅读 闹钟 音乐 翻转棋 设置",
    # 时钟页
    "周一 周二 周三 周四 周五 周六 周日",
    "电87%", "闹钟 未设置", "今日一句 道德经",
    # 每日一句（08 句轮换，按日期取）
    "道可道 非常道",
    "上善若水 水善利万物而不争",
    "千里之行 始于足下",
    "知人者智 自知者明",
    "大方无隅 大器晚成 大音希声",
    "天网恢恢 疏而不失",
    "治大国 若烹小鲜",
    "胜人者有力 自胜者强",
    "短按PWR=菜单",
    # 应用占位页
    "全文约五千字 书签断电记忆",
    "两组闹钟 深睡中准时唤醒",
    "方波合成器 五首内置曲目",
    "人机对弈 三档难度",
    "敬请期待",
    "短按PWR=返回",
    # 电源菜单
    "软关机 重启 真关机 取消",
    "轻按任一键唤醒",
    "长按PWR开机",
    # 设置页
    "全刷周期", "局刷计数", "短按BOOT=调节",
    "短按选行 长按调节",
    # 阅读器 / 书架
    "目录", "长按BOOT=跳转", "阅读字号",
    "书架", "五本随身书", "长按BOOT=打开",
    # 翻转棋
    "难度", "简单 标准 大师", "黑胜 白胜 平局",
    "再来一局", "认输 新局", "轮到你 轮到我", "无子可落已跳过",
    # 音乐
    "小星星 欢乐颂 茉莉花 送别 墨舟谣",
    "播放中 已暂停 音量", "短按切曲 长按播放",
    # 闹钟
    "闹钟一 闹钟二", "每天 工作日 单次", "时 分 重复",
    "响铃中", "贪睡十分钟", "短按贪睡 长按停止",
    "长按编 短按换行",
    # v5 键位改革新增文案
    "短按BOOT=菜单",
    "自动睡眠 永不 5分 15分 100",
    "音量",
    "转 停续",
    "换难度",
    "短按上下选 长按调节",
    # v5w 卡仓（TF 卡状态页）
    "卡仓 卡就绪 总 剩 根目录 项 未挂载",
    "无卡 插拔卡后重启生效",
    "最近 电脑运行 卡仓.bat put",
    "BOOT长=刷新 PWR长=退出",
    "模拟 无真卡 真机此处显示",          # 模拟器专用文案（共用同一份字库）
    # v5w2 卡仓 WiFi 上传（屏显状态行 + 引导行；模拟器同页提示）
    "连网中 连网失败 长按重试",
    "浏览器打开网址传文件 真机此页显示传书网址",
]

# ---- 32px：标题、大时钟（数字走 ASCII32）----
STRINGS_32 = [
    "墨舟",
    "电源",
    "设置",
    "扁舟暂泊",
    "时钟 阅读 闹钟 音乐 翻转棋",      # 占位页大标题（应用名）
    "书架",
    "小星星 欢乐颂 茉莉花 送别 墨舟谣",  # 曲名（音乐页大标题）
    "黑胜 白胜 平局",                   # 棋局终局判词
    "卡仓",                             # v5w 卡仓页标题
]

ASCII_CHARS = "".join(chr(c) for c in range(32, 127))


def gb2312_hanzi() -> set:
    """GB2312 全部一二级汉字（16-87 区，共 6763 字）——v5x 起卡上书字符不再按需定制：
    任何卡书的常用汉字都能直接显示（flash +约 785KB，固件仍在 huge_app 3MB 内）。
    符号区（01-09 区）不收：全角标点书正文/UI 文案已覆盖。"""
    out = set()
    for hi in range(0xB0, 0xF8):
        for lo in range(0xA1, 0xFF):
            try:
                out.add(bytes([hi, lo]).decode("gb2312"))
            except UnicodeDecodeError:
                pass
    return out


def render_glyph(ch: str, px: int, cell_w: int, baseline_align: bool = False) -> bytes:
    """把一个字符渲染成 cell_w x px 的 1bpp 位图（MSB 在左，行尾补零到整字节）"""
    font = ImageFont.truetype(FONT_FILE, px)
    img = Image.new("1", (cell_w, px), 0)
    draw = ImageDraw.Draw(img)
    x0, y0, x1, y1 = font.getbbox(ch)
    if baseline_align:
        # ASCII 专用（2026-08-29 修"字母七上八下"）：所有字符共享基线。
        # 旧逻辑逐字按 bbox 垂直居中——a/x（无上行部）偏下、t/h 偏上、p/g/y（有下行部）偏上，
        # 同一行英文里每个字母垂直位置都不一样。改为统一的 ascender 顶坐标 → 基线天然对齐。
        ascent, descent = font.getmetrics()
        y = (px - ascent - descent) // 2
    else:
        y = (px - y1 + y0) // 2 - y0                  # 中文方块字：按自身 bbox 居中即可
    draw.text(((cell_w - x1 + x0) // 2 - x0, y), ch, font=font, fill=1)
    rows = []
    for y2 in range(px):
        row = 0
        for x in range(cell_w):
            if img.getpixel((x, y2)):
                # 2026-08-26 位序修复：写进容器最高位（MSB 左对齐），与绘制端
                # draw_glyph 的 0x80>>col 读取一致。旧写法 1<<(cell_w-1-x) 在
                # 12/20px 档（格宽不满容器字节）会让字形右偏 4 位
                row |= 1 << (((cell_w + 7) // 8) * 8 - 1 - x)
        rows.append(row.to_bytes((cell_w + 7) // 8, "big"))
    return b"".join(rows)


def emit_font(out, chars, size, cell_w, nbyte, ascii_first):
    """ascii_first=True 生成 ASCII 全表；否则按 chars 生成中文表"""
    if ascii_first:
        out.append(f"// ASCII {size}px：{cell_w}x{size} 点阵，95 个，下标 = 字符码 - 32")
        out.append(f"const uint8_t ASCII{size}[95][{nbyte}] = {{")
        for c in range(32, 127):
            bm = render_glyph(chr(c), size, cell_w, baseline_align=True)   # 基线对齐
            out.append("  {" + ",".join(f"0x{b:02X}" for b in bm) + f"}},  // '{chr(c)}'")
        out.append("};\n")
    else:
        chars = sorted(chars)
        out.append(f"// 中文 {size}px：{cell_w}x{size} 点阵，IDS 码表 + BITMAPS 字模")
        out.append(f"const uint16_t ZH{size}_IDS[] = {{")
        out.append("  " + ",".join(f"0x{ord(ch):04X}" for ch in chars))
        out.append("};")
        out.append(f"const uint8_t ZH{size}_BITMAPS[][{nbyte}] = {{")
        for ch in chars:
            bm = render_glyph(ch, size, cell_w)
            assert len(bm) == nbyte, f"{ch}@{size}px {len(bm)} != {nbyte}"
            out.append("  {" + ",".join(f"0x{b:02X}" for b in bm) + f"}},  // {ch}")
        out.append("};\n")


def main():
    # 书的正文也进字符集（阅读器三档字号都要能显示全书）
    book = ""
    for f in TXT_FILES:
        if f.exists():
            book += f.read_text(encoding="utf-8")
        else:
            print(f"[警告] 找不到 {f}，字库将不含该书正文！")

    ui16 = "".join(STRINGS_16)
    zh16 = {ch for ch in ui16 + book if ord(ch) >= 128} | {"\u3000"}   # U+3000：段首缩进（txt 原文没有，生成时才加）
    zh16 |= gb2312_hanzi()                   # v5x：GB2312 全量汉字（卡上书任意常用字直接可显）
    zh12 = zh16                            # 阅读三档统一字符集，避免某一档缺字
    zh20 = zh16
    zh32 = {ch for s in STRINGS_32 for ch in s if ord(ch) >= 128}

    out = ["// 本文件由 inkboat/make_inkboat_font.py 自动生成，手改无效",
           "// 位图格式：每行 (格宽+7)//8 字节，MSB 在左；位 1=白 0=黑（同驱动约定）",
           "#pragma once", "#include <Arduino.h>", ""]
    emit_font(out, None, 16, 8, 16, ascii_first=True)
    emit_font(out, zh16, 16, 16, 32, ascii_first=False)
    emit_font(out, None, 32, 24, 96, ascii_first=True)
    emit_font(out, zh32, 32, 32, 128, ascii_first=False)
    emit_font(out, zh12, 12, 12, 24, ascii_first=False)   # 阅读紧凑档（12px 极限档同用此表）
    emit_font(out, zh20, 20, 20, 60, ascii_first=False)   # 阅读大字档

    dest = Path(__file__).parent / "font.h"
    dest.write_text("\n".join(out), encoding="utf-8")
    # 双写固件目录（2026-08-19 教训：只更新模拟器侧导致真机缺字）
    fw = Path(__file__).parent.parent / "firmware" / "inkboat" / "font.h"
    fw.write_text("\n".join(out), encoding="utf-8")
    print(f"[OK] 已生成 {dest}")
    print(f"     同步固件副本 → {fw}")
    print(f"     16/12/20px 中文各 {len(zh16)} 字（UI + 全书），32px {len(zh32)} 字")
    print(f"     另含 ASCII 16px 与 32px 全表")


if __name__ == "__main__":
    main()
