#!/usr/bin/env python3
"""C2 Task B + C3 (JSON) for cli-anything-audacity (sox backend). Writes taskB/commands.jsonl, taskB/results.json."""
import json, subprocess, time, datetime, os, wave, numpy as np
W = os.path.abspath("taskB"); os.makedirs(W, exist_ok=True); os.chdir(W)
SR = 44100
def tone(path, f, secs):
    t = np.arange(int(SR * secs)) / SR; x = (0.5 * np.sin(2 * np.pi * f * t) * 32767).astype("<i2")
    with wave.open(path, "wb") as w: w.setnchannels(1); w.setsampwidth(2); w.setframerate(SR); w.writeframes(x.tobytes())
tone("tone440.wav", 440, 1.0); tone("tone880.wav", 880, 2.0)
A = "cli-anything-audacity"; rows = []; log = open("commands.jsonl", "w")
def run(args, expect_ok=True, tag="task"):
    t0 = time.time(); ts = datetime.datetime.now().strftime("%H:%M:%S")
    p = subprocess.run([A, "--json"] + args, capture_output=True, text=True); dt = round(time.time() - t0, 3)
    try: json.loads(p.stdout); ok = True
    except Exception: ok = False
    r = {"time_utc8": ts, "cmd": [A, "--json"] + args, "exit": p.returncode, "secs": dt, "stdout_is_one_json_value": ok,
         "stdout": p.stdout[:1500], "stderr": p.stderr[:800], "tag": tag, "expect_ok": expect_ok}
    log.write(json.dumps(r) + "\n"); log.flush(); rows.append(r)
    print(f"[{ts} {dt}s exit {p.returncode} json={ok}] {' '.join(args)[:110]}")
P = "tones.audacity-cli.json"
if os.path.exists(P): os.remove(P)
run(["project", "new", "--name", "Tones", "-sr", "44100", "-ch", "1", "-o", P])
run(["--project", P, "track", "add", "-n", "Tones"])
run(["--project", P, "clip", "import", "tone440.wav"])
run(["--project", P, "clip", "add", "0", "tone440.wav", "-n", "a440", "-s", "0"])
run(["--project", P, "clip", "add", "0", "tone880.wav", "-n", "b880", "-s", "1.0"])
run(["--project", P, "clip", "list", "0"])
run(["--project", P, "project", "info"])
run(["--project", P, "export", "render", "out.wav", "-p", "wav", "--overwrite"])
run(["--project", P, "media", "probe", "out.wav"])
run(["--project", "does-not-exist.json", "project", "info"], expect_ok=False, tag="error")
run(["--project", P, "clip", "add", "0", "tone440.wav", "-s", "notanumber"], expect_ok=False, tag="error")
run(["--project", P, "clip", "remove", "0", "99"], expect_ok=False, tag="error")
res = {}
if os.path.exists("out.wav"):
    with wave.open("out.wav") as w:
        n, sr, ch, sw = w.getnframes(), w.getframerate(), w.getnchannels(), w.getsampwidth()
        x = np.frombuffer(w.readframes(n), dtype="<i2").astype(float)
        if ch > 1: x = x.reshape(-1, ch).mean(1)
    def dom(a):
        if len(a) == 0: return None
        a = a * np.hanning(len(a)); s = np.abs(np.fft.rfft(a)); return round(float(np.fft.rfftfreq(len(a), 1 / sr)[s.argmax()]), 1)
    res = {"frames": n, "sample_rate": sr, "channels": ch, "sample_width_bytes": sw, "duration_s": round(n / sr, 4),
           "dominant_hz_0.05-0.95s": dom(x[int(.05*sr):int(.95*sr)]), "dominant_hz_1.05-2.95s": dom(x[int(1.05*sr):int(2.95*sr)]),
           "peak": float(np.abs(x).max()) / 32767 if len(x) else None,
           "ground_truth": {"duration_s": 3.0, "tol_s": 0.01, "hz_first": 440, "hz_rest": 880, "tol_hz": 5}}
print(json.dumps(res, indent=1)); json.dump({"verify": res, "commands": rows}, open("results.json", "w"), indent=1)
