#!/usr/bin/env python3
"""Segment the delivery-arc heroes into per-part polygons.

Phase 'debug': label bright components on the void, save numbered overlays for eyeball
assignment. Phase 'emit': given a component->part mapping, write the scene manifest
(percent-coordinate hulls + collapse offsets toward the scene anchor).
"""
import json, sys, pathlib
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from scipy import ndimage
from scipy.spatial import ConvexHull

ROOT = pathlib.Path(__file__).resolve().parent.parent
W = 1376  # working width

def load(name):
    im = Image.open(ROOT / "assets/raw" / name).convert("RGB")
    r = W / im.width
    im2 = im.resize((W, int(im.height * r)))
    a = np.asarray(im2).astype(np.float32)
    lum = a @ np.array([0.299, 0.587, 0.114], dtype=np.float32)
    return im2, lum

def components(lum, thresh=26, dilate=6, min_area=350):
    mask = lum > thresh
    mask = ndimage.binary_dilation(mask, iterations=dilate)
    lab, n = ndimage.label(mask)
    out = []
    for i in range(1, n + 1):
        ys, xs = np.nonzero(lab == i)
        if len(xs) < min_area:
            continue
        out.append(dict(id=len(out) + 1, xs=xs, ys=ys,
                        cx=float(xs.mean()), cy=float(ys.mean()),
                        bbox=(int(xs.min()), int(ys.min()), int(xs.max()), int(ys.max())),
                        area=int(len(xs))))
    return out

def debug(name, tag):
    im, lum = load(name)
    comps = components(lum)
    d = ImageDraw.Draw(im)
    for c in comps:
        x0, y0, x1, y1 = c["bbox"]
        d.rectangle([x0, y0, x1, y1], outline=(63, 186, 245), width=2)
        d.text((c["cx"] - 8, c["cy"] - 10), str(c["id"]), fill=(255, 80, 80))
    out = pathlib.Path(sys.argv[3]) / f"debug-{tag}.png"
    im.save(out)
    h = im.height
    print(f"== {tag} ({len(comps)} components, {W}x{h})")
    for c in comps:
        print(f"  #{c['id']:2d} centroid=({c['cx']/W*100:5.1f}%,{c['cy']/h*100:5.1f}%) "
              f"bbox%=({c['bbox'][0]/W*100:.0f},{c['bbox'][1]/h*100:.0f},"
              f"{c['bbox'][2]/W*100:.0f},{c['bbox'][3]/h*100:.0f}) area={c['area']}")

def emit(name, tag, spec):
    im, lum = load(name)
    comps = {c["id"]: c for c in components(lum)}
    h = im.height
    parts = []
    for part in spec["parts"]:
        xs = np.concatenate([comps[i]["xs"] for i in part["comps"]])
        ys = np.concatenate([comps[i]["ys"] for i in part["comps"]])
        pts = np.stack([xs, ys], 1).astype(np.float64)
        hull = ConvexHull(pts)
        poly = pts[hull.vertices]
        # pad the hull outward from its centroid by ~1.2% of width (glow safety)
        c = poly.mean(0)
        v = poly - c
        poly = c + v * (1 + (W * 0.012) / (np.abs(v).max(1, keepdims=True) + 1e-6))
        # simplify: keep every k-th vertex to <= 18 points
        k = max(1, len(poly) // 18)
        poly = poly[::k]
        parts.append(dict(
            name=part["name"], title=part["title"], blurb=part["blurb"],
            z=part.get("z", 0),
            points=[[round(float(x) / W * 100, 2), round(float(y) / h * 100, 2)] for x, y in poly],
            cx=round(float(np.mean(xs)) / W * 100, 2), cy=round(float(np.mean(ys)) / h * 100, 2)))
    ax, ay = next((p["cx"], p["cy"]) for p in parts if p["name"] == spec["anchor"])
    for p in parts:
        nest = spec.get("nest_overrides", {}).get(p["name"], spec.get("nest", 0.72))
        p["dx"] = round((ax - p["cx"]) * nest, 2)
        p["dy"] = round((ay - p["cy"]) * nest, 2)
        del p["cx"]; del p["cy"]
    man = dict(scene=tag, source=f"assets/raw/{name}", parts=parts)
    if "stagger" in spec:
        man["stagger"] = spec["stagger"]
    out = ROOT / "pilot" / f"manifest-{tag}.json"
    out.write_text(json.dumps(man, indent=1))
    print(f"manifest-{tag}.json: {len(parts)} parts ->",
          ", ".join(f"{p['name']}({p['dx']},{p['dy']})" for p in parts))

if __name__ == "__main__":
    mode, scenes_json, outdir = sys.argv[1], sys.argv[2], sys.argv[3]
    scenes = json.loads(pathlib.Path(scenes_json).read_text()) if mode == "emit" else None
    heroes = [("course-II-cluster.jpeg", "cluster"),
              ("course-VI-gitops.jpeg", "gitops"),
              ("course-VII-supply-chain.jpeg", "supply"),
              ("course-VIII-helm-press.jpeg", "helm")]
    for name, tag in heroes:
        if mode == "debug":
            debug(name, tag)
        else:
            emit(name, tag, scenes[tag])
