#!/usr/bin/env bash
# check-sfxmix.sh - Host-cc differential harness for the shared SFX
# overlay mixer (src/core/audioSfxMix.c), built on the check-blank.sh /
# check-millis.sh pattern. Compiles the CURRENT mixer TU with the host
# cc plus tests/host/sfxMixHost.c and runs a fixed-seed scenario set
# (fixed-buffer + streaming slots, rate up/down, multi-slot, mid-mix
# refill and end-of-stream, amplitude 0/max, >64k read positions).
#
# First run captures tests/goldens/sfxmix-baseline.txt (capture it
# against the CURRENT mixer (the go-forward reference)); later runs diff per scenario:
#   default             exact mode: FNV-1a hash + raw bytes must match
#                       the baseline (gate for findings #7 and #33,
#                       which must be bit-identical).
#   JL_SFX_TOLERANCE=1  per-sample mode for finding #6: prints each
#                       scenario's max-abs-delta and allows a deviation
#                       of tolerance * slots per output sample (the
#                       1-LSB interp deviation stacks across
#                       simultaneous slots before the clamp).
#
# The harness binary is also run twice and the outputs compared, so a
# nondeterministic harness (or mixer reading uninitialized memory)
# fails loudly instead of producing a flaky baseline.
set -euo pipefail

repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
cc=${CC:-cc}
work=$(mktemp -d -t joeylib-sfxmix.XXXXXX)
trap 'rm -rf "$work"' EXIT
baseline="$repo/tests/goldens/sfxmix-baseline.txt"

cd "$repo"
# BLANK platform: audioSfxMix.c is portable C with no jlp* hooks; the
# stream fill callback is mocked inside sfxMixHost.c.
"$cc" -DJOEYLIB_PLATFORM_BLANK \
    -Iinclude -Iinclude/joey -Isrc/core -Wall \
    src/core/audioSfxMix.c tests/host/sfxMixHost.c \
    -o "$work/sfxMixHost"

"$work/sfxMixHost" > "$work/out1.txt"
"$work/sfxMixHost" > "$work/out2.txt"
if ! cmp -s "$work/out1.txt" "$work/out2.txt"; then
    echo "check-sfxmix: FAIL - harness output differs across two runs (nondeterministic)" >&2
    exit 1
fi

if [ ! -f "$baseline" ]; then
    mkdir -p "$(dirname "$baseline")"
    cp "$work/out1.txt" "$baseline"
    count=$(grep -c '^name=' "$baseline")
    echo "check-sfxmix: baseline captured ($count scenarios) -> tests/goldens/sfxmix-baseline.txt"
    exit 0
fi

tolerance="${JL_SFX_TOLERANCE:-}"
if [ -n "$tolerance" ]; then
    echo "check-sfxmix: comparing against baseline (per-sample, tolerance ${tolerance} LSB * slots)"
else
    echo "check-sfxmix: comparing against baseline (exact hash mode)"
fi

if python3 - "$baseline" "$work/out1.txt" "$tolerance" <<'PY'
import sys


def parse(path):
    order = []
    scen = {}
    name = None
    with open(path) as f:
        for line in f:
            line = line.strip()
            if line.startswith("name="):
                fields = dict(p.split("=", 1) for p in line.split())
                name = fields["name"]
                order.append(name)
                scen[name] = {"hash": fields["hash"], "slots": int(fields["slots"]), "raw": b""}
            elif line.startswith("raw=") and name is not None:
                scen[name]["raw"] = bytes.fromhex(line[4:])
    return order, scen


baseOrder, base = parse(sys.argv[1])
curOrder, cur = parse(sys.argv[2])
tolerance = int(sys.argv[3]) if sys.argv[3] else None

fail = False
if curOrder != baseOrder:
    print("check-sfxmix: scenario list changed vs baseline")
    print("  baseline: " + " ".join(baseOrder))
    print("  current:  " + " ".join(curOrder))
    fail = True

for name in baseOrder:
    if name not in cur:
        continue
    b = base[name]
    c = cur[name]
    label = "  %-24s" % (name + ":")
    if len(b["raw"]) != len(c["raw"]):
        print("%s FAIL (length %d -> %d)" % (label, len(b["raw"]), len(c["raw"])))
        fail = True
        continue
    if tolerance is None:
        ok = (b["hash"] == c["hash"]) and (b["raw"] == c["raw"])
        if ok:
            print("%s PASS (hash %s)" % (label, c["hash"]))
        else:
            print("%s FAIL (hash %s -> %s)" % (label, b["hash"], c["hash"]))
            fail = True
    else:
        maxDelta = 0
        for x, y in zip(b["raw"], c["raw"]):
            d = abs(x - y)
            if d > maxDelta:
                maxDelta = d
        allowed = tolerance * b["slots"]
        ok = maxDelta <= allowed
        print("%s maxAbsDelta=%d (allowed %d) %s" % (label, maxDelta, allowed, "PASS" if ok else "FAIL"))
        fail = fail or not ok

sys.exit(1 if fail else 0)
PY
then
    echo "check-sfxmix: PASS"
else
    echo "check-sfxmix: FAIL (mixer output diverged from tests/goldens/sfxmix-baseline.txt)" >&2
    exit 1
fi
