<script>
    /* Diagnostics — the desktop's debug overlay (Ctrl+Shift+D) as a screen,
       on the dev flavour only: what this process costs the phone, what the
       network is moving, the track playing and its loudness correction, the
       tracks the pre-buffer is fetching behind it, and how big the play
       history has grown. Polled once a second while the screen is open and
       never otherwise, the way the overlay does it. The GPU counters are
       Windows PDH and the private commit is a Win32 call, so neither row is
       here — on the phone they would read nothing at all. */
    import Icon from "../../components/Icon.svelte";
    import * as api from "../../lib/api.js";
    import { fmtBytes, basename } from "../util.js";

    let stats = $state(null);
    let plays = $state(null);
    let failed = $state("");

    /* nothing reactive is READ in here: the effect runs once when the screen
       mounts and its cleanup when the screen leaves. An effect that reads
       what it writes takes its whole subtree down with it. */
    $effect(() => {
        let live = true;
        const tick = async () => {
            try {
                const s = await api.debugStats();
                if (live) { stats = s; failed = ""; }
            } catch (e) {
                if (live) failed = String(e);
            }
        };
        void tick();
        api.playRowCount().then((n) => { if (live) plays = n; }, () => {});
        const id = setInterval(tick, 1000);
        return () => { live = false; clearInterval(id); };
    });

    const pct = (v) => (v == null ? "—" : v.toFixed(1) + "%");
    const size = (n) => (n ? fmtBytes(n) : "—");
    const rate = (n) => (n ? fmtBytes(n) + "/s" : "—");
    const db = (v) => (v == null ? "—" : (v > 0 ? "+" : "") + v.toFixed(1) + " dB");
    const upfor = (s) => {
        if (s == null) return "—";
        const h = Math.floor(s / 3600), m = Math.floor((s % 3600) / 60);
        return h ? h + " hr " + m + " min" : m ? m + " min" : s + " s";
    };
    const share = (p) => (p.total ? Math.max(0.02, Math.min(1, p.received / p.total)) : 0);
</script>

<div class="phead"><div class="row"><h1>Diagnostics</h1></div></div>
<div class="sec-note">The desktop's debug overlay, on the dev flavour. Polled once a second while this screen is open.</div>

{#if failed}
    <div class="stub">
        <b>No diagnostics.</b>
        {failed}
    </div>
{/if}

<div class="sheet-sec">This process</div>
<div class="group diag">
    <div class="kv"><span class="k">App CPU</span><span class="v">{pct(stats?.cpuProcess)}</span></div>
    <div class="kv"><span class="k">System CPU</span><span class="v">{pct(stats?.cpuSystem)}</span></div>
    <div class="kv"><span class="k">Memory</span><span class="v">{size(stats?.memWorkingSet)}</span></div>
    <div class="kv"><span class="k">System memory</span><span class="v dim">{size(stats?.memSystemUsed)} of {size(stats?.memSystemTotal)}</span></div>
    <div class="kv"><span class="k">Processes</span><span class="v">{stats?.processCount ?? "—"}</span></div>
    <div class="kv"><span class="k">Up</span><span class="v">{upfor(stats?.uptimeSecs)}</span></div>
</div>
<div class="sec-note">Processes counts this app's own tree. Android runs the WebView in a system process of its own, so it is outside these numbers; on the desktop the whole WebView2 family is inside them.</div>

<div class="sheet-sec">Network</div>
<div class="group diag">
    <div class="kv"><span class="k">Down</span><span class="v">{rate(stats?.netRxBps)}</span></div>
    <div class="kv"><span class="k">Up</span><span class="v">{rate(stats?.netTxBps)}</span></div>
</div>
<div class="sec-note">Every interface on the phone, not this app alone.</div>

<div class="sheet-sec">The track playing</div>
<div class="group diag">
    <div class="kv"><span class="k">Stream</span><span class="v">{!stats ? "—" : stats.dlTotal ? size(stats.dlReceived) + " of " + size(stats.dlTotal) + (stats.dlDone ? " · done" : "") : "nothing streaming"}</span></div>
    <div class="kv"><span class="k">Normalization</span><span class="v">{!stats ? "—" : stats.normalizationEnabled ? "on · " + (stats.targetLufs ?? -14) + " LUFS" : "off"}</span></div>
    <div class="kv"><span class="k">Measured</span><span class="v dim">{stats?.loudness?.lufs != null ? stats.loudness.lufs.toFixed(1) + " LUFS" + (stats.loudness.analyzing ? " · measuring" : "") : "—"}</span></div>
    <div class="kv"><span class="k">Gain</span><span class="v">{db(stats?.normNowDb)}{stats && stats.normNowDb != null && stats.normNowDb !== stats.normTargetDb ? " → " + db(stats.normTargetDb) : ""}</span></div>
</div>

<div class="sheet-sec">On deck</div>
{#if stats?.preloadDl?.length}
    <div class="group diag-deck">
        {#each stats.preloadDl as p (p.username + p.path)}
            <div class="row">
                <span class="lead"><Icon name="download" size={20} /></span>
                <span class="b">
                    <span class="t">{basename(p.path)}</span>
                    <span class="m">{p.username} · {p.state}{p.place != null ? " · queue " + p.place : ""}{p.speedBps ? " · " + fmtBytes(p.speedBps) + "/s" : ""}</span>
                    {#if p.total}<span class="lbar"><i style="transform: scaleX({share(p).toFixed(3)})"></i><em>{fmtBytes(p.received)}</em></span>{/if}
                </span>
            </div>
        {/each}
    </div>
{:else}
    <div class="sec-note">Nothing pre-buffering. The player readies the next three tracks while one plays.</div>
{/if}

<div class="sheet-sec">Library</div>
<div class="group diag">
    <div class="kv"><span class="k">Play rows</span><span class="v">{plays != null ? plays.toLocaleString() : "—"}</span></div>
</div>

<div class="endpad"></div>
