#!/usr/bin/env python3
"""Encoding-agnostic: which words of an object actually carry live state?

Rather than assume the orientation is a matrix (it is not) or a quaternion,
classify every 4-byte word of the captured window by how it behaves over the
capture: constant, smoothly varying (a continuous quantity), or jumpy.

Usage: whatchanges.py <probe.bin> [name-substring] [max-report]
"""
import math
import struct
import sys

sys.path.insert(0, "/home/fabi/RE - Project Sylpheed/sylpheed-reborn/tools/re-capture")
from flight_analyze import load  # noqa: E402


def main():
    path = sys.argv[1]
    want = sys.argv[2] if len(sys.argv) > 2 else "Player"
    top = int(sys.argv[3]) if len(sys.argv) > 3 else 60
    insts, window, frames = load(path)
    i = next(k for k, (_, _, nm) in enumerate(insts) if want in nm)
    print(f"# {insts[i][2]} @ {insts[i][0]:#010x}, {len(frames)} frames, window {window:#x}")

    nconst = nsmooth = njump = 0
    smooth = []
    for o in range(0, window - 3, 4):
        vals = []
        ok = True
        for t, inp, lab, bufs in frames:
            (v,) = struct.unpack_from(">f", bufs[i], o)
            if not math.isfinite(v):
                ok = False
                break
            vals.append(v)
        if not ok:
            continue
        lo, hi = min(vals), max(vals)
        if lo == hi:
            nconst += 1
            continue
        rng = hi - lo
        steps = [abs(b - a) for a, b in zip(vals, vals[1:])]
        mx = max(steps)
        # smooth = no single step is a large fraction of the whole excursion
        if mx < 0.25 * rng:
            nsmooth += 1
            smooth.append((o, lo, hi, vals))
        else:
            njump += 1
    print(f"# constant {nconst}   smooth {nsmooth}   jumpy {njump}")
    print(f"\n# smoothly varying words (candidate continuous state):")
    for o, lo, hi, vals in smooth[:top]:
        print(f"  +{o:#05x}  [{lo:12.4g} .. {hi:12.4g}]  "
              f"start {vals[0]:12.4g}  end {vals[-1]:12.4g}")

    # consecutive runs of smooth words -> vectors
    offs = [o for o, _, _, _ in smooth]
    runs, cur = [], []
    for o in offs:
        if cur and o == cur[-1] + 4:
            cur.append(o)
        else:
            if len(cur) >= 3:
                runs.append(cur)
            cur = [o]
    if len(cur) >= 3:
        runs.append(cur)
    print(f"\n# runs of >=3 consecutive smooth words (vector-shaped):")
    for r in runs:
        print(f"  +{r[0]:#05x} .. +{r[-1]:#05x}  ({len(r)} words)")


if __name__ == "__main__":
    main()
