#!/usr/bin/env python3
"""Does the LCXL actually reach SuperCollider? Exit 0 if yes.

Replaces `aconnect -l | grep -A3 "Launch Control XL" | grep -q "128:"`, which
passed unconditionally: -A3 runs off the end of the LCXL block into the
`client 128: 'SuperCollider'` HEADER, so the string "128:" was always present
whether or not a single wire existed. A gate that cannot fail is not a gate.

The real path on this box is two hops -- LCXL 20:0 -> Midi Through 14:0 -> SC
in2 -- so a direct-only test would also be wrong. Follow the graph instead.
"""
import pathlib, re, subprocess, sys

# An optional file argument feeds a recorded graph in, so the gate itself can be
# tested for FAILURE without unplugging anything the night before a gig.
if len(sys.argv) > 1:
    out = pathlib.Path(sys.argv[1]).read_text()
else:
    try:
        out = subprocess.run(["aconnect", "-l"], capture_output=True, text=True,
                             timeout=5).stdout
    except (OSError, subprocess.SubprocessError):
        sys.exit(2)

# client id -> name, and (client:port) -> set of destination clients
names, edges, client, port = {}, {}, None, None
for line in out.splitlines():
    m = re.match(r"client (\d+): '([^']*)'", line)
    if m:
        client, names[int(m.group(1))] = int(m.group(1)), m.group(2)
        continue
    m = re.match(r"\s+(\d+) '", line)
    if m and client is not None:
        port = (client, int(m.group(1)))
        continue
    m = re.match(r"\s+Connecting To: (.*)", line)
    if m and port is not None:
        dst = {int(x) for x in re.findall(r"(\d+):\d+", m.group(1))}
        edges.setdefault(port, set()).update(dst)

def clients(pred):
    return {c for c, n in names.items() if pred(n)}

lcxl = clients(lambda n: "Launch Control XL" in n)
sc   = clients(lambda n: n == "SuperCollider")
thru = clients(lambda n: "Midi Through" in n)
if not lcxl:
    print("no LCXL on the bus"); sys.exit(1)
if not sc:
    print("SuperCollider is not on the MIDI bus at all"); sys.exit(1)

# one hop, then two hops via Midi Through
reach = {d for (c, _), ds in edges.items() if c in lcxl for d in ds}
if reach & sc:
    print("LCXL -> SC direct"); sys.exit(0)
if reach & thru:
    onward = {d for (c, _), ds in edges.items() if c in thru for d in ds}
    if onward & sc:
        print("LCXL -> Midi Through -> SC"); sys.exit(0)
    print("LCXL -> Midi Through, but Through does NOT reach SC"); sys.exit(1)
print(f"LCXL reaches {sorted(reach)} — neither SC nor Midi Through"); sys.exit(1)
