"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Renderer = void 0;
const ansi = __importStar(require("./utils/ansi"));
const MIN_COLS = 40;
const MIN_ROWS = 10;
class Renderer {
    emulator;
    geometry;
    prevChildHash = [];
    prevGameHash = [];
    forceFullRedraw = true;
    gameWidthPercent = 50;
    constructor(emulator, gameWidthPercent = 50) {
        this.emulator = emulator;
        this.gameWidthPercent = gameWidthPercent;
        this.geometry = this.calculateGeometry();
    }
    setGameWidthPercent(pct) {
        this.gameWidthPercent = pct;
    }
    calculateGeometry() {
        const totalCols = process.stdout.columns || 80;
        const totalRows = process.stdout.rows || 24;
        let rightWidth = Math.max(20, Math.floor(totalCols * this.gameWidthPercent / 100));
        let leftWidth = Math.max(20, totalCols - rightWidth - 1);
        // Clamp: panels + border must not exceed totalCols
        if (leftWidth + rightWidth + 1 > totalCols) {
            rightWidth = totalCols - leftWidth - 1;
        }
        // Enforce minimum 10 columns for each panel
        rightWidth = Math.max(10, rightWidth);
        leftWidth = Math.max(10, leftWidth);
        return {
            leftWidth,
            rightWidth,
            height: totalRows,
            borderCol: leftWidth,
        };
    }
    updateGeometry() {
        this.geometry = this.calculateGeometry();
        this.forceFullRedraw = true;
        return this.geometry;
    }
    get currentGeometry() {
        return this.geometry;
    }
    isTooSmall() {
        const cols = process.stdout.columns || 80;
        const rows = process.stdout.rows || 24;
        return cols < MIN_COLS || rows < MIN_ROWS;
    }
    renderTooSmall() {
        const cols = process.stdout.columns || 80;
        const rows = process.stdout.rows || 24;
        const msg = 'Terminal too small';
        const sub = `Need ${MIN_COLS}x${MIN_ROWS}, got ${cols}x${rows}`;
        let output = ansi.hideCursor() + ansi.clearScreen();
        const midRow = Math.max(1, Math.floor(rows / 2));
        const msgCol = Math.max(1, Math.floor((cols - msg.length) / 2) + 1);
        const subCol = Math.max(1, Math.floor((cols - sub.length) / 2) + 1);
        output += ansi.moveTo(midRow, msgCol) + msg;
        output += ansi.moveTo(midRow + 1, subCol) + sub;
        output += ansi.showCursor();
        process.stdout.write(output);
        this.invalidate();
    }
    renderSplit(gameState, statusBar) {
        if (this.isTooSmall()) {
            this.renderTooSmall();
            return;
        }
        const { leftWidth, rightWidth, height, borderCol } = this.geometry;
        let output = ansi.hideCursor();
        for (let row = 0; row < height; row++) {
            const childRow = this.emulator.getRow(row);
            const childRowStr = this.renderChildRow(childRow, leftWidth, row);
            const gameRow = gameState.grid[row];
            const gameRowStr = this.renderGameRow(gameRow, rightWidth, borderCol, row);
            // Border
            output += ansi.moveTo(row + 1, borderCol + 1);
            output += ansi.resetAttributes();
            output += ansi.noColor ? '│' : `\x1b[38;5;240m│`;
            // Only update rows that changed
            const childHash = childRowStr;
            const gameHash = gameRowStr;
            if (this.forceFullRedraw || this.prevChildHash[row] !== childHash) {
                output += childRowStr;
            }
            if (this.forceFullRedraw || this.prevGameHash[row] !== gameHash) {
                output += gameRowStr;
            }
            this.prevChildHash[row] = childHash;
            this.prevGameHash[row] = gameHash;
        }
        // Status bar
        const barText = statusBar ?? this.defaultStatusBar(gameState, rightWidth);
        const truncated = barText.slice(0, rightWidth);
        output += ansi.moveTo(height, borderCol + 2);
        output += ansi.resetAttributes();
        if (!ansi.noColor)
            output += `\x1b[48;5;236m\x1b[38;5;252m`;
        output += truncated.padEnd(rightWidth, ' ');
        // Cursor
        const cursor = this.emulator.getCursor();
        output += ansi.moveTo(cursor.y + 1, Math.min(cursor.x + 1, leftWidth));
        output += ansi.resetAttributes();
        output += ansi.showCursor();
        process.stdout.write(output);
        this.forceFullRedraw = false;
    }
    defaultStatusBar(gameState, width) {
        const statusMsg = gameState.statusMessage || '';
        return ` Score: ${gameState.score} | ${gameState.status.toUpperCase()} ${statusMsg ? '| ' + statusMsg + ' ' : ''}`;
    }
    renderFullscreen() {
        if (this.isTooSmall()) {
            this.renderTooSmall();
            return;
        }
        const totalCols = process.stdout.columns || 80;
        const totalRows = process.stdout.rows || 24;
        let output = ansi.hideCursor();
        for (let row = 0; row < totalRows; row++) {
            output += ansi.moveTo(row + 1, 1);
            const childRow = this.emulator.getRow(row);
            let prevAttrs = '';
            output += ansi.resetAttributes();
            for (let col = 0; col < totalCols; col++) {
                const cell = childRow[col] || this.emptyCell();
                if (cell.width === 0)
                    continue;
                const attrs = this.buildAttrs(cell);
                if (attrs !== prevAttrs) {
                    output += ansi.resetAttributes() + attrs;
                    prevAttrs = attrs;
                }
                output += cell.char;
            }
        }
        const cursor = this.emulator.getCursor();
        output += ansi.moveTo(cursor.y + 1, cursor.x + 1);
        output += ansi.resetAttributes();
        output += ansi.showCursor();
        process.stdout.write(output);
    }
    invalidate() {
        this.forceFullRedraw = true;
        this.prevChildHash = [];
        this.prevGameHash = [];
    }
    renderChildRow(cells, width, row) {
        let out = ansi.moveTo(row + 1, 1);
        let prevAttrs = '';
        out += ansi.resetAttributes();
        for (let col = 0; col < width; col++) {
            const cell = cells[col] || this.emptyCell();
            if (cell.width === 0)
                continue;
            const attrs = this.buildAttrs(cell);
            if (attrs !== prevAttrs) {
                out += ansi.resetAttributes() + attrs;
                prevAttrs = attrs;
            }
            out += cell.char;
        }
        return out;
    }
    renderGameRow(cells, width, borderCol, row) {
        let out = ansi.moveTo(row + 1, borderCol + 2);
        out += ansi.resetAttributes();
        let prevFg = '';
        let prevBg = '';
        for (let col = 0; col < width; col++) {
            const cell = cells?.[col] || { char: ' ', fg: { mode: 'default', value: 0 }, bg: { mode: 'default', value: 0 } };
            const fg = ansi.fgColor(cell.fg);
            const bg = ansi.bgColor(cell.bg);
            if (fg !== prevFg || bg !== prevBg) {
                if (fg !== prevFg) {
                    out += fg;
                    prevFg = fg;
                }
                if (bg !== prevBg) {
                    out += bg;
                    prevBg = bg;
                }
            }
            out += cell.char;
        }
        return out;
    }
    buildAttrs(cell) {
        let s = '';
        if (cell.bold)
            s += ansi.bold();
        if (cell.dim)
            s += ansi.dim();
        if (cell.italic)
            s += ansi.italic();
        if (cell.underline)
            s += ansi.underline();
        if (cell.inverse)
            s += ansi.inverse();
        if (cell.strikethrough)
            s += ansi.strikethrough();
        s += ansi.fgColor(cell.fg);
        s += ansi.bgColor(cell.bg);
        return s;
    }
    emptyCell() {
        return {
            char: ' ', width: 1,
            fg: { mode: 'default', value: 0 },
            bg: { mode: 'default', value: 0 },
            bold: false, italic: false, underline: false,
            dim: false, inverse: false, strikethrough: false,
        };
    }
}
exports.Renderer = Renderer;
//# sourceMappingURL=renderer.js.map