#!/usr/bin/env python3
"""将地块贴图 .meta 设为 sprite-frame，对齐 Unity pivot，按透明边裁剪以贴满格子。"""
from __future__ import annotations

import argparse
import json
import re
import struct
import uuid
from pathlib import Path

TILE_GLOBS = [
    "assets/resources/textures/*/Baseblock.png",
    "assets/resources/textures/*/JumpBlock.png",
    "assets/resources/textures/*/WallBlock.png",
    "assets/resources/textures/*/Decor23.png",
    "assets/resources/textures/*/kuai11.png",
    "assets/resources/textures/*/素材切图-23.png",
    "assets/resources/textures/*/素材切图2-23.png",
]

COCOS_TO_UNITY = {
    "default": "Level",
    "silu": "silu",
    "snow": "snow",
    "sanxing": "sanxing",
    "chinese": "Chinese",
    "numMan": "numMan",
    "redarmy": "redArmy",
    "redArmy": "redArmy",
}


def png_size(path: Path) -> tuple[int, int]:
    with path.open("rb") as f:
        f.read(16)
        return struct.unpack(">II", f.read(8))


def alpha_bbox(png_path: Path) -> tuple[int, int, int, int] | None:
    try:
        from PIL import Image
    except ImportError:
        return None
    try:
        img = Image.open(png_path).convert("RGBA")
        box = img.getbbox()
        if not box:
            return None
        return box
    except Exception:
        return None


def read_unity_pivot(unity_tex: Path, theme_folder: str, tile_name: str) -> tuple[float, float]:
    theme_dir = unity_tex if theme_folder == "Level" else unity_tex / theme_folder
    meta_path = theme_dir / f"{tile_name}.png.meta"
    if not meta_path.is_file() and tile_name == "Decor23":
        meta_path = theme_dir / "素材切图-23.png.meta"
    if not meta_path.is_file():
        return 0.5, 0.92
    text = meta_path.read_text(encoding="utf-8")
    m = re.search(r"spritePivot:\s*\{x:\s*([^,]+),\s*y:\s*([^}]+)\}", text)
    if not m:
        return 0.5, 0.92
    return float(m.group(1)), float(m.group(2))


def resolve_trim(
    png_path: Path,
    full_w: int,
    full_h: int,
    pivot_x: float,
    pivot_y: float,
) -> dict:
    """按透明边裁剪，并换算 Unity pivot 到裁剪后 sprite。"""
    bbox = alpha_bbox(png_path)
    if not bbox:
        return {
            "trimType": "none",
            "trimX": 0,
            "trimY": 0,
            "width": full_w,
            "height": full_h,
            "rawWidth": full_w,
            "rawHeight": full_h,
            "offsetX": 0,
            "offsetY": 0,
            "pivotX": pivot_x,
            "pivotY": pivot_y,
        }

    left, top, right, bottom = bbox
    trim_w = right - left
    trim_h = bottom - top
    if trim_w <= 0 or trim_h <= 0 or (trim_w >= full_w and trim_h >= full_h):
        return {
            "trimType": "none",
            "trimX": 0,
            "trimY": 0,
            "width": full_w,
            "height": full_h,
            "rawWidth": full_w,
            "rawHeight": full_h,
            "offsetX": 0,
            "offsetY": 0,
            "pivotX": pivot_x,
            "pivotY": pivot_y,
        }

    pivot_bottom = pivot_y * full_h
    trim_bottom = full_h - bottom
    pivot_bottom_trim = pivot_bottom - trim_bottom
    new_pivot_y = max(0.0, min(1.0, pivot_bottom_trim / trim_h))
    new_pivot_x = max(0.0, min(1.0, (pivot_x * full_w - left) / trim_w))

    return {
        "trimType": "custom",
        "trimX": left,
        "trimY": top,
        "width": trim_w,
        "height": trim_h,
        "rawWidth": full_w,
        "rawHeight": full_h,
        "offsetX": 0,
        "offsetY": 0,
        "pivotX": round(new_pivot_x, 4),
        "pivotY": round(new_pivot_y, 4),
    }


def ensure_image_meta(meta_path: Path, display_name: str) -> None:
    if meta_path.is_file():
        return
    base_uuid = str(uuid.uuid4())
    meta_path.write_text(
        json.dumps(
            {
                "ver": "1.0.27",
                "importer": "image",
                "imported": False,
                "uuid": base_uuid,
                "files": [".json", ".png"],
                "subMetas": {
                    "6c48a": {
                        "importer": "texture",
                        "uuid": f"{base_uuid}@6c48a",
                        "displayName": display_name,
                        "id": "6c48a",
                        "name": "texture",
                        "userData": {
                            "imageUuidOrDatabaseUri": base_uuid,
                            "isUuid": True,
                            "visible": False,
                        },
                        "ver": "1.0.22",
                        "imported": False,
                        "files": [".json"],
                        "subMetas": {},
                    }
                },
                "userData": {"type": "texture", "redirect": f"{base_uuid}@6c48a"},
            },
            indent=2,
        ),
        encoding="utf-8",
    )


def patch_meta(meta_path: Path, png_path: Path, trim: dict) -> None:
    ensure_image_meta(meta_path, meta_path.stem.replace(".png", ""))
    meta = json.loads(meta_path.read_text(encoding="utf-8"))
    base_uuid = meta["uuid"]
    tex_uuid = f"{base_uuid}@6c48a"
    sf_uuid = f"{base_uuid}@f9941"

    meta["userData"] = {
        "type": "sprite-frame",
        "redirect": tex_uuid,
        "hasAlpha": True,
        "fixAlphaTransparencyArtifacts": False,
    }
    meta["subMetas"]["f9941"] = {
        "ver": "1.0.12",
        "importer": "sprite-frame",
        "uuid": sf_uuid,
        "imported": True,
        "files": [".json"],
        "subMetas": {},
        "userData": {
            "wrapModeS": "clamp-to-edge",
            "wrapModeT": "clamp-to-edge",
            "minfilter": "linear",
            "magfilter": "linear",
            "premultiplyAlpha": False,
            "generateMipmap": False,
            "anisotropy": 1,
            "trimType": trim["trimType"],
            "trimThreshold": 1,
            "rotated": False,
            "offsetX": trim["offsetX"],
            "offsetY": trim["offsetY"],
            "trimX": trim["trimX"],
            "trimY": trim["trimY"],
            "width": trim["width"],
            "height": trim["height"],
            "rawWidth": trim["rawWidth"],
            "rawHeight": trim["rawHeight"],
            "borderTop": 0,
            "borderBottom": 0,
            "borderLeft": 0,
            "borderRight": 0,
            "isUuid": True,
            "imageUuidOrDatabaseUri": tex_uuid,
            "atlasUuid": "",
            "mipfilter": "none",
            "packable": True,
            "pixelsToUnit": 100,
            "pivotX": trim["pivotX"],
            "pivotY": trim["pivotY"],
            "meshType": 0,
        },
        "displayName": meta_path.stem,
        "id": "f9941",
        "name": "spriteFrame",
    }
    meta_path.write_text(json.dumps(meta, indent=2), encoding="utf-8")
    rel = meta_path.relative_to(meta_path.parents[4])
    print(
        f"  patched {rel} -> draw {trim['width']}x{trim['height']} "
        f"(raw {trim['rawWidth']}x{trim['rawHeight']}) pivot=({trim['pivotX']},{trim['pivotY']})"
    )


def collect_tile_pngs(root: Path, themes: set[str] | None) -> list[Path]:
    found: set[Path] = set()
    for pattern in TILE_GLOBS:
        for p in root.glob(pattern):
            if not p.is_file():
                continue
            if themes:
                theme = p.parent.name
                if theme not in themes:
                    continue
            found.add(p)
    return sorted(found)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--unity-root", help="Unity 项目根目录，用于读取 spritePivot")
    ap.add_argument("--themes", help="仅处理指定 Cocos 主题目录，逗号分隔，如 snow")
    args = ap.parse_args()

    root = Path(__file__).resolve().parent.parent
    theme_filter = {t.strip() for t in args.themes.split(",") if t.strip()} if args.themes else None
    unity_tex = Path(args.unity_root) / "Assets" / "Texture" if args.unity_root else None

    pngs = collect_tile_pngs(root, theme_filter)
    for png in pngs:
        meta = png.with_suffix(".png.meta")
        if not png.is_file():
            print(f"  skip missing {png.relative_to(root)}")
            continue
        w, h = png_size(png)
        tile_name = png.stem
        cocos_theme = png.parent.name
        if unity_tex and unity_tex.is_dir():
            unity_folder = COCOS_TO_UNITY.get(cocos_theme, cocos_theme)
            pivot_x, pivot_y = read_unity_pivot(unity_tex, unity_folder, tile_name)
        else:
            pivot_x, pivot_y = 0.5, 0.92
        trim = resolve_trim(png, w, h, pivot_x, pivot_y)
        patch_meta(meta, png, trim)

    print(f"Patched {len(pngs)} tile metas. 请在 Cocos Creator 中刷新 assets/resources/textures。")


if __name__ == "__main__":
    main()
