"""Build Camera Test Register replay pack 1. Requires FFmpeg with libx264."""
from pathlib import Path
import hashlib
import json
import subprocess

ROOT = Path(__file__).resolve().parent
MEDIA = ROOT / "media"
MEDIA.mkdir(exist_ok=True)
FPS, WIDTH, HEIGHT, FRAMES = 10, 640, 360, 40
CC0 = "https://creativecommons.org/publicdomain/zero/1.0/"
BYSA = "https://creativecommons.org/licenses/by-sa/4.0/"
SOURCES = [
    ("Temporary_traffic_signal_at_Colonel_By_Drive.webm", "137a", CC0, "9fcef2db704442d3de1974c385fda1afce096f2dd93e9201d91dbee51e70d953"),
    ("Cars_Passing_by_at_Night.webm", "Maximilian Schönherr", BYSA, "f15a2299b70fe4f43b87e411ccc05641d34e4dd74139c3247acf61f630e3b3c0"),
    ("Dean_Street_timelapse_Albury_NSW.webm", "Thennicke", CC0, "d7c226e29a1d114ecb399d0ba14174879f882c81f7e42e35ead09690075e2951")
]
cases = []

def digest(path):
    return hashlib.sha256(path.read_bytes()).hexdigest()

def command(*args):
    return ["ffmpeg", "-v", "error", "-y", *args]

def encode(case_id, inputs, vf=None, pixels=None):
    final = MEDIA / (case_id + ".mp4")
    args = command(*inputs, "-an", "-map_metadata", "-1")
    if vf:
        args += ["-vf", vf]
    args += ["-frames:v", str(FRAMES), "-c:v", "libx264", "-threads", "1", "-preset", "medium", "-crf", "20", "-pix_fmt", "yuv420p", "-movflags", "+faststart", str(final)]
    subprocess.run(args, input=pixels, check=True)
    return {"id": case_id, "file": "media/" + final.name, "sha256": digest(final)}

def interval(label, start, end):
    return {"label": label, "start_frame": start, "end_frame_exclusive": end}

def rectangle(frame, x, y, w, h, color):
    row = bytes(color) * w
    for line in range(y, y + h):
        offset = (line * WIDTH + x) * 3
        frame[offset:offset+len(row)] = row

def control(idx, n):
    frame = bytearray(bytes((29,40,49)) * WIDTH * HEIGHT)
    rectangle(frame, 320, 0, 2, HEIGHT, (81,100,110))
    if idx == 1:
        rectangle(frame, 80 + n*10, 160, 32, 32, (240,185,60))
    elif idx == 2 and 10 <= n < 30:
        rectangle(frame, 420, 150, 32, 64, (135,215,225))
    elif idx == 3 and (5 <= n < 12 or 22 <= n < 29):
        rectangle(frame, 220, 90, 40, 40, (230,160,60))
    elif idx == 4:
        rectangle(frame, 145+n*6, 165, 70, 30, (180,220,230))
        rectangle(frame, 405-n*6, 170, 60, 30, (225,165,75))
    elif idx == 5 and 15 <= n < 20:
        frame[:] = bytes((175,175,175)) * WIDTH * HEIGHT
    return frame

control_specs = [
    ("Empty divider", [], "Expect a constant frame throughout."),
    ("Divider crossing", [interval("left edge at or right of x=320",24,40)], "Follow the amber square across the center line."),
    ("Two-second hold", [interval("cyan rectangle visible",10,30)], "Check the inclusive onset and exclusive release frame."),
    ("Repeat pulse", [interval("square visible",5,12), interval("square visible",22,29)], "Two appearances separated by ten empty frames."),
    ("Overlapping paths", [interval("rectangles overlap",16,27)], "Separate two colored tracks through partial occlusion."),
    ("Luminance step", [interval("gray frame",15,20)], "Isolate a full-frame illumination change.")
]
for i, (name, events, task) in enumerate(control_specs):
    data = b"".join(control(i, n) for n in range(FRAMES))
    entry = encode(f"C{i+1:02}", ["-f","rawvideo","-pixel_format","rgb24","-video_size","640x360","-framerate","10","-i","pipe:0"], pixels=data)
    cases.append(dict(entry, title=name, group="synthetic control", family=f"control-{i+1}", provenance="Procedural RGB frames from this script. No camera capture or generative model.", annotations=events, task=task, label_basis="construction", license=CC0))

field_specs = [
    (0,1,"Approach lane","Red signal at right; traffic approaches in the left lane.", "red signal lit"),
    (0,10,"Waiting lane","A light-colored vehicle waits on the right while opposing traffic passes.", "light vehicle present at right"),
    (1,2,"Oncoming lights","Night street with lit storefront at left and traffic approaching.", "street and lit storefront visible"),
    (1,9,"Signal queue","View down the road toward red lights and waiting traffic.", "queue scene visible"),
    (2,1,"High view, first interval","Elevated night timelapse. Observe the bright intersection near the lower center.", "illuminated junction visible"),
    (2,8,"High view, later interval","Later interval from the same timelapse. Preserve playback order across the cut.", "illuminated junction visible")
]
for i, (src, start, name, task, label) in enumerate(field_specs, 1):
    original, author, license_url, sha = SOURCES[src]
    path = ROOT / "originals" / original
    if digest(path) != sha:
        raise ValueError(f"Unexpected source bytes: {original}")
    entry = encode(f"F{i:02}", ["-ss",str(start),"-i",str(path)], "fps=10,scale=640:360,setsar=1")
    cases.append(dict(entry, title=name, group="field excerpt", family=original, creator=author, license=license_url,
        source_page="https://commons.wikimedia.org/wiki/File:" + original, source_sha256=sha, source_interval_seconds=[start,start+4],
        changes="Four-second excerpt, resized, sampled to 10 fps, H.264 encode, sound removed.", task=task,
        annotations=[interval(label,10,11),interval(label,20,21)], label_basis="Scene landmarks at sampled frames. No exhaustive object tracks or event boundaries."))

# The bumper first touches the segment on frame 27, read on the decoded F01.
cases[6]["annotations"].append(interval("first approaching sedan front bumper reaches y=270 within x=[0,320)",27,28))
cases[6]["label_basis"] += " The single specified bumper crossing was inspected frame by frame. Coordinate origin is top left, in decoded pixels."

edit_specs = [
    ("M01","Gray approach","F01","hue=s=0","Chroma removed throughout.", [interval("grayscale",0,40)]),
    ("M02","Masked approach","F01","drawbox=x=0:y=180:w=320:h=180:color=black:t=fill:enable='between(n,10,29)'","Lower-left quadrant covered for two seconds.",[interval("black quadrant",10,30)]),
    ("M03","Held night frame","F04","freezeframes=first=10:last=29:replace=9","Frame 9 held across frames 10 through 29; the stream continues.",[interval("inserted hold",10,30)])
]
for cid, title, parent, vf, task, annotations in edit_specs:
    base = next(c for c in cases if c["id"] == parent)
    inp = str(MEDIA / (parent + ".mp4"))
    if cid == "M03":
        entry = encode(cid, ["-i",inp,"-i",inp,"-filter_complex","[0:v][1:v]"+vf+"[out]","-map","[out]"])
    else:
        entry = encode(cid, ["-i",inp], vf)
    cases.append(dict(entry, title=title, group="altered field", parent=parent, family=base["family"], creator=base["creator"],
        source_page=base["source_page"],license=base["license"],changes=task, task="Compare with "+parent+". "+task,
        annotations=annotations,label_basis="Edit timeline relative to the delivered clip."))

manifest = {"release":"CTR-1", "format":{"container":"MP4","codec":"H.264","pixel_format":"yuv420p","audio":False,"width":WIDTH,"height":HEIGHT,"fps":FPS,"frames_per_clip":FRAMES,"duration_seconds":4}, "frame_origin":0,
    "interval_convention":"[start_frame,end_frame_exclusive); t in seconds = frame / 10",
    "encoder":subprocess.check_output(["ffmpeg","-version"],text=True).splitlines()[0], "cases":cases}
(ROOT / "manifest.json").write_text(json.dumps(manifest,indent=2,ensure_ascii=False)+"\n")
