"""Génère les variantes teintées des sprites de battlers selon Enemies.csv.

Lit `csv_data/<game>/Enemies.csv` et pour chaque entrée avec `battler_hue > 0`,
applique une rotation HSV (canal H uniquement, RPG Maker VX Ace style) sur le PNG
source et sauvegarde le résultat. Idempotent : skip si le fichier existe déjà.

Sources : `public/images/battlers/<battler_name>.png`
Sorties : `public/images/battlers/generated/<battler_name>_h<hue>.png`

Usage :
    py tools/generate_battler_hues.py                       # csi-forever par défaut
    py tools/generate_battler_hues.py --game=csi-ii-forever
    py tools/generate_battler_hues.py --force               # régénère même si présent
"""
import csv
import sys
from pathlib import Path

import numpy as np
from PIL import Image

ROOT         = Path(r"c:\wamp64\www\csi-world")
SRC_DIR      = ROOT / "public" / "images" / "battlers"
OUT_DIR      = SRC_DIR / "generated"
DEFAULT_GAME = "csi-forever"


def shift_hue(img: Image.Image, hue_deg: int) -> Image.Image:
    """Rotation HSV (canal H seulement) — équivalent RPG Maker `@battler_hue`.

    Pillow utilise une représentation HSV où H est sur 0-255 (pas 0-360).
    On préserve l'alpha. Saturation = 0 (pixels gris) restent gris.
    """
    img    = img.convert("RGBA")
    arr    = np.array(img)
    rgb    = Image.fromarray(arr[:, :, :3], "RGB").convert("HSV")
    hsv    = np.array(rgb)
    shift  = int(round(hue_deg * 255 / 360)) % 256
    hsv[:, :, 0] = (hsv[:, :, 0].astype(np.int16) + shift) % 256
    rgb_out = Image.fromarray(hsv, "HSV").convert("RGB")
    out     = np.dstack([np.array(rgb_out), arr[:, :, 3:4]])
    return Image.fromarray(out, "RGBA")


def process(csv_path: Path, force: bool) -> None:
    if not csv_path.exists():
        print(f"[ERROR] CSV introuvable : {csv_path}", file=sys.stderr)
        sys.exit(1)
    if not SRC_DIR.exists():
        print(f"[ERROR] dossier sources absent : {SRC_DIR}", file=sys.stderr)
        sys.exit(1)

    OUT_DIR.mkdir(parents=True, exist_ok=True)

    stats = {"todo": 0, "ok": 0, "skip_exists": 0, "skip_no_hue": 0, "missing_src": set()}
    seen  = set()

    with csv_path.open(encoding="utf-8-sig", newline="") as fh:
        for row in csv.DictReader(fh, delimiter=";"):
            name = (row.get("battler_name") or "").strip()
            hue  = int(row.get("battler_hue") or 0)
            if not name:
                continue
            if hue == 0:
                stats["skip_no_hue"] += 1
                continue
            key = (name, hue)
            if key in seen:
                continue
            seen.add(key)

            src = SRC_DIR / f"{name}.png"
            dst = OUT_DIR / f"{name}_h{hue}.png"
            if not src.exists():
                stats["missing_src"].add(name)
                continue
            if dst.exists() and not force:
                stats["skip_exists"] += 1
                continue

            stats["todo"] += 1
            try:
                shifted = shift_hue(Image.open(src), hue)
                shifted.save(dst, "PNG")
                stats["ok"] += 1
                print(f"  OK {name} +{hue}° -> {dst.name}")
            except Exception as e:
                print(f"  ERR {name} hue={hue}: {e}", file=sys.stderr)

    print()
    print(f"Couples à traiter   : {stats['todo']}")
    print(f"  Générés           : {stats['ok']}")
    print(f"  Déjà présents     : {stats['skip_exists']}")
    print(f"Lignes sans hue     : {stats['skip_no_hue']}")
    if stats["missing_src"]:
        print(f"PNG source manquants ({len(stats['missing_src'])}) :")
        for n in sorted(stats["missing_src"]):
            print(f"  - {n}.png")


def main() -> None:
    game  = DEFAULT_GAME
    force = False
    for arg in sys.argv[1:]:
        if arg.startswith("--game="):
            game = arg.split("=", 1)[1].strip() or DEFAULT_GAME
        elif arg == "--force":
            force = True
        else:
            print(f"[WARN] argument ignoré : {arg}", file=sys.stderr)

    process(ROOT / "csv_data" / game / "Enemies.csv", force)


if __name__ == "__main__":
    main()
