// A factory network's devices found with Bonjour: the printers (IPP and LPD), web panels and
// Chromecast screens on the LAN with their addresses and TXT records, the kinds of service
// the network has, a .local name resolved; and this program advertising its own dashboard
// (and a label printer's queue), which it then finds itself, and a watch that sees services
// come and go.
//
// No server needed: it uses the LAN's multicast (UDP 5353). Where multicast is blocked (some
// containers and VPNs) browsing finds nothing.
//
//   quillo run mdns.qwe      then open http://localhost:8080/printers

use "mdns.qwer"

let PORT = int(env("PORT", "8080"))

// This program on the network, as a web dashboard and a print queue.
let me = mdns_advertise("Line 3 dashboard", "_http._tcp", PORT, { path: "/line3", vendor: "Quillo" }, {})
let queue = mdns_advertise("Line 3 label printer", "_quillo-ipp._tcp", 6310, { rp: "ipp/print", ty: "Zebra ZT411", pdl: "application/vnd.zebra-zpl", note: "Hall 2" }, {})

// What comes and goes on the network, kept as it happens.
let seen = []
let watching = mdns_watch("labels", "_quillo-ipp._tcp")
fn drain() {
    if !watching { return null }
    let got = mdns_receive("labels", 100, 0)
    if got { seen = slice(seen + (got | map((e) => `${e.event} ${e.name}`)), -50) }
}
every(500, drain)

route GET /printers = {
    ipp: mdns_browse("_ipp._tcp", 1500)? | map((p) => { name: p.name, host: p.host, port: p.port, model: p.txt.ty ?? null }),
    lpd: mdns_browse("_printer._tcp", 1500)? | map((p) => p.name),
}

route GET /screens = mdns_browse("_googlecast._tcp", 1500)? | map((c) => { name: c.txt.fn ?? c.name, model: c.txt.md ?? null, addresses: c.addresses })

route GET /types = mdns_service_types(1500)?

// This program finds what it advertises, as any other machine on the LAN would.
route GET /self {
    me?
    queue?
    let q = mdns_browse("_quillo-ipp._tcp", 2000)? | find((s) => s.name == "Line 3 label printer")
    let web = mdns_browse("_http._tcp", 2000)? | find((s) => s.name == "Line 3 dashboard")
    {
        queue: q == null ? null : { port: q.port, txt: q.txt, has_address: len(q.addresses) > 0 },
        dashboard: web == null ? null : { port: web.port, path: web.txt.path, host: web.host },
        resolved: web == null ? null : len(mdns_resolve(web.host, 1500)?) > 0,
    }
}

// A second queue advertised and withdrawn: the watch sees both.
route GET /comings {
    mdns_advertise("Line 4 label printer", "_quillo-ipp._tcp", 6311, { rp: "ipp/print" }, {})?
    sleep(1500)
    mdns_unadvertise("Line 4 label printer._quillo-ipp._tcp")?
    sleep(1500)
    { seen: seen | filter((s) => contains(s, "Line 4")) }
}

route GET /offline = {
    bad_type: mdns_browse("ipp", 10).error,
    not_local: mdns_resolve("example.com", 10).error,
}
