#!/usr/bin/env python3
"""Ch340 byte-exact parser gate. Regenerates the synthetic fixture and asserts gs_parse decodes it
into the EXACT expected normalized event stream (container header + every GIF mode). No board, no
real dump needed. Exit non-zero on any mismatch."""
import os, sys, subprocess
HERE = os.path.dirname(__file__)
sys.path.insert(0, HERE)
import gs_parse

def main():
    subprocess.run([sys.executable, os.path.join(HERE, "gs_make_synthetic.py")], check=True)
    fix = os.path.join(HERE, "..", "captures", "gs", "synthetic", "mini.gs")
    h, ev = gs_parse.parse_dump(fix)
    fail = 0
    def check(cond, msg):
        nonlocal fail
        print(("  ok   " if cond else "  FAIL ") + msg)
        if not cond: fail = 1

    check(h.serial == "SYNTH001", f"serial == SYNTH001 (got {h.serial!r})")
    check(h.crc == 0x12345678, f"crc == 0x12345678 (got 0x{h.crc:08x})")
    check(h.state_size == 16, f"state_size == 16 (got {h.state_size})")

    # The expected ordered (kind, reg, value) skeleton of the event stream.
    got = [(e.kind, e.reg, e.value) for e in ev]
    exp = [
        ("TRANSFER","",0),
          ("GIFTAG","",0),
          ("GSREG","FRAME_1",0x0000_0000_0C00_1807),
        ("TRANSFER","",0),
          ("GIFTAG","",0),
          ("GSREG","PRIM", 3 | (1<<4) | (1<<6)),                 # via PRE
          ("GSREG","RGBAQ", 0xFF | (0<<8) | (0<<16) | (0x80<<24)),   # vtx0 red
          ("GSREG","XYZ2",  (100<<4) | ((50<<4)<<16) | (0x5000<<32)),
          ("GSREG","RGBAQ", 0x00 | (0xFF<<8) | (0<<16) | (0x80<<24)),# vtx1 green
          ("GSREG","XYZ2",  (200<<4) | ((50<<4)<<16) | (0x5000<<32)),
          ("GSREG","RGBAQ", 0x00 | (0<<8) | (0xFF<<16) | (0x80<<24)),# vtx2 blue
          ("GSREG","XYZ2",  (100<<4) | ((150<<4)<<16) | (0x5000<<32)),
        ("TRANSFER","",0),
          ("GIFTAG","",0),
          ("IMAGE","",0),
        ("FRAME_BOUNDARY","",0),
    ]
    check(len(got)==len(exp), f"event count == {len(exp)} (got {len(got)})")
    for i,(g,e) in enumerate(zip(got,exp)):
        # compare kind always; reg+value only where the expected has them
        if e[0] in ("GSREG",):
            check(g==e, f"event[{i}] {e}  (got {g})")
        else:
            check(g[0]==e[0], f"event[{i}] kind {e[0]}  (got {g[0]})")
    # IMAGE qwc + no malformed
    img=[e for e in ev if e.kind=="IMAGE"]
    check(len(img)==1 and img[0].info.get("qwc")==2, f"one IMAGE qwc==2 (got {[e.info for e in img]})")
    check(all(e.kind!="MALFORMED" for e in ev), "no MALFORMED events")

    # Ch342 — PACKED ST must expose the STQ Q lane as info["q_stq"] (don't drop RGBAQ.Q).
    h2, ev2 = gs_parse.parse_dump(os.path.join(HERE, "..", "captures", "gs", "synthetic", "mini_st.gs"))
    sts = [e for e in ev2 if e.kind=="GSREG" and e.reg=="ST"]
    check(len(sts)==1, f"mini_st: one ST event (got {len(sts)})")
    if sts:
        e=sts[0]
        check((e.value & 0xFFFFFFFF)==0x11111111, "mini_st: ST.S == 0x11111111")
        check(((e.value>>32)&0xFFFFFFFF)==0x22222222, "mini_st: ST.T == 0x22222222")
        check(e.info.get("q_stq")==0x33333333, f"mini_st: q_stq == 0x33333333 (got {e.info.get('q_stq')})")

    print("RESULT:", "PASS" if not fail else "FAIL")
    return fail

if __name__ == "__main__":
    sys.exit(main())
