#!/usr/bin/env python3
"""Quick census of an hs_gpio capture: edge counts and true HS high widths.

Scratch helper for the first GPIO3 capture on pos5, which is too small for the
full hs_gpio_analyze.py cross-source comparison. Reports what the USB paths
cannot see: HS-active windows below their ~3 ms resolution floor.
"""

from __future__ import annotations

import collections
import json
import sys
from pathlib import Path

BUCKETS_US = (50, 100, 250, 500, 1000, 2000, 2750, 3000)


def main() -> None:
    path = Path(sys.argv[1])
    records = [
        json.loads(line)
        for line in path.read_text(encoding="utf-8").splitlines()
        if line.strip()
    ]
    kinds = collections.Counter(r.get("type") for r in records)
    print(f"{path.name}: {len(records)} records {dict(kinds)}")

    hdr = next((r for r in records if r.get("type") == "hdr"), {})
    print(
        f"  hdr: eg={hdr.get('eg')} line={hdr.get('line')} pos={hdr.get('fixture_pos')} "
        f"active_low={hdr.get('active_low')} initial_hs={hdr.get('initial_hs')}"
    )

    edges = [r for r in records if r.get("type") == "hs_edge"]
    if not edges:
        print("  no edges")
        return

    span_s = (edges[-1]["ts_ns"] - edges[0]["ts_ns"]) / 1e9
    falls = sum(1 for e in edges if e.get("edge") == "fall")
    rises = sum(1 for e in edges if e.get("edge") == "rise")
    print(f"  {len(edges)} edges ({falls} fall / {rises} rise) over {span_s:.3f} s")

    gaps = [r for r in records if r.get("type") == "seq_gap"]
    if gaps:
        print(f"  WARNING: {len(gaps)} seq gap(s) — widths spanning them are excluded")

    # HS high window = hs going 1 until hs going 0, skipping any pair across a gap.
    widths_us: list[float] = []
    open_ns: int | None = None
    last_seq: int | None = None
    for e in edges:
        seq = e.get("seqno")
        contiguous = last_seq is None or seq == last_seq + 1
        last_seq = seq
        if e.get("hs") == 1:
            open_ns = e["ts_ns"]
        elif open_ns is not None:
            if contiguous:
                widths_us.append((e["ts_ns"] - open_ns) / 1e3)
            open_ns = None

    if not widths_us:
        print("  no complete HS windows")
        return

    widths_us.sort()
    n = len(widths_us)
    print(f"  {n} complete HS-active windows, us:")
    print(
        f"    min={widths_us[0]:.1f} p50={widths_us[n // 2]:.1f} "
        f"p95={widths_us[min(n - 1, int(n * 0.95))]:.1f} max={widths_us[-1]:.1f}"
    )
    print("  census below USB resolution floor:")
    for b in BUCKETS_US:
        c = sum(1 for w in widths_us if w < b)
        print(f"    < {b:>5} us: {c:>4} / {n}")

    sub3ms = sum(1 for w in widths_us if w < 3000)
    if sub3ms:
        print(
            f"  VERDICT: {sub3ms} window(s) under 3 ms — below the GET_REG poll floor, "
            "so the bridge has been deciding on HS structure it cannot resolve."
        )
    else:
        print("  VERDICT: no sub-3 ms windows in this sample.")


if __name__ == "__main__":
    main()
