"""Split a contact sheet of background panels into individual images.

A sheet arrives as one image holding several panels separated by thin white
gutters. Rather than hardcoding a grid, this finds the gutters: rows and
columns that are almost entirely near-white are separators, everything between
them is a panel. That survives a sheet with a different panel count or uneven
spacing.

Usage:
    python scripts/split_backgrounds.py                     # inspect + write panels
    python scripts/split_backgrounds.py --sheet path.png
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

from PIL import Image

ROOT = Path(__file__).resolve().parent.parent
DEFAULT_SHEET = ROOT / "public" / "images" / "_backgrounds-sheet.png"
OUT_DIR = ROOT / "public" / "images" / "_panels"

# A line counts as gutter if this fraction of its pixels are near-white.
GUTTER_RATIO = 0.97
NEAR_WHITE = 244
MIN_PANEL_PX = 80          # ignore slivers
SAMPLE_STEP = 4            # subsample across the line; gutters are uniform


def runs(is_gutter: list[bool], min_len: int) -> list[tuple[int, int]]:
    """Contiguous spans where is_gutter is False — i.e. the panels."""
    spans, start = [], None
    for i, g in enumerate(is_gutter):
        if not g and start is None:
            start = i
        elif g and start is not None:
            if i - start >= min_len:
                spans.append((start, i))
            start = None
    if start is not None and len(is_gutter) - start >= min_len:
        spans.append((start, len(is_gutter)))
    return spans


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--sheet", type=Path, default=DEFAULT_SHEET)
    ap.add_argument("--quality", type=int, default=88)
    ap.add_argument("--max-width", type=int, default=1920)
    args = ap.parse_args()

    if not args.sheet.exists():
        print("Sheet not found: {}".format(args.sheet), file=sys.stderr)
        print("Save the contact sheet there, then re-run.", file=sys.stderr)
        return 1

    im = Image.open(args.sheet).convert("RGB")
    w, h = im.size
    px = im.load()
    print("Sheet   : {}  {}x{}".format(args.sheet.name, w, h))

    col_gutter = []
    for x in range(w):
        near = sum(1 for y in range(0, h, SAMPLE_STEP)
                   if min(px[x, y]) >= NEAR_WHITE)
        col_gutter.append(near / len(range(0, h, SAMPLE_STEP)) >= GUTTER_RATIO)

    row_gutter = []
    for y in range(h):
        near = sum(1 for x in range(0, w, SAMPLE_STEP)
                   if min(px[x, y]) >= NEAR_WHITE)
        row_gutter.append(near / len(range(0, w, SAMPLE_STEP)) >= GUTTER_RATIO)

    cols = runs(col_gutter, MIN_PANEL_PX)
    rows = runs(row_gutter, MIN_PANEL_PX)
    print("Grid    : {} column(s) x {} row(s) = {} panel(s)".format(
        len(cols), len(rows), len(cols) * len(rows)))
    if not cols or not rows:
        print("No panels detected — is the sheet gutter-separated?", file=sys.stderr)
        return 1

    OUT_DIR.mkdir(parents=True, exist_ok=True)
    for f in OUT_DIR.glob("panel-*.jpg"):
        f.unlink()

    n = 0
    for r, (y0, y1) in enumerate(rows, start=1):
        for c, (x0, x1) in enumerate(cols, start=1):
            n += 1
            panel = im.crop((x0, y0, x1, y1))
            if panel.width > args.max_width:
                ratio = args.max_width / panel.width
                panel = panel.resize(
                    (args.max_width, int(panel.height * ratio)), Image.LANCZOS)
            dest = OUT_DIR / "panel-{:02d}.jpg".format(n)
            panel.save(dest, "JPEG", quality=args.quality, optimize=True)
            print("  panel-{:02d}  r{}c{}  {}x{}  {:.0f} KB".format(
                n, r, c, panel.width, panel.height, dest.stat().st_size / 1024))

    print("Wrote {} panel(s) to public/images/_panels/".format(n))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
