import { ErrorCode, ValueTag } from '@bilig/protocol';
function isFreshLiteralLogicalSheetInternals(value) {
    return typeof value === 'object' && value !== null;
}
function shouldMaterializeLiteralCell(raw) {
    return raw !== null && raw !== undefined;
}
export function loadLiteralSheetIntoEmptySheet(workbook, strings, sheetId, content, shouldMaterialize = shouldMaterializeLiteralCell, inspection) {
    const sheet = workbook.getSheetById(sheetId);
    if (!sheet) {
        throw new Error(`Unknown sheet id: ${sheetId}`);
    }
    let potentialCellCount = inspection?.materializedCellCount ?? 0;
    let maxColumnCount = inspection?.maxColumnCount ?? 0;
    if (!inspection) {
        for (let rowIndex = 0; rowIndex < content.length; rowIndex += 1) {
            const row = content[rowIndex];
            const width = row?.length ?? 0;
            maxColumnCount = Math.max(maxColumnCount, width);
            if (!row) {
                continue;
            }
            for (let colIndex = 0; colIndex < row.length; colIndex += 1) {
                if (shouldMaterialize(row[colIndex], rowIndex, colIndex)) {
                    potentialCellCount += 1;
                }
            }
        }
    }
    if (potentialCellCount === 0) {
        return 0;
    }
    const cellStore = workbook.cellStore;
    cellStore.ensureCapacity(cellStore.size + potentialCellCount);
    const writtenColumns = new Uint8Array(maxColumnCount);
    const rowIds = [];
    const colIds = [];
    const ensureRowId = workbook.createLogicalAxisIdEnsurer(sheetId, 'row');
    const ensureColumnId = workbook.createLogicalAxisIdEnsurer(sheetId, 'column');
    const attachFreshCell = createFreshLiteralCellAttacher(workbook, sheet);
    let writtenColumnCount = 0;
    const previousOnSetValue = cellStore.onSetValue;
    cellStore.onSetValue = null;
    let literalCount = 0;
    try {
        for (let rowIndex = 0; rowIndex < content.length; rowIndex += 1) {
            const row = content[rowIndex];
            for (let colIndex = 0; colIndex < row.length; colIndex += 1) {
                const raw = row[colIndex];
                if (!shouldMaterialize(raw, rowIndex, colIndex)) {
                    continue;
                }
                const cellIndex = cellStore.allocateReserved(sheetId, rowIndex, colIndex);
                literalCount += 1;
                if (writtenColumns[colIndex] === 0) {
                    writtenColumns[colIndex] = 1;
                    writtenColumnCount += 1;
                }
                const rowId = (rowIds[rowIndex] ??= ensureRowId(rowIndex));
                const colId = (colIds[colIndex] ??= ensureColumnId(colIndex));
                attachFreshCell(rowIndex, colIndex, cellIndex, rowId, colId);
                writeLiteralCell(cellStore, strings, cellIndex, raw);
            }
        }
        if (writtenColumnCount > 0) {
            workbook.notifyColumnsWritten(sheetId, materializeWrittenColumns(writtenColumns, writtenColumnCount));
        }
    }
    finally {
        cellStore.onSetValue = previousOnSetValue;
    }
    return literalCount;
}
export function loadDenseLiteralSheetIntoEmptySheet(workbook, strings, sheetId, content, inspection) {
    const sheet = workbook.getSheetById(sheetId);
    if (!sheet) {
        throw new Error(`Unknown sheet id: ${sheetId}`);
    }
    let potentialCellCount = inspection?.materializedCellCount ?? 0;
    let maxColumnCount = inspection?.maxColumnCount ?? 0;
    if (!inspection) {
        for (let rowIndex = 0; rowIndex < content.length; rowIndex += 1) {
            const row = content[rowIndex];
            maxColumnCount = Math.max(maxColumnCount, row?.length ?? 0);
        }
        potentialCellCount = content.length * maxColumnCount;
    }
    if (potentialCellCount === 0) {
        return 0;
    }
    if (inspection?.allMaterializedCellsAreNumbers === true && potentialCellCount === content.length * maxColumnCount) {
        return loadDenseNumericLiteralSheetIntoEmptySheet(workbook, sheet, sheetId, content, maxColumnCount);
    }
    const cellStore = workbook.cellStore;
    const firstCellIndex = cellStore.allocateDenseRowMajorReserved(sheetId, content.length, maxColumnCount);
    const writtenColumns = materializeDenseWrittenColumns(maxColumnCount);
    const rowIds = workbook.createDenseLogicalAxisIds(sheetId, 'row', 0, content.length);
    const colIds = workbook.createDenseLogicalAxisIds(sheetId, 'column', 0, maxColumnCount);
    const attachedDenseCells = attachDenseFreshLiteralCells(sheet, firstCellIndex, 0, 0, rowIds, colIds);
    const previousOnSetValue = cellStore.onSetValue;
    cellStore.onSetValue = null;
    let literalCount = 0;
    try {
        if (attachedDenseCells) {
            for (let rowIndex = 0; rowIndex < content.length; rowIndex += 1) {
                const row = content[rowIndex];
                const rowBase = firstCellIndex + rowIndex * maxColumnCount;
                for (let colIndex = 0; colIndex < maxColumnCount; colIndex += 1) {
                    writeLiteralCell(cellStore, strings, rowBase + colIndex, row[colIndex]);
                    literalCount += 1;
                }
            }
        }
        else {
            const attachFreshCell = createFreshLiteralCellAttacher(workbook, sheet);
            for (let rowIndex = 0; rowIndex < content.length; rowIndex += 1) {
                const row = content[rowIndex];
                const rowId = rowIds[rowIndex];
                const rowBase = firstCellIndex + rowIndex * maxColumnCount;
                for (let colIndex = 0; colIndex < maxColumnCount; colIndex += 1) {
                    const cellIndex = rowBase + colIndex;
                    attachFreshCell(rowIndex, colIndex, cellIndex, rowId, colIds[colIndex]);
                    writeLiteralCell(cellStore, strings, cellIndex, row[colIndex]);
                    literalCount += 1;
                }
            }
        }
        if (writtenColumns.length > 0) {
            workbook.notifyColumnsWritten(sheetId, writtenColumns);
        }
    }
    finally {
        cellStore.onSetValue = previousOnSetValue;
    }
    return literalCount;
}
function loadDenseNumericLiteralSheetIntoEmptySheet(workbook, sheet, sheetId, content, colCount) {
    const rowCount = content.length;
    const cellCount = rowCount * colCount;
    const cellStore = workbook.cellStore;
    const firstCellIndex = cellStore.allocateDenseRowMajorReserved(sheetId, rowCount, colCount);
    const writtenColumns = materializeDenseWrittenColumns(colCount);
    const rowIds = workbook.createDenseLogicalAxisIds(sheetId, 'row', 0, rowCount);
    const colIds = workbook.createDenseLogicalAxisIds(sheetId, 'column', 0, colCount);
    const attachedDenseCells = attachDenseFreshLiteralCells(sheet, firstCellIndex, 0, 0, rowIds, colIds);
    const previousOnSetValue = cellStore.onSetValue;
    cellStore.onSetValue = null;
    try {
        cellStore.tags.fill(ValueTag.Number, firstCellIndex, firstCellIndex + cellCount);
        cellStore.stringIds.fill(0, firstCellIndex, firstCellIndex + cellCount);
        cellStore.errors.fill(ErrorCode.None, firstCellIndex, firstCellIndex + cellCount);
        cellStore.formulaIds.fill(0, firstCellIndex, firstCellIndex + cellCount);
        cellStore.versions.fill(1, firstCellIndex, firstCellIndex + cellCount);
        cellStore.flags.fill(16 /* CellFlags.Materialized */, firstCellIndex, firstCellIndex + cellCount);
        cellStore.topoRanks.fill(0, firstCellIndex, firstCellIndex + cellCount);
        cellStore.cycleGroupIds.fill(-1, firstCellIndex, firstCellIndex + cellCount);
        if (attachedDenseCells) {
            for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
                const row = content[rowIndex];
                const rowBase = firstCellIndex + rowIndex * colCount;
                for (let colIndex = 0; colIndex < colCount; colIndex += 1) {
                    const raw = row[colIndex];
                    if (typeof raw !== 'number') {
                        throw new Error('Dense numeric literal sheet inspection does not match sheet content');
                    }
                    cellStore.numbers[rowBase + colIndex] = raw;
                }
            }
        }
        else {
            const attachFreshCell = createFreshLiteralCellAttacher(workbook, sheet);
            for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
                const row = content[rowIndex];
                const rowId = rowIds[rowIndex];
                const rowBase = firstCellIndex + rowIndex * colCount;
                for (let colIndex = 0; colIndex < colCount; colIndex += 1) {
                    const cellIndex = rowBase + colIndex;
                    const raw = row[colIndex];
                    if (typeof raw !== 'number') {
                        throw new Error('Dense numeric literal sheet inspection does not match sheet content');
                    }
                    attachFreshCell(rowIndex, colIndex, cellIndex, rowId, colIds[colIndex]);
                    cellStore.numbers[cellIndex] = raw;
                }
            }
        }
        if (writtenColumns.length > 0) {
            workbook.notifyColumnsWritten(sheetId, writtenColumns);
        }
    }
    finally {
        cellStore.onSetValue = previousOnSetValue;
    }
    return cellCount;
}
function materializeDenseWrittenColumns(count) {
    const columns = new Uint32Array(count);
    for (let col = 0; col < count; col += 1) {
        columns[col] = col;
    }
    return columns;
}
function attachDenseFreshLiteralCells(sheet, firstCellIndex, rowStart, colStart, rowIds, colIds) {
    const logicalCandidate = sheet.logical;
    const logical = isFreshLiteralLogicalSheetInternals(logicalCandidate) ? logicalCandidate : undefined;
    const attachDenseFreshVisibleCellIdentities = logical?.setFreshVisibleDenseRowMajorIdentitiesWithAxisIdsDeferred?.bind(logical);
    if (!attachDenseFreshVisibleCellIdentities) {
        return false;
    }
    logical?.deferVisibleCellPageRebuild?.();
    attachDenseFreshVisibleCellIdentities(firstCellIndex, rowIds, colIds);
    sheet.grid.setDenseRowMajor(rowStart, colStart, rowIds.length, colIds.length, firstCellIndex);
    return true;
}
function createFreshLiteralCellAttacher(workbook, sheet) {
    const logicalCandidate = sheet.logical;
    const logical = isFreshLiteralLogicalSheetInternals(logicalCandidate) ? logicalCandidate : undefined;
    const attachFreshVisibleCellIdentity = logical?.setFreshVisibleCellIdentityWithAxisIdsDeferred?.bind(logical);
    if (attachFreshVisibleCellIdentity) {
        logical?.deferVisibleCellPageRebuild?.();
        const setGridCell = sheet.grid.createRowMajorSetter();
        return (row, col, cellIndex, rowId, colId) => {
            attachFreshVisibleCellIdentity(cellIndex, rowId, colId);
            setGridCell(row, col, cellIndex);
        };
    }
    const attachFreshVisibleCell = logical?.setFreshVisibleCellWithAxisIdsDeferred?.bind(logical);
    if (!attachFreshVisibleCell) {
        return (row, col, cellIndex, rowId, colId) => {
            workbook.attachAllocatedCellWithLogicalAxisIds(sheet.id, row, col, cellIndex, rowId, colId);
        };
    }
    const setGridCell = sheet.grid.createRowMajorSetter();
    return (row, col, cellIndex, rowId, colId) => {
        attachFreshVisibleCell(row, col, cellIndex, rowId, colId);
        setGridCell(row, col, cellIndex);
    };
}
function materializeWrittenColumns(writtenColumns, count) {
    const columns = new Uint32Array(count);
    let writeIndex = 0;
    for (let col = 0; col < writtenColumns.length; col += 1) {
        if (writtenColumns[col] !== 0) {
            columns[writeIndex] = col;
            writeIndex += 1;
        }
    }
    return columns;
}
function writeLiteralCell(cellStore, strings, cellIndex, raw) {
    cellStore.flags[cellIndex] = 16 /* CellFlags.Materialized */;
    cellStore.formulaIds[cellIndex] = 0;
    cellStore.errors[cellIndex] = ErrorCode.None;
    cellStore.versions[cellIndex] = 1;
    cellStore.topoRanks[cellIndex] = 0;
    cellStore.cycleGroupIds[cellIndex] = -1;
    if (raw === null || raw === undefined) {
        cellStore.flags[cellIndex] |= 256 /* CellFlags.AuthoredBlank */;
        cellStore.tags[cellIndex] = ValueTag.Empty;
        cellStore.numbers[cellIndex] = 0;
        cellStore.stringIds[cellIndex] = 0;
        return;
    }
    if (typeof raw === 'number') {
        cellStore.tags[cellIndex] = ValueTag.Number;
        cellStore.numbers[cellIndex] = raw;
        cellStore.stringIds[cellIndex] = 0;
        return;
    }
    if (typeof raw === 'boolean') {
        cellStore.tags[cellIndex] = ValueTag.Boolean;
        cellStore.numbers[cellIndex] = raw ? 1 : 0;
        cellStore.stringIds[cellIndex] = 0;
        return;
    }
    cellStore.tags[cellIndex] = ValueTag.String;
    cellStore.numbers[cellIndex] = 0;
    cellStore.stringIds[cellIndex] = strings.intern(raw);
}
//# sourceMappingURL=literal-sheet-loader.js.map