/* ── the measured envelope, and the rough pass over it ───────────────────
   The desktop's editor offers the same two things (LyricsSyncEditor.svelte):
   a strip showing where the recording has voice in it, and a fill that lays
   the lines across the ACTIVE stretches only. src/lib/lyric-sync.js is the
   desktop's own generator — pure, shared, unit-tested — so the phone imports
   it rather than keeping a second copy of the arithmetic. It refuses instead
   of guessing when the measurement shows nothing usable: an estimate is a
   starting point for the tap, never finished timing, and the room says so
   everywhere it shows. The envelope belongs to the track, not to the store —
   it is measured on open and thrown away with the room. */
async function loadActivity(id) {
    /* a streamed track is a cache file the engine may still be filling, and
       the reducer refuses one on the same decode path for the same reason */
    if (!id?.path || player.track?.stream) { tapSync.reading = "none"; return; }
    tapSync.reading = "reading";
    try {
        const a = await api.lyricsVocalActivity(id.path);
        if (tapSync.identity?.key !== id.key) return;   // the room moved on
        tapSync.activity = a?.frames?.length ? a : null;
        tapSync.reading = tapSync.activity ? "ready" : "none";
    } catch {
        if (tapSync.identity?.key === id.key) tapSync.reading = "none";
    }
}
/** Lay every line across the stretches the envelope says are sung, then put
 *  the cursor back at the top: the taps CORRECT the estimate from there. */
export function estimateTimings() {
    const a = tapSync.activity;
    if (!a || !tapSync.rows.length) return false;
    /* the room's rows ARE the lines: openTapSync built them from the document
       the sheet is showing. So the generator's two halves are called directly
       rather than roughSync's own text parse, which drops a section label a
       SYNCED document still carries and would hand back a shorter list that
       no longer lines up with the rows the room is asking about. */
    const filled = distributeLines(tapSync.rows.map((r) => String(r.text ?? "").trim()), activeSegments(a.frames, a.frameMs));
    /* it refuses rather than guesses when there is too little measured voice
       to lay the lines across — and that is the only reason it refuses, so
       the room can say which one it is */
    if (!filled) {
        tapSync.error = "There is not enough voice in this song to estimate from.";
        return false;
    }
    for (let i = 0; i < filled.length; i++) tapSync.rows[i].time_ms = filled[i].time_ms;
    tapSync.cursor = 0;
    tapSync.estimated = true;
    tapSync.error = "";
    return true;
}
/** Drop it: every line back to no time, ready for the tap. A rough fill
 *  stamps every row at once, so Undo has nothing left to take back — this
 *  is the way out of an estimate that is wrong. */
export function clearEstimate() {
    for (const r of tapSync.rows) r.time_ms = null;
    tapSync.cursor = 0;
    tapSync.estimated = false;
    tapSync.error = "";
}
