"""Validate saved row timing diagrams and the actual decoded original clip"""

import argparse
import json
import subprocess
from fractions import Fraction
from pathlib import Path

import numpy as np

p = argparse.ArgumentParser()
p.add_argument("assets", type=Path)
a = p.parse_args()
root = a.assets
cases = []
for name, sign, interval in [("default", 1, 24), ("reverse", -1, 24), ("zero", 1, 0)]:
    d = json.loads((root / f"rolling-{name}.json").read_text())
    for row in d["rows"]:
        exact = sign * Fraction(str(row["row"])) / 359 - Fraction(sign, 2)
        shift = 1200 * exact * Fraction(interval, 1000)
        assert abs(row["horizontalShiftPixels"] - float(shift)) < 1e-10
    cases.append({"name": name, "rows": len(d["rows"]), "endShift": d["topToBottomShiftPixels"]})
f = root / "rolling-row-comparison.mp4"
probe = json.loads(
    subprocess.check_output(
        ["ffprobe", "-v", "error", "-show_streams", "-show_format", "-of", "json", str(f)]
    )
)
v = next(s for s in probe["streams"] if s["codec_type"] == "video")
assert [v["width"], v["height"], int(v["nb_frames"]), v["avg_frame_rate"]] == [
    1920,
    720,
    72,
    "24/1",
]
assert not any(s["codec_type"] == "audio" for s in probe["streams"])
frames = json.loads(
    subprocess.check_output(
        [
            "ffprobe",
            "-v",
            "error",
            "-select_streams",
            "v:0",
            "-show_entries",
            "frame=best_effort_timestamp_time",
            "-of",
            "json",
            str(f),
        ]
    )
)["frames"]
assert len(frames) == 72
error = max(abs(float(x["best_effort_timestamp_time"]) - i / 24) for i, x in enumerate(frames))
assert error < 0.000002
raw = subprocess.check_output(
    [
        "ffmpeg",
        "-v",
        "error",
        "-i",
        str(f),
        "-ss",
        "1.5",
        "-frames:v",
        "1",
        "-f",
        "rawvideo",
        "-pix_fmt",
        "rgb24",
        "pipe:1",
    ]
)
im = np.frombuffer(raw, dtype=np.uint8).reshape(720, 1920, 3).astype(float)
centers = []
for j, expected in enumerate([0, 28.8, -28.8]):
    measured = []
    for y in [240, 599]:
        row = im[y, j * 640 + 280 : j * 640 + 440]
        ids = np.where((row[:, 1] - row[:, 0] > 80) & (row[:, 1] > 150))[0] + 280
        assert len(ids) > 30 and len(ids) < 45
        measured.append(float(ids.mean()))
    shift = measured[1] - measured[0]
    assert abs(shift - expected) < 1.2, (j, shift)
    centers.append(
        {"panel": j, "expectedPointSampleShift": expected, "decodedThresholdCenterShift": shift}
    )
print(
    json.dumps(
        {
            "scope": "Independent rational row table checks and decoded native simulation not sensor measurement or real footage repair",
            "timingCases": cases,
            "native": {
                "frames": 72,
                "fps": "24/1",
                "duration": float(probe["format"]["duration"]),
                "dimensions": [1920, 720],
                "audioStreams": 0,
                "timestampErrorSeconds": error,
                "middleFrameRowShifts": centers,
            },
        },
        indent=2,
    )
)
