#!/usr/bin/env python3
"""
护照写入 - 偷懒法 v2 (带 PASSPORT/MOHA 定位校准)
1. 读取 m03 标题坐标 (REPUBLIC) -> 校准基准
2. 读取 m02 标签+内容间距 -> 计算 dx
3. 以 PASSPORT (左上) 和 MOHA (右下) 为定位参考，防止文字飞出
4. MRZ 从直肖右侧开始
"""
import json, sys, argparse
from datetime import datetime
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
import numpy as np

# ──────────────────────────────────────────────
# 1. m02 参考坐标
# ──────────────────────────────────────────────
M02_FIELD_REF = {
    "surname":     {"lx": 600, "ly": 1020, "dx": 140},
    "given_name":  {"lx": 906, "ly": 1020, "dx": 110},
    "sex":         {"lx": 600, "ly": 1092, "dx": 140},
    "dob":         {"lx": 600, "ly": 1160, "dx": 140},
    "birth_place": {"lx": 600, "ly": 1226, "dx": 140},
    "issue_date":  {"lx": 600, "ly": 1300, "dx": 140},
    "expiry_date": {"lx": 600, "ly": 1361, "dx": 140},
    "authority":   {"lx": 908, "ly": 1380, "dx": 20},
}

# ──────────────────────────────────────────────
# 2. 字体加载
# ──────────────────────────────────────────────
FONT_DIR = Path(__file__).parent.parent / "fonts"
FONT_FILES = {"sans": "DejaVuSans.ttf", "sans_bold": "DejaVuSans-Bold.ttf", "mono": "DejaVuSansMono.ttf"}

def load_font(name: str, size: int) -> ImageFont.FreeTypeFont:
    path = FONT_DIR / FONT_FILES[name]
    if not path.exists():
        raise FileNotFoundError(f"Font not found: {path}")
    return ImageFont.truetype(str(path), size)

# ──────────────────────────────────────────────
# 3. MRZ 生成
# ──────────────────────────────────────────────
sys.path.insert(0, str(Path(__file__).parent))
from mrz import build_line1, build_line2

def gen_mrz(data: dict, dob: datetime, expiry: datetime) -> tuple[str, str]:
    dob_str = dob.strftime("%y%m%d")
    exp_str = expiry.strftime("%y%m%d")
    l1 = build_line1(data["surname"], data["given_name"], data.get("type", "PV"), data.get("country_code", "MMR"))
    l2 = build_line2(data["passport_no"], dob_str, exp_str, data.get("sex", "M"), "MMR")
    assert len(l1) == 44 and len(l2) == 44, f"MRZ长度异常: {len(l1)}/{len(l2)}"
    return l1, l2

# ──────────────────────────────────────────────
# 4. Mask-based 绘制
# ──────────────────────────────────────────────
def hex_to_rgb(h):
    h = h.lstrip('#')
    if len(h) == 3: h = ''.join(c*2 for c in h)
    return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))

def draw_text_mask(draw, img, xy, text, font, fill, anchor, debug=False, label=""):
    mask = font.getmask(text, mode='L')
    mask_img = Image.frombytes('L', mask.size, bytes(mask))
    mw, mh = mask_img.size
    x, y = xy
    anchors = {"lt": (x, y), "mt": (x-mw//2, y), "rt": (x-mw, y),
               "mm": (x-mw//2, y-mh//2), "lb": (x, y-mh),
               "mb": (x-mw//2, y-mh), "rb": (x-mw, y-mh)}
    tl_x, tl_y = anchors.get(anchor, (x, y))
    text_color = hex_to_rgb(fill) if isinstance(fill, str) else fill
    text_layer = Image.new('RGB', (mw, mh), text_color)
    img.paste(text_layer, (tl_x, tl_y), mask_img)
    if debug:
        draw.rectangle([tl_x, tl_y, tl_x+mw, tl_y+mh], outline="#FF0000", width=2)
        draw.ellipse([x-3, y-3, x+3, y+3], fill="#00FF00")

# ──────────────────────────────────────────────
# 5. 定位参考点 (PASSPORT + MOHA)
# ──────────────────────────────────────────────
def find_reference_points(img: Image.Image) -> dict:
    """读取 PASSPORT (左上) 和 MOHA (右下) 作为定位参考"""
    arr = np.array(img.convert('RGB'))
    W, H = img.size
    
    refs = {}
    
    # PASSPORT: y=1000-1100, x=250-600
    pass_region = arr[1000:1100, 250:600, :]
    dark = pass_region < [100, 100, 100]
    ys, xs = np.where(dark.all(axis=2))
    if len(ys) > 0:
        refs["passport"] = {
            "x0": 250 + xs.min(), "x1": 250 + xs.max(),
            "y0": 1000 + ys.min(), "y1": 1000 + ys.max(),
            "cx": 250 + xs.min() + (250 + xs.max() - (250 + xs.min())) // 2,
            "cy": 1000 + ys.min() + (1000 + ys.max() - (1000 + ys.min())) // 2,
        }
    else:
        refs["passport"] = {"x0": 304, "x1": 559, "y0": 1025, "y1": 1064, "cx": 432, "cy": 1063}
    
    # MOHA: y=1380-1450, x=900-1300
    moha_region = arr[1380:1450, 900:1300, :]
    dark_m = moha_region < [100, 100, 100]
    ys_m, xs_m = np.where(dark_m.all(axis=2))
    if len(ys_m) > 0:
        refs["moha"] = {
            "x0": 900 + xs_m.min(), "x1": 900 + xs_m.max(),
            "y0": 1380 + ys_m.min(), "y1": 1380 + ys_m.max(),
            "cx": 900 + xs_m.min() + (900 + xs_m.max() - (900 + xs_m.min())) // 2,
            "cy": 1380 + ys_m.min() + (1380 + ys_m.max() - (1380 + ys_m.min())) // 2,
        }
    else:
        refs["moha"] = {"x0": 908, "x1": 1273, "y0": 1380, "y1": 1415, "cx": 1090, "cy": 1397}
    
    # REPUBLIC 标题: y=880-970, x=500-1200
    rep_region = arr[880:970, 500:1200, :]
    dark_r = rep_region < [100, 100, 100]
    ys_r, xs_r = np.where(dark_r.all(axis=2))
    if len(ys_r) > 0:
        refs["republic"] = {
            "cx": 500 + xs_r.min() + (500 + xs_r.max() - (500 + xs_r.min())) // 2,
            "cy": 880 + ys_r.min() + (880 + ys_r.max() - (880 + ys_r.min())) // 2,
        }
    else:
        refs["republic"] = {"cx": 845, "cy": 932}
    
    # 直肖右侧
    portrait_region = arr[118:318, 150:520, :]
    non_white = np.any(portrait_region < 220, axis=2)
    ys_p, xs_p = np.where(non_white)
    refs["portrait_right"] = 150 + xs_p.max() if len(xs_p) > 0 else 500
    
    return refs

# ──────────────────────────────────────────────
# 6. 主写入函数
# ──────────────────────────────────────────────
def write_passport_lazy(img_path, out_path, data, debug=False, skip_header=False):
    img = Image.open(img_path).convert("RGB")
    W, H = img.size
    
    # Step 1: 读取参考点
    refs = find_reference_points(img)
    print(f"[Lazy] PASSPORT: ({refs['passport']['cx']}, {refs['passport']['cy']})")
    print(f"[Lazy] MOHA: ({refs['moha']['cx']}, {refs['moha']['cy']})")
    print(f"[Lazy] Republic: ({refs['republic']['cx']}, {refs['republic']['cy']})")
    print(f"[Lazy] 直肖右侧: x={refs['portrait_right']}")
    
    # Step 2: 计算校准因子
    # PASSPORT 中心到 MOHA 中心的向量
    pass_cx, pass_cy = refs["passport"]["cx"], refs["passport"]["cy"]
    moha_cx, moha_cy = refs["moha"]["cx"], refs["moha"]["cy"]
    rep_cx, rep_cy = refs["republic"]["cx"], refs["republic"]["cy"]
    
    # 计算偏移量 (防止文字飞出)
    # PASSPORT 在左上，MOHA 在右下
    # 文字区域应在 PASSPORT 和 MOHA 之间
    offset_x = moha_cx - pass_cx  # 右移距离
    offset_y = moha_cy - pass_cy  # 下移距离
    
    print(f"[Lazy] 校准: PASSPORT->MOHA dx={offset_x}, dy={offset_y}")
    
    # 字体
    scale_f = min(W / 900, H / 630)
    sf = lambda v: max(1, int(v * scale_f))
    F = {
        "title": load_font("sans_bold", max(12, int(24*scale_f))),
        "val": load_font("sans_bold", max(9, int(13*scale_f))),
        "mrz": load_font("mono", max(10, int(18*scale_f))),
        "label": load_font("sans", max(8, int(11*scale_f))),
        "tiny": load_font("sans", max(6, int(8*scale_f))),
    }
    C = {"title": "#1a1a1a", "pass": "#6B0000", "val": "#111", "mrz": "#111", "label": "#666"}
    
    draw = ImageDraw.Draw(img)
    
    if not skip_header:
        # 标题使用参考点
        draw_text_mask(draw, img, (rep_cx, rep_cy - sf(15)),
                       "REPUBLIC OF THE UNION OF MYANMAR", F["title"], C["title"], "mm", debug, "title")
        draw_text_mask(draw, img, (rep_cx, rep_cy + sf(21)),
                       "P A S S P O R T", F["title"], C["pass"], "mm", debug, "passport")
        
        # Passport No
        draw_text_mask(draw, img, (W - sf(30), sf(25)), f"Passport No  {data['passport_no']}",
                       F["label"], "#1a1a1a", "rt", debug, "passport_no")
        # Type/Code
        draw_text_mask(draw, img, (W - sf(30), sf(45)),
                       f"Type  {data.get('type','PV')}    Code  {data.get('country_code','MMR')}",
                       F["tiny"], "#555", "rt", debug, "type_code")
    
    # Step 3: 使用参考点校准后的坐标写入 8 个变量
    for key, ref in M02_FIELD_REF.items():
        val = str(data.get(key, ""))
        if not val:
            continue
        # 基础坐标
        lx = ref["lx"]
        ly = ref["ly"]
        dx = ref["dx"]
        
        # 校准：基于 PASSPORT 位置微调
        # 如果 PASSPORT 在 y=1063 而参考值是 y=1020，偏移 = 1063 - 1020 = 43
        y_offset = pass_cy - 1020  # PASSPORT 参考 y - m02 参考 y
        x_offset = pass_cx - 432  # PASSPORT 参考 x - m02 参考 x
        
        # 新坐标
        vx = lx + dx + x_offset
        vy = ly + y_offset
        
        # 安全边界检查：确保不飞出图片
        vx = max(10, min(W - 100, vx))
        vy = max(10, min(H - 10, vy))
        
        draw_text_mask(draw, img, (vx, vy), val, F["val"], C["val"], "lt", debug, f"val_{key}")
    
    # Step 4: MRZ - 从直肖右侧开始
    dob = datetime.strptime(data["dob"], "%d %b %Y")
    expiry = datetime.strptime(data["expiry_date"], "%d %b %Y")
    l1, l2 = gen_mrz(data, dob, expiry)
    
    # MRZ y 坐标 (基于 PASSPORT 和 MOHA 之间的位置)
    mrz_y1 = int(pass_cy + offset_y * 0.17)  # PASSPORT 和 MOHA 之间
    mrz_y2 = mrz_y1 + 65
    
    # MRZ x 坐标: 从直肖右侧开始
    mrz_x = refs["portrait_right"] + 10
    
    draw_text_mask(draw, img, (mrz_x, mrz_y1 - sf(6)), "P<", F["mrz"], C["mrz"], "lt", debug, "mrz_prefix")
    draw_text_mask(draw, img, (mrz_x + sf(20), mrz_y1), l1, F["mrz"], C["mrz"], "lt", debug, "mrz_l1")
    draw_text_mask(draw, img, (mrz_x + sf(20), mrz_y2), l2, F["mrz"], C["mrz"], "lt", debug, "mrz_l2")
    
    img.save(out_path, dpi=(300, 300))
    print(f"✓ Lazy v2: {out_path} ({W}x{H})")

# ──────────────────────────────────────────────
if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--img", required=True)
    parser.add_argument("--out", required=True)
    parser.add_argument("--data", required=True)
    parser.add_argument("--debug", action="store_true")
    parser.add_argument("--skip-header", action="store_true")
    args = parser.parse_args()
    
    if args.data.startswith("@"):
        with open(args.data[1:], "r", encoding="utf-8") as f:
            data = json.load(f)
    else:
        data = json.loads(args.data)
    
    write_passport_lazy(args.img, args.out, data, args.debug, args.skip_header)
