/**
 * GridTable — generic scrollable table with box-drawing borders.
 *
 * Renders a fixed-width columnar table with cursor highlighting,
 * scrolling, and box-drawing borders. Column definitions control
 * header text, width bounds, and per-cell rendering; the table
 * handles padding, alignment, and scroll state.
 */
import { Container, Text, visibleWidth } from '../../../tui/index.js';
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function padVisible(text, targetWidth) {
    const w = visibleWidth(text);
    return w >= targetWidth ? text : text + ' '.repeat(targetWidth - w);
}
/** Build one horizontal grid line: ┌──┬──┐ or ├──┼──┤ or └──┴──┘ */
function gridLine(bdr, left, mid, right, widths) {
    return ('  ' + bdr(left) + widths.map((w) => bdr('\u2500'.repeat(w + 2))).join(bdr(mid)) + bdr(right));
}
// ---------------------------------------------------------------------------
// GridTable
// ---------------------------------------------------------------------------
export const DEFAULT_GRID_VISIBLE_ROWS = 8;
export class GridTable extends Container {
    cursorRow = 0;
    scrollOffset = 0;
    columns;
    rows;
    theme;
    inner;
    colWidths;
    maxVisibleRows;
    constructor(columns, rows, theme, options) {
        super();
        this.columns = columns;
        this.rows = rows;
        this.theme = theme;
        this.maxVisibleRows = Math.max(1, Math.floor(options?.maxVisibleRows ?? DEFAULT_GRID_VISIBLE_ROWS));
        // Compute widths from ALL rows so layout is stable across scrolling
        this.colWidths = columns.map((col) => {
            let w = col.minWidth;
            for (const row of rows)
                w = Math.max(w, col.width(row));
            return Math.min(w, col.maxWidth);
        });
        this.inner = new Container();
        this.addChild(this.inner);
        this.rebuild();
    }
    moveCursor(delta) {
        const maxRow = this.rows.length - 1;
        if (maxRow < 0)
            return;
        const next = Math.max(0, Math.min(maxRow, this.cursorRow + delta));
        if (next === this.cursorRow)
            return;
        this.cursorRow = next;
        if (this.cursorRow < this.scrollOffset) {
            this.scrollOffset = this.cursorRow;
        }
        else if (this.cursorRow >= this.scrollOffset + this.maxVisibleRows) {
            this.scrollOffset = this.cursorRow - this.maxVisibleRows + 1;
        }
        this.rebuild();
    }
    movePage(direction) {
        const maxRow = this.rows.length - 1;
        if (maxRow < 0)
            return;
        const maxOffset = Math.max(0, this.rows.length - this.maxVisibleRows);
        this.scrollOffset = Math.max(0, Math.min(maxOffset, this.scrollOffset + direction * this.maxVisibleRows));
        this.cursorRow = Math.max(this.scrollOffset, Math.min(this.scrollOffset + this.maxVisibleRows - 1, this.cursorRow + direction * this.maxVisibleRows));
        this.rebuild();
    }
    scrollBy(delta) {
        const maxRow = this.rows.length - 1;
        if (maxRow < 0)
            return;
        const maxOffset = Math.max(0, this.rows.length - this.maxVisibleRows);
        const nextOffset = Math.max(0, Math.min(maxOffset, this.scrollOffset + delta));
        if (nextOffset === this.scrollOffset)
            return;
        this.scrollOffset = nextOffset;
        this.cursorRow = Math.max(this.scrollOffset, Math.min(this.scrollOffset + this.maxVisibleRows - 1, this.cursorRow));
        this.rebuild();
    }
    setMaxVisibleRows(maxVisibleRows) {
        const next = Math.max(1, Math.floor(maxVisibleRows));
        if (next === this.maxVisibleRows)
            return;
        this.maxVisibleRows = next;
        const maxOffset = Math.max(0, this.rows.length - this.maxVisibleRows);
        this.scrollOffset = Math.max(0, Math.min(this.scrollOffset, maxOffset));
        if (this.cursorRow < this.scrollOffset) {
            this.cursorRow = this.scrollOffset;
        }
        else if (this.cursorRow >= this.scrollOffset + this.maxVisibleRows) {
            this.cursorRow = Math.min(this.rows.length - 1, this.scrollOffset + this.maxVisibleRows - 1);
        }
        this.rebuild();
    }
    rebuild() {
        this.inner.clear();
        const { theme, columns, rows, colWidths: widths } = this;
        const visible = rows.slice(this.scrollOffset, this.scrollOffset + this.maxVisibleRows);
        const bdr = (s) => theme.fg('border', s);
        const addLine = (l, m, r) => this.inner.addChild(new Text(gridLine(bdr, l, m, r, widths), 0, 0));
        const addRow = (cells, prefix) => {
            // Pad each cell to its column width so columns stay aligned
            const parts = cells.map((c, i) => ` ${padVisible(c, widths[i])} `);
            this.inner.addChild(new Text(prefix + bdr('\u2502') + parts.join(bdr('\u2502')) + bdr('\u2502'), 0, 0));
        };
        // Top border
        addLine('\u250c', '\u252c', '\u2510');
        // Header
        addRow(widths.map((w, i) => theme.fg('text', theme.bold(padVisible(columns[i].header, w)))), '  ');
        // Header separator
        addLine('\u251c', '\u253c', '\u2524');
        // Data rows
        for (let idx = 0; idx < visible.length; idx++) {
            const row = visible[idx];
            const absIdx = this.scrollOffset + idx;
            const isCursor = absIdx === this.cursorRow;
            addRow(widths.map((w, i) => columns[i].render(row, w, isCursor, theme)), isCursor ? '\u25b8 ' : '  ');
            if (idx < visible.length - 1)
                addLine('\u251c', '\u253c', '\u2524');
        }
        // Bottom border
        addLine('\u2514', '\u2534', '\u2518');
        // Scroll indicator
        if (rows.length > this.maxVisibleRows) {
            const info = `  ${this.scrollOffset + 1}-${Math.min(this.scrollOffset + this.maxVisibleRows, rows.length)} of ${rows.length}`;
            this.inner.addChild(new Text(theme.fg('dim', info + '  (\u2191\u2193 navigate)'), 0, 0));
        }
    }
}
//# sourceMappingURL=grid-table.js.map