#!/usr/bin/env python3
"""Slice a 3x3 go-bananas spot-illustration sheet into transparent PNGs.

House-style illustrations are MULTI-TONE (ink + pale secondary + white + one
accent), like the Stripe/Linear icon systems — so we must NOT flatten to one
colour. White ground is knocked out by flood-filling from the CELL EDGES only
(white *interior* details — screens, knockouts — survive), then every pixel
snaps to the nearest brand hex so the film's palette stays exact. A mid steel
tone is kept in the snap set: a pure-INK snap collapses ring/nub detail.

Env: BRAND_PACK (palette + ICON_SHEET_NAMES), BRAND_PROJECT.
Usage: python3 slice_icons.py <sheet.png>
Writes <project>/assets/icons/<name>.png and fails if any cell slices empty.
"""
import os
import sys
from collections import deque

from PIL import Image

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from packlib import parse_pack as _parse_pack, hex_rgb as _hex  # noqa: E402

PACK = _parse_pack(os.environ["BRAND_PACK"])
PROJ = os.environ["BRAND_PROJECT"]

INK = _hex(PACK.get("COLOR_INK"))
SECONDARY = _hex(PACK.get("COLOR_SECONDARY"))
ACCENT = _hex(PACK.get("COLOR_ACCENT"))
WHITE = _hex(PACK.get("COLOR_CARD"), (0xFF, 0xFF, 0xFF))
STEEL = (0x6B, 0x66, 0x66)
SNAP = [INK, SECONDARY, ACCENT, WHITE, STEEL]

NAMES = [n.strip() for n in PACK.get("ICON_SHEET_NAMES", "").split("|") if n.strip()]
if len(NAMES) != 9:
    sys.exit(f"pack: ICON_SHEET_NAMES must list 9 |-separated names, got {len(NAMES)}")

OUTDIR = os.path.join(PROJ, "assets", "icons")
BG_TOL = 26


def is_bg(p):
    return p[0] > 255 - BG_TOL and p[1] > 255 - BG_TOL and p[2] > 255 - BG_TOL


def snap(p):
    best, bd = p[:3], 1 << 30
    for c in SNAP:
        d = (p[0] - c[0]) ** 2 + (p[1] - c[1]) ** 2 + (p[2] - c[2]) ** 2
        if d < bd:
            bd, best = d, c
    return best


def main():
    sheet_path = sys.argv[1]
    os.makedirs(OUTDIR, exist_ok=True)
    im = Image.open(sheet_path).convert("RGBA")
    W, H = im.size
    cw, ch = W // 3, H // 3
    print(f"sheet {W}x{H} -> cells {cw}x{ch}")

    for idx, name in enumerate(NAMES):
        r, c = divmod(idx, 3)
        cell = im.crop((c * cw, r * ch, (c + 1) * cw, (r + 1) * ch)).convert("RGBA")
        px = cell.load()
        w, h = cell.size

        # flood the OUTER white only, from the cell edges
        outside = bytearray(w * h)
        q = deque()
        for x in range(w):
            for y in (0, h - 1):
                if is_bg(px[x, y]) and not outside[y * w + x]:
                    outside[y * w + x] = 1
                    q.append((x, y))
        for y in range(h):
            for x in (0, w - 1):
                if is_bg(px[x, y]) and not outside[y * w + x]:
                    outside[y * w + x] = 1
                    q.append((x, y))
        while q:
            x, y = q.popleft()
            for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)):
                nx, ny = x + dx, y + dy
                if 0 <= nx < w and 0 <= ny < h and not outside[ny * w + nx] and is_bg(px[nx, ny]):
                    outside[ny * w + nx] = 1
                    q.append((nx, ny))

        for y in range(h):
            row = y * w
            for x in range(w):
                if outside[row + x]:
                    px[x, y] = (0, 0, 0, 0)
                else:
                    rr, gg, bb, aa = px[x, y]
                    s = snap((rr, gg, bb))
                    px[x, y] = (s[0], s[1], s[2], aa)

        bbox = cell.getbbox()
        if not bbox or (bbox[2] - bbox[0]) < 40 or (bbox[3] - bbox[1]) < 40:
            sys.exit(f"slice: cell {idx} ({name}) sliced empty/tiny — regenerate the sheet")
        cell = cell.crop(bbox)
        out = os.path.join(OUTDIR, f"{name}.png")
        cell.save(out)
        print(f"  {name:12} {cell.size[0]}x{cell.size[1]} -> {out}")


if __name__ == "__main__":
    main()
