#!/usr/bin/env python3
"""GIF Forge deterministic checker — generic gates plus YOUR scene claims.
Usage: checker-template.py <gif> <round-tag>
Writes a JSON metrics report and a 2x2 contact sheet (frames 0 / 35% /
50% / 85%). The model never sees this file.
Edit the CONFIG section and the YOUR-MEASUREMENTS section for your scene.
"""
import json
import os
import sys
from PIL import Image

# ---- config ---------------------------------------------------------------
W, H = 960, 540            # canvas you contracted for
MIN_FRAMES, MAX_FRAMES = 48, 120
MIN_SEC, MAX_SEC = 4.0, 10.0
MAX_MB = 12
# ---------------------------------------------------------------------------

def lum(p):
    return 0.2126 * p[0] + 0.7152 * p[1] + 0.0722 * p[2]

def main():
    path, tag = sys.argv[1], sys.argv[2]
    here = os.path.dirname(os.path.abspath(__file__))
    out = {"file": path, "bytes": os.path.getsize(path)}
    im = Image.open(path)
    n = getattr(im, "n_frames", 1)
    out.update({"format": im.format, "size_wh": im.size,
                "loop": im.info.get("loop"), "frames": n})
    dur = 0
    for i in range(n):
        im.seek(i)
        dur += im.info.get("duration", 0)
    out["duration_s"] = dur / 1000.0
    picks = {"f0": 0, "f35": int(n * 0.35), "f50": int(n * 0.50),
             "f85": int(n * 0.85), "last": n - 1, "last2": n - 2}
    frames = {}
    for k, idx in picks.items():
        im.seek(idx)
        frames[k] = im.convert("RGB").copy()

    def small(img, s=(240, 135)):
        return img.resize(s).getdata()

    def near_black_frac(img):
        px = small(img)
        return sum(1 for p in px if p[0] < 8 and p[1] < 8 and p[2] < 8) / len(px)

    def meandiff(a, b):
        pa, pb = small(a), small(b)
        return sum(abs(x[0] - y[0]) + abs(x[1] - y[1]) + abs(x[2] - y[2])
                   for x, y in zip(pa, pb)) / (len(pa) * 3)

    out["near_black_f0"] = round(near_black_frac(frames["f0"]), 3)
    out["unique_colors_f50"] = len(frames["f50"].resize((480, 270))
                                           .getcolors(1 << 22) or [])
    out["diff_action"] = round(meandiff(frames["f0"], frames["f35"]), 2)
    out["diff_hold"] = round(meandiff(frames["last2"], frames["last"]), 2)

    gates = {
        "G1_format": (out["format"] == "GIF" and out["size_wh"] == (W, H)
                      and out["loop"] == 0
                      and MIN_FRAMES <= n <= MAX_FRAMES
                      and MIN_SEC <= out["duration_s"] <= MAX_SEC
                      and out["bytes"] <= MAX_MB * 1_000_000),
        "G2_background": (out["near_black_f0"] < 0.60
                          and out["unique_colors_f50"] >= 1200),
        "G3_motion_hold": (out["diff_action"] > 2.0
                           and out["diff_hold"] < out["diff_action"]),
        # --- YOUR MEASUREMENTS: replace this section with 3-6 claims a
        #     script can count about YOUR scene. Starship examples (commented):
        # "G4_booster": booster_rows >= 300,
        # "G5_flame": flame_px >= 200,
        # "G6_arms": arms >= 25,
    }
    # --- example helpers (uncomment/adapt as needed) -------------------------
    # f85 = frames["f85"]
    # booster_rows = sum(
    #     1 for y in range(H)
    #     if sum(1 for x in range(612, 669)
    #            if 100 <= lum(f85.getpixel((x, y))) <= 235) >= 10)
    # -----------------------------------------------------------------------
    out["gates"] = gates
    out["all_pass"] = all(gates.values())
    print(json.dumps(out, indent=1))

    thumbs = [frames[k].resize((W // 2, H // 2)) for k in ("f0", "f35", "f50", "f85")]
    sheet = Image.new("RGB", (W, H))
    sheet.paste(thumbs[0], (0, 0)); sheet.paste(thumbs[1], (W // 2, 0))
    sheet.paste(thumbs[2], (0, H // 2)); sheet.paste(thumbs[3], (W // 2, H // 2))
    sheet.save(os.path.join(here, f"contact-{tag}.png"))
    print("contact sheet: contact-%s.png" % tag)

if __name__ == "__main__":
    main()
