"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const worker_threads_1 = require("worker_threads");
const fs_1 = require("fs");
const glyphs_1 = require("./glyphs");
// Write directly to fd 1 (stdout) instead of writeStdout().
// In Node.js worker threads, process.stdout is proxied through the main
// thread's event loop — so if the main thread is blocked (e.g. SQLite),
// stdout writes from the worker queue up and the animation freezes.
// fs.writeSync(1, ...) is a direct kernel syscall that bypasses this.
//
// Side effect: bypasses Node's TTY-aware encoding conversion on Windows,
// so UTF-8 bytes hit the console raw and mojibake on OEM codepages.
// `getGlyphs()` returns ASCII fallbacks on Windows to avoid this (#168).
function writeStdout(s) {
    (0, fs_1.writeSync)(1, s);
}
const G = (0, glyphs_1.getGlyphs)();
const SPINNER_GLYPHS = G.spinner;
const ANIM_INTERVAL = 150;
const FRAMES_PER_GLYPH = 3;
const RST = '\x1b[0m';
const DM = '\x1b[2m';
const GRN = '\x1b[32m';
const BOLD = '\x1b[1m';
const startTime = worker_threads_1.workerData.startTime;
function animFrame() {
    return Math.floor((Date.now() - startTime) / ANIM_INTERVAL);
}
function lerp(a, b, t) {
    return Math.round(a + (b - a) * t);
}
function shimmerColor(frame) {
    const t = (Math.sin(frame * 2 * Math.PI / 13) + 1) / 2;
    const r = lerp(160, 251, t);
    const g = lerp(100, 191, t);
    const b = lerp(9, 36, t);
    return `\x1b[38;2;${r};${g};${b}m${BOLD}`;
}
function formatNumber(n) {
    return n.toLocaleString();
}
function renderBar(frame, filled, empty) {
    if (filled === 0)
        return `${DM}${G.barEmpty.repeat(empty)}${RST}`;
    const cycleFrames = 24;
    const shimmerPos = ((frame % cycleFrames) / cycleFrames) * (filled + 6) - 3;
    const shimmerWidth = 3;
    let bar = '';
    for (let i = 0; i < filled; i++) {
        const dist = Math.abs(i - shimmerPos);
        const t = Math.max(0, 1 - dist / shimmerWidth);
        const r = lerp(160, 251, t);
        const g = lerp(100, 191, t);
        const b = lerp(9, 36, t);
        bar += `\x1b[38;2;${r};${g};${b}m${BOLD}${G.barFilled}`;
    }
    bar += `${RST}${DM}${G.barEmpty.repeat(empty)}${RST}`;
    return bar;
}
// Mutable state
let currentMessage = '';
let currentPercent = -1;
let currentCount = 0;
let currentDetail = '';
function render() {
    if (!currentMessage)
        return;
    const frame = animFrame();
    const glyphIdx = Math.floor(frame / FRAMES_PER_GLYPH) % SPINNER_GLYPHS.length;
    const glyph = SPINNER_GLYPHS[glyphIdx] ?? SPINNER_GLYPHS[0] ?? '.';
    const color = shimmerColor(frame);
    let line;
    if (currentPercent >= 0) {
        const barWidth = 25;
        const filled = Math.round(barWidth * currentPercent / 100);
        const empty = barWidth - filled;
        line = `${DM}${G.rail}${RST}  ${color}${glyph}${RST} ${currentMessage}${currentDetail ? ` ${DM}${G.dash}${RST} ${currentDetail}` : ''}  ${renderBar(frame, filled, empty)}  ${currentPercent}%`;
    }
    else if (currentCount > 0) {
        line = `${DM}${G.rail}${RST}  ${color}${glyph}${RST} ${currentMessage}... ${formatNumber(currentCount)} found`;
    }
    else {
        line = `${DM}${G.rail}${RST}  ${color}${glyph}${RST} ${currentMessage}${currentDetail ? ` ${DM}${G.dash}${RST} ${currentDetail}` : '...'}`;
    }
    writeStdout(`\r\x1b[K${line}`);
}
function finishPhase() {
    if (!currentMessage)
        return;
    writeStdout(`\r\x1b[K`);
    let detail = '';
    if (currentPercent >= 0)
        detail = ` ${G.dash} done`;
    else if (currentCount > 0)
        detail = ` ${G.dash} ${formatNumber(currentCount)} found`;
    writeStdout(`${DM}${G.rail}${RST}  ${GRN}${G.phaseDone}${RST} ${currentMessage}${detail}\n`);
    currentMessage = '';
    currentPercent = -1;
    currentCount = 0;
    currentDetail = '';
}
// Render loop — independent of main thread
const tickInterval = setInterval(render, 50);
worker_threads_1.parentPort.on('message', (msg) => {
    if (msg.type === 'update') {
        currentMessage = msg.phaseName;
        currentPercent = msg.percent;
        currentCount = msg.count;
        currentDetail = msg.detail ?? '';
    }
    else if (msg.type === 'finish-phase') {
        finishPhase();
    }
    else if (msg.type === 'stop') {
        clearInterval(tickInterval);
        finishPhase();
        worker_threads_1.parentPort.postMessage({ type: 'stopped' });
    }
});
//# sourceMappingURL=shimmer-worker.js.map