"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CellMatrix = void 0;
const col_cache_1 = require("./col-cache.js");
// Helper to check for prototype pollution
function isSafeKey(key) {
    if (typeof key === "number") {
        return true;
    }
    return key !== "__proto__" && key !== "constructor" && key !== "prototype";
}
// Safe deep clone that filters out prototype pollution keys
function safeDeepClone(obj) {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    if (Array.isArray(obj)) {
        return obj.map(item => safeDeepClone(item));
    }
    const result = {};
    for (const key of Object.keys(obj)) {
        if (isSafeKey(key)) {
            result[key] = safeDeepClone(obj[key]);
        }
    }
    return result;
}
class CellMatrix {
    constructor(template) {
        this.template = template;
        this.sheets = {};
    }
    addCell(addressStr) {
        this.addCellEx(col_cache_1.colCache.decodeEx(addressStr));
    }
    getCell(addressStr) {
        return this.findCellEx(col_cache_1.colCache.decodeEx(addressStr), true);
    }
    findCell(addressStr) {
        return this.findCellEx(col_cache_1.colCache.decodeEx(addressStr), false);
    }
    findCellAt(sheetName, rowNumber, colNumber) {
        const sheet = this.sheets[sheetName];
        const row = sheet && sheet[rowNumber];
        return row && row[colNumber];
    }
    addCellEx(address) {
        if (address.top !== undefined) {
            for (let row = address.top; row <= address.bottom; row++) {
                for (let col = address.left; col <= address.right; col++) {
                    this.getCellAt(address.sheetName, row, col);
                }
            }
        }
        else {
            this.findCellEx(address, true);
        }
    }
    getCellEx(address) {
        return this.findCellEx(address, true);
    }
    findCellEx(address, create) {
        const sheet = this.findSheet(address, create);
        const row = this.findSheetRow(sheet, address, create);
        return this.findRowCell(row, address, create);
    }
    getCellAt(sheetName, rowNumber, colNumber) {
        if (!isSafeKey(sheetName)) {
            throw new Error(`Invalid sheet name: ${sheetName}`);
        }
        const sheet = this.sheets[sheetName] || (this.sheets[sheetName] = []);
        const row = sheet[rowNumber] || (sheet[rowNumber] = []);
        const cell = row[colNumber] ||
            (row[colNumber] = {
                sheetName,
                address: col_cache_1.colCache.n2l(colNumber) + rowNumber,
                row: rowNumber,
                col: colNumber
            });
        return cell;
    }
    removeCellEx(address) {
        const sheet = this.findSheet(address, false);
        if (!sheet) {
            return;
        }
        const row = this.findSheetRow(sheet, address, false);
        if (!row) {
            return;
        }
        delete row[address.col];
    }
    forEachInSheet(sheetName, callback) {
        const sheet = this.sheets[sheetName];
        if (sheet) {
            sheet.forEach((row, rowNumber) => {
                if (row) {
                    row.forEach((cell, colNumber) => {
                        if (cell) {
                            callback(cell, rowNumber, colNumber);
                        }
                    });
                }
            });
        }
    }
    forEach(callback) {
        Object.keys(this.sheets).forEach(sheetName => {
            this.forEachInSheet(sheetName, callback);
        });
    }
    map(callback) {
        const results = [];
        this.forEach(cell => {
            results.push(callback(cell));
        });
        return results;
    }
    findSheet(address, create) {
        const name = address.sheetName;
        if (!isSafeKey(name)) {
            throw new Error(`Invalid sheet name: ${name}`);
        }
        if (Object.prototype.hasOwnProperty.call(this.sheets, name)) {
            return this.sheets[name];
        }
        if (create) {
            return (this.sheets[name] = []);
        }
        return undefined;
    }
    findSheetRow(sheet, address, create) {
        const { row } = address;
        if (!isSafeKey(row)) {
            throw new Error(`Invalid row: ${row}`);
        }
        if (sheet && sheet[row]) {
            return sheet[row];
        }
        if (create) {
            return (sheet[row] = []);
        }
        return undefined;
    }
    findRowCell(row, address, create) {
        const { col } = address;
        if (!isSafeKey(col)) {
            throw new Error(`Invalid column: ${col}`);
        }
        if (row && row[col]) {
            return row[col];
        }
        if (create) {
            return (row[col] = this.template
                ? { ...address, ...safeDeepClone(this.template) }
                : address);
        }
        return undefined;
    }
    spliceRows(sheetName, start, numDelete, numInsert) {
        const sheet = this.sheets[sheetName];
        if (sheet) {
            const inserts = [];
            for (let i = 0; i < numInsert; i++) {
                inserts.push([]);
            }
            sheet.splice(start, numDelete, ...inserts);
        }
    }
    spliceColumns(sheetName, start, numDelete, numInsert) {
        const sheet = this.sheets[sheetName];
        if (sheet) {
            const inserts = [];
            for (let i = 0; i < numInsert; i++) {
                inserts.push(null);
            }
            Object.values(sheet).forEach((row) => {
                row.splice(start, numDelete, ...inserts);
            });
        }
    }
}
exports.CellMatrix = CellMatrix;
