"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TableDesc = void 0;
const tslib_1 = require("tslib");
const is_number_1 = tslib_1.__importDefault(require("is-number"));
const utils_1 = require("./utils.js");
class TableDesc {
    rows;
    cols;
    rowsDesc;
    baseOffset;
    static __cache = new WeakMap();
    static create(table) {
        if (this.__cache.has(table))
            return this.__cache.get(table);
        if (!(0, utils_1.isTableNode)(table))
            return null;
        const tbody = table.lastChild;
        if (!tbody || !(0, utils_1.isTableBodyNode)(tbody))
            return null;
        // table –> tbody -> ...
        const baseOffset = 2;
        const rows = [];
        tbody.forEach((trow, offset) => {
            rows.push({ node: trow, offset, cells: [] });
        });
        tbody.forEach((trow, _1, trowIndex) => {
            trow.forEach((tcell, offset, tcellRealIndex) => {
                const rowDesc = rows[trowIndex];
                let tcellIndex = tcellRealIndex;
                while (rowDesc.cells[tcellIndex]) {
                    tcellIndex++;
                }
                const cellDesc = (rowDesc.cells[tcellIndex] = {
                    type: 'real',
                    node: tcell,
                    offset,
                });
                const map = [trowIndex, tcellIndex];
                if ((0, is_number_1.default)(tcell.attrs['colspan'])) {
                    rowDesc.colspan = true;
                    const colspan = (cellDesc.colspan = parseInt(tcell.attrs['colspan'], 10));
                    for (let i = 1; i < colspan; i++) {
                        rowDesc.cells[tcellIndex + i] = {
                            type: 'virtual',
                            colspan: map,
                        };
                    }
                }
                if ((0, is_number_1.default)(tcell.attrs['rowspan'])) {
                    rowDesc.rowspan = true;
                    const rowspan = (cellDesc.rowspan = parseInt(tcell.attrs['rowspan'], 10));
                    for (let i = 1; i < rowspan; i++) {
                        const colspan = cellDesc.colspan ?? 1;
                        for (let j = 0; j < colspan; j++)
                            rows[trowIndex + i].cells[tcellIndex + j] = {
                                type: 'virtual',
                                rowspan: map,
                            };
                    }
                }
            });
        });
        const desc = new this(rows.length, rows[0].cells.length, rows, baseOffset);
        this.__cache.set(table, desc);
        return desc;
    }
    constructor(
    /* eslint-disable @typescript-eslint/parameter-properties */
    rows, cols, rowsDesc, baseOffset) {
        this.rows = rows;
        this.cols = cols;
        this.rowsDesc = rowsDesc;
        this.baseOffset = baseOffset;
    }
    rowHasVirtualCells(rowIndex) {
        return this.rowsDesc[rowIndex]?.cells.some((cell) => cell.type === 'virtual');
    }
    columnHasVirtualCells(columnIndex) {
        return this.rowsDesc.some((row) => row.cells[columnIndex]?.type === 'virtual');
    }
    isSafeColumn(columnIndex) {
        return this.rowsDesc.every((row) => {
            const cell = row.cells[columnIndex];
            if (cell.type === 'real' && (!cell.colspan || cell.colspan === 1))
                return true;
            if (cell.type === 'virtual' && cell.rowspan?.[1] === columnIndex)
                return true;
            return false;
        });
    }
    isSafeRow(rowIndex) {
        return this.rowsDesc[rowIndex]?.cells.every((cell) => {
            if (cell.type === 'real' && (!cell.rowspan || cell.rowspan === 1))
                return true;
            if (cell.type === 'virtual' && cell.colspan?.[0] === rowIndex)
                return true;
            return false;
        });
    }
    getOffsetForRow(rowIndex) {
        return this.baseOffset + this.rowsDesc[rowIndex]?.offset;
    }
    getRelativePosForRow(rowIndex) {
        const from = this.getOffsetForRow(rowIndex);
        const to = from + this.rowsDesc[rowIndex].node.nodeSize;
        return { from, to };
    }
    getRelativePosForCell(rowIndex, columnIndex) {
        const cell = this.rowsDesc[rowIndex]?.cells[columnIndex];
        const rowOffset = this.getOffsetForRow(rowIndex);
        if (cell?.type === 'real') {
            // 1 – open boundary for row node
            const from = rowOffset + 1 + cell.offset;
            const to = from + cell.node.nodeSize;
            return { type: 'real', from, to };
        }
        if (cell?.type === 'virtual') {
            let index = columnIndex;
            while (--index >= 0) {
                const cell = this.rowsDesc[rowIndex].cells[index];
                if (cell.type === 'real') {
                    const closest = rowOffset + 1 + cell.offset + cell.node.nodeSize;
                    return { type: 'virtual', closestPos: closest };
                }
            }
            return { type: 'virtual', closestPos: rowOffset + 1 };
        }
        throw new Error(`Impossible to calculate offset for cell with position [${rowIndex},${columnIndex}]`);
    }
    getRelativePosForColumn(columnIndex) {
        const ranges = [];
        for (let i = 0; i < this.rows; i++) {
            ranges.push(this.getRelativePosForCell(i, columnIndex));
        }
        return ranges;
    }
    getCellNodeType() {
        const isRealCell = (cell) => cell.type === 'real';
        return this.rowsDesc[0].cells.find(isRealCell).node.type;
    }
    getRowNodeType() {
        return this.rowsDesc[0].node.type;
    }
}
exports.TableDesc = TableDesc;
//# sourceMappingURL=table-desc.js.map
