#!/Users/jje/JJ-PROJECTS/landr-master-live/python_live
"""Route check — DDJ-FLX input peak scan."""
from __future__ import annotations

import math
import os
import sys
from pathlib import Path

_ROOT = Path(__file__).resolve().parent
os.chdir(_ROOT)
sys.path.insert(0, str(_ROOT))

import numpy as np
import sounddevice as sd

WANTS = ("flx", "ddj-flx", "ddj", "pioneer")
FORBID = ("16ch", "64ch", "256ch")
THRESH_DB = -48.0
SCAN_SEC = 0.8


def peak_db(rec: np.ndarray) -> float:
    peak = float(np.max(np.abs(rec)))
    return 20.0 * math.log10(max(peak, 1e-12))


def input_candidates() -> list[tuple[int, str]]:
    out: list[tuple[int, str]] = []
    for i, d in enumerate(sd.query_devices()):
        if d["max_input_channels"] < 1:
            continue
        name = d["name"]
        low = name.lower()
        if any(x in low for x in FORBID):
            continue
        if any(w in low for w in WANTS):
            out.append((i, name))
    return out


def pick_default(cands: list[tuple[int, str]]) -> tuple[int, str]:
    for w in WANTS:
        for i, name in cands:
            if w in name.lower():
                return i, name
    if cands:
        return cands[0]
    sys.exit("No DDJ / Pioneer input found")


def scan_all(cands: list[tuple[int, str]]) -> list[dict]:
    rows: list[dict] = []
    for idx, name in cands:
        sr = int(sd.query_devices(idx).get("default_samplerate") or 48000)
        n = int(sr * SCAN_SEC)
        try:
            max_in = int(sd.query_devices(idx).get("max_input_channels") or 2)
            ch = min(4, max_in) if any(x in name.lower() for x in ("ddj", "flx", "pioneer")) else 2
            rec = sd.rec(n, samplerate=sr, channels=ch, dtype="float32", device=idx)
            sd.wait()
            dbp = peak_db(rec)
            pair = ""
            if ch >= 4:
                e01 = float(np.max(np.abs(rec[:, 0:2])))
                e23 = float(np.max(np.abs(rec[:, 2:4])))
                pair = "1-2" if e01 >= e23 else "3-4"
            rows.append({"idx": idx, "name": name, "peak_db": dbp, "lock": dbp > THRESH_DB, "usb_pair": pair})
        except OSError as e:
            rows.append({"idx": idx, "name": name, "error": str(e)})
    rows.sort(key=lambda r: r.get("peak_db", -999), reverse=True)
    return rows


def main() -> int:
    cands = input_candidates()
    rows = scan_all(cands)
    best = rows[0] if rows else {}
    idx, name = pick_default(cands)
    print("Route  Input → Master → Output", flush=True)
    print(f"17 Input  {name}  #{idx}", flush=True)
    print("--- play a deck ---", flush=True)
    for r in rows:
        if "error" in r:
            print(f"  #{r['idx']}  {r['name']}  ERR {r['error']}", flush=True)
        else:
            tag = "signal lock" if r["lock"] else "silent"
            extra = f"  USB {r['usb_pair']}" if r.get("usb_pair") else ""
            print(f"  #{r['idx']}  {r['name']}  {r['peak_db']:+.1f} dB  {tag}{extra}", flush=True)
    if best.get("lock"):
        print(f"\nUse input  #{best['idx']}  {best['name']}", flush=True)
        return 0
    print("\nNo signal yet — djay output = DDJ-FLX", flush=True)
    return 1


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