"use strict";
// src/cli/MatrixRain.ts
// Animated Matrix code rain — vertical streams of glyphs falling, brightest
// at the head fading down the tail. Renders into a fixed region via
// absolute cursor positioning so the chat scroll above stays intact.
//
// Reference: Downloads/69e80715-...png
// Usage:
//   const rain = startRain({ row: 12, col: 1, width: 60, height: 8 });
//   // ...await long task...
//   rain.stop();
Object.defineProperty(exports, "__esModule", { value: true });
exports.startRain = startRain;
exports.sampleFrame = sampleFrame;
const MatrixTheme_1 = require("./MatrixTheme");
const randomGlyph = () => MatrixTheme_1.RAIN_GLYPHS[Math.floor(Math.random() * MatrixTheme_1.RAIN_GLYPHS.length)];
/**
 * Start the rain animation. Caller MUST call `.stop()` before exit or after
 * the awaited work completes, otherwise the interval leaks.
 *
 * Safe in non-TTY contexts (returns a no-op handle) so callers don't need
 * to branch.
 */
function startRain(opts) {
    // No-op in non-TTY / no-color environments — rain would corrupt logs.
    if (!(0, MatrixTheme_1.supportsColor)() || !process.stdout.isTTY) {
        return { stop: () => { }, isRunning: () => false };
    }
    const tickMs = opts.tickMs ?? MatrixTheme_1.RAIN_CONFIG.tickMs;
    const rainWidth = Math.max(1, opts.rainWidth ?? opts.width);
    const numColumns = rainWidth;
    const columns = Array.from({ length: numColumns }, () => ({
        head: 0,
        trail: 0,
        glyphs: Array.from({ length: opts.height }, () => ' '),
        active: false,
    }));
    // Hide cursor while rain runs. Restored in stop().
    process.stdout.write(MatrixTheme_1.ANSI.hideCursor);
    const render = () => {
        // Save cursor so the user's input prompt below is preserved.
        process.stdout.write(MatrixTheme_1.ANSI.save);
        for (let cIdx = 0; cIdx < numColumns; cIdx++) {
            const col = columns[cIdx];
            // Spawn new column if dormant
            if (!col.active && Math.random() < MatrixTheme_1.RAIN_CONFIG.spawnChance) {
                col.active = true;
                col.head = 0;
                col.trail = MatrixTheme_1.RAIN_CONFIG.trailMin + Math.floor(Math.random() * (MatrixTheme_1.RAIN_CONFIG.trailMax - MatrixTheme_1.RAIN_CONFIG.trailMin));
                col.glyphs = Array.from({ length: opts.height }, () => ' ');
            }
            if (!col.active)
                continue;
            // Advance the head down one row
            col.head++;
            // Decide glyph at the new head position
            if (col.head <= opts.height) {
                col.glyphs[col.head - 1] = randomGlyph();
            }
            // Mutate a mid-trail glyph occasionally for noise
            if (Math.random() < MatrixTheme_1.RAIN_CONFIG.glyphMutate && col.head > 2) {
                const mid = col.head - 1 - Math.floor(Math.random() * Math.min(col.trail, col.head - 1));
                if (mid >= 0 && mid < opts.height)
                    col.glyphs[mid] = randomGlyph();
            }
            // Paint the column from top to bottom of the region
            for (let r = 0; r < opts.height; r++) {
                const rowRelToHead = col.head - 1 - r; // 0 = head, positive = trail above
                if (rowRelToHead < 0 || rowRelToHead > col.trail || col.glyphs[r] === ' ') {
                    // Erase residue when head moved past
                    process.stdout.write((0, MatrixTheme_1.moveTo)(opts.row + r, opts.col + cIdx) + ' ');
                    continue;
                }
                let color = MatrixTheme_1.MATRIX.body;
                if (rowRelToHead === 0)
                    color = MatrixTheme_1.MATRIX.head;
                else if (rowRelToHead >= col.trail - 2)
                    color = MatrixTheme_1.MATRIX.tail;
                process.stdout.write((0, MatrixTheme_1.moveTo)(opts.row + r, opts.col + cIdx) + color + col.glyphs[r] + MatrixTheme_1.ANSI.reset);
            }
            // Deactivate when entire trail has scrolled off
            if (col.head > opts.height + col.trail) {
                col.active = false;
            }
        }
        // Optional side panel overlay (reasoning text, etc.)
        if (opts.sidePanelLines) {
            const lines = opts.sidePanelLines();
            const panelCol = opts.col + rainWidth + 1;
            for (let i = 0; i < Math.min(lines.length, opts.height); i++) {
                // Clear the line in the region first to avoid overdraw
                process.stdout.write((0, MatrixTheme_1.moveTo)(opts.row + i, panelCol) + MatrixTheme_1.ANSI.clearLine.replace(/\[2K/, '[K'));
                process.stdout.write((0, MatrixTheme_1.moveTo)(opts.row + i, panelCol) + MatrixTheme_1.MATRIX.body + lines[i] + MatrixTheme_1.ANSI.reset);
            }
        }
        process.stdout.write(MatrixTheme_1.ANSI.restore);
    };
    const intervalId = setInterval(render, tickMs);
    let running = true;
    return {
        isRunning: () => running,
        stop: () => {
            if (!running)
                return;
            running = false;
            clearInterval(intervalId);
            // Clear the rain region so the next chat output starts clean.
            process.stdout.write(MatrixTheme_1.ANSI.save);
            for (let r = 0; r < opts.height; r++) {
                process.stdout.write((0, MatrixTheme_1.moveTo)(opts.row + r, opts.col));
                process.stdout.write(' '.repeat(opts.width));
            }
            process.stdout.write(MatrixTheme_1.ANSI.restore);
            process.stdout.write(MatrixTheme_1.ANSI.showCursor);
            process.stdout.write(MatrixTheme_1.ANSI.reset);
        },
    };
}
/**
 * Static (non-interactive) sample frame — used in tests to confirm the
 * renderer doesn't blow up and produces reasonable bytes.
 */
function sampleFrame(width = 20, height = 5) {
    const rows = [];
    for (let r = 0; r < height; r++) {
        let line = '';
        for (let c = 0; c < width; c++) {
            const dice = Math.random();
            if (dice < 0.05)
                line += MatrixTheme_1.MATRIX.head + randomGlyph() + MatrixTheme_1.ANSI.reset;
            else if (dice < 0.25)
                line += MatrixTheme_1.MATRIX.body + randomGlyph() + MatrixTheme_1.ANSI.reset;
            else if (dice < 0.40)
                line += MatrixTheme_1.MATRIX.tail + randomGlyph() + MatrixTheme_1.ANSI.reset;
            else
                line += ' ';
        }
        rows.push(line);
    }
    return rows.join('\n');
}
