export class AxisResidentCellIndex {
    rebuildSource;
    sheetIds = [];
    rowIds = [];
    colIds = [];
    byRow = new Map();
    byColumn = new Map();
    denseRowMajorBlocks = [];
    denseDeletedCellIndices = new Set();
    primaryIndexDirty = false;
    secondaryIndexesDirty = false;
    constructor(rebuildSource) {
        this.rebuildSource = rebuildSource;
    }
    set(cellIndex, identity) {
        this.ensurePrimaryIndexUnlessDense();
        const existingRowId = this.rowIds[cellIndex];
        const existingColId = this.colIds[cellIndex];
        if (existingRowId !== undefined && existingColId !== undefined && !this.secondaryIndexesDirty) {
            deleteFromSetMap(this.byRow, existingRowId, cellIndex);
            deleteFromSetMap(this.byColumn, existingColId, cellIndex);
        }
        this.setPrimaryParts(cellIndex, identity.sheetId, identity.rowId, identity.colId);
        if (!this.secondaryIndexesDirty) {
            addToSetMap(this.byRow, identity.rowId, cellIndex);
            addToSetMap(this.byColumn, identity.colId, cellIndex);
        }
    }
    add(cellIndex, identity) {
        this.ensurePrimaryIndexUnlessDense();
        const existingRowId = this.rowIds[cellIndex];
        const existingColId = this.colIds[cellIndex];
        if (existingRowId !== undefined && existingColId !== undefined && !this.secondaryIndexesDirty) {
            deleteFromSetMap(this.byRow, existingRowId, cellIndex);
            deleteFromSetMap(this.byColumn, existingColId, cellIndex);
        }
        this.setPrimaryParts(cellIndex, identity.sheetId, identity.rowId, identity.colId);
        if (!this.secondaryIndexesDirty) {
            addToSetMap(this.byRow, identity.rowId, cellIndex);
            addToSetMap(this.byColumn, identity.colId, cellIndex);
        }
    }
    addDeferred(cellIndex, identity) {
        this.ensurePrimaryIndexUnlessDense();
        this.setPrimaryParts(cellIndex, identity.sheetId, identity.rowId, identity.colId);
        this.secondaryIndexesDirty = true;
    }
    addDeferredParts(cellIndex, rowId, colId) {
        this.ensurePrimaryIndexUnlessDense();
        this.setPrimaryParts(cellIndex, undefined, rowId, colId);
        this.secondaryIndexesDirty = true;
    }
    addDenseRowMajorDeferredParts(firstCellIndex, rowIds, colIds) {
        const colCount = colIds.length;
        const cellCount = rowIds.length * colCount;
        if (cellCount === 0) {
            return;
        }
        this.primaryIndexDirty = false;
        this.denseRowMajorBlocks.push({
            firstCellIndex,
            cellCount,
            colCount,
            rowIds,
            colIds,
        });
        this.secondaryIndexesDirty = true;
    }
    deferRebuild() {
        this.sheetIds.length = 0;
        this.rowIds.length = 0;
        this.colIds.length = 0;
        this.byRow.clear();
        this.byColumn.clear();
        this.denseRowMajorBlocks.length = 0;
        this.denseDeletedCellIndices.clear();
        this.primaryIndexDirty = true;
        this.secondaryIndexesDirty = false;
    }
    get(cellIndex) {
        this.ensurePrimaryIndexUnlessDense();
        const rowId = this.rowIds[cellIndex];
        const colId = this.colIds[cellIndex];
        if (rowId === undefined || colId === undefined) {
            return this.getDenseRowMajorIdentity(cellIndex);
        }
        const sheetId = this.sheetIds[cellIndex];
        return sheetId === undefined ? { rowId, colId } : { sheetId, rowId, colId };
    }
    delete(cellIndex) {
        this.ensurePrimaryIndexUnlessDense();
        const existingRowId = this.rowIds[cellIndex];
        const existingColId = this.colIds[cellIndex];
        const existingDenseIdentity = this.getDenseRowMajorIdentity(cellIndex);
        if (existingRowId === undefined || existingColId === undefined) {
            if (!existingDenseIdentity) {
                return false;
            }
            this.denseDeletedCellIndices.add(cellIndex);
            return true;
        }
        this.sheetIds[cellIndex] = undefined;
        this.rowIds[cellIndex] = undefined;
        this.colIds[cellIndex] = undefined;
        if (this.secondaryIndexesDirty) {
            return true;
        }
        deleteFromSetMap(this.byRow, existingRowId, cellIndex);
        deleteFromSetMap(this.byColumn, existingColId, cellIndex);
        return true;
    }
    clear() {
        this.sheetIds.length = 0;
        this.rowIds.length = 0;
        this.colIds.length = 0;
        this.byRow.clear();
        this.byColumn.clear();
        this.denseRowMajorBlocks.length = 0;
        this.denseDeletedCellIndices.clear();
        this.primaryIndexDirty = false;
        this.secondaryIndexesDirty = false;
    }
    cellsInRow(rowId) {
        this.ensureSecondaryIndexes();
        const cells = new Set(this.byRow.get(rowId));
        this.addDenseCellsInRow(rowId, cells);
        return sortedCells(cells);
    }
    cellsInColumn(colId) {
        this.ensureSecondaryIndexes();
        const cells = new Set(this.byColumn.get(colId));
        this.addDenseCellsInColumn(colId, cells);
        return sortedCells(cells);
    }
    forEachCellInRow(rowId, callback) {
        this.ensureSecondaryIndexes();
        this.byRow.get(rowId)?.forEach(callback);
        this.forEachDenseCellInRow(rowId, callback);
    }
    forEachCellInColumn(colId, callback) {
        this.ensureSecondaryIndexes();
        this.byColumn.get(colId)?.forEach(callback);
        this.forEachDenseCellInColumn(colId, callback);
    }
    cellsInRows(rowIds) {
        return sortedUniqueCells(rowIds.flatMap((rowId) => this.cellsInRow(rowId)));
    }
    cellsInColumns(colIds) {
        return sortedUniqueCells(colIds.flatMap((colId) => this.cellsInColumn(colId)));
    }
    cellsInRowsUnordered(rowIds) {
        this.ensureSecondaryIndexes();
        return uniqueCells(rowIds.flatMap((rowId) => this.unorderedCellsInRow(rowId)));
    }
    cellsInColumnsUnordered(colIds) {
        this.ensureSecondaryIndexes();
        return uniqueCells(colIds.flatMap((colId) => this.unorderedCellsInColumn(colId)));
    }
    ensurePrimaryIndexUnlessDense() {
        if (!this.primaryIndexDirty || this.denseRowMajorBlocks.length > 0) {
            return;
        }
        this.ensurePrimaryIndex();
    }
    ensurePrimaryIndex() {
        if (!this.primaryIndexDirty) {
            return;
        }
        this.sheetIds.length = 0;
        this.rowIds.length = 0;
        this.colIds.length = 0;
        this.rebuildSource?.((cellIndex, identity) => {
            this.setPrimaryParts(cellIndex, identity.sheetId, identity.rowId, identity.colId);
        });
        this.primaryIndexDirty = false;
        this.secondaryIndexesDirty = true;
    }
    ensureSecondaryIndexes() {
        this.ensurePrimaryIndexUnlessDense();
        if (!this.secondaryIndexesDirty) {
            return;
        }
        this.byRow.clear();
        this.byColumn.clear();
        for (let cellIndex = 0; cellIndex < this.rowIds.length; cellIndex += 1) {
            const rowId = this.rowIds[cellIndex];
            const colId = this.colIds[cellIndex];
            if (rowId === undefined || colId === undefined) {
                continue;
            }
            addToSetMap(this.byRow, rowId, cellIndex);
            addToSetMap(this.byColumn, colId, cellIndex);
        }
        this.secondaryIndexesDirty = false;
    }
    setPrimaryParts(cellIndex, sheetId, rowId, colId) {
        this.denseDeletedCellIndices.delete(cellIndex);
        this.sheetIds[cellIndex] = sheetId;
        this.rowIds[cellIndex] = rowId;
        this.colIds[cellIndex] = colId;
    }
    unorderedCellsInRow(rowId) {
        const cells = [...(this.byRow.get(rowId) ?? [])];
        this.forEachDenseCellInRow(rowId, (cellIndex) => {
            cells.push(cellIndex);
        });
        return cells;
    }
    unorderedCellsInColumn(colId) {
        const cells = [...(this.byColumn.get(colId) ?? [])];
        this.forEachDenseCellInColumn(colId, (cellIndex) => {
            cells.push(cellIndex);
        });
        return cells;
    }
    addDenseCellsInRow(rowId, cells) {
        this.forEachDenseCellInRow(rowId, (cellIndex) => {
            cells.add(cellIndex);
        });
    }
    addDenseCellsInColumn(colId, cells) {
        this.forEachDenseCellInColumn(colId, (cellIndex) => {
            cells.add(cellIndex);
        });
    }
    forEachDenseCellInRow(rowId, callback) {
        for (let blockIndex = this.denseRowMajorBlocks.length - 1; blockIndex >= 0; blockIndex -= 1) {
            const block = this.denseRowMajorBlocks[blockIndex];
            block.rowOffsets ??= indexedAxisIds(block.rowIds);
            const rowOffset = block.rowOffsets.get(rowId);
            if (rowOffset === undefined) {
                continue;
            }
            const rowCellStart = block.firstCellIndex + rowOffset * block.colCount;
            for (let colOffset = 0; colOffset < block.colCount; colOffset += 1) {
                const cellIndex = rowCellStart + colOffset;
                if (this.isDenseCellShadowed(cellIndex)) {
                    continue;
                }
                callback(cellIndex);
            }
        }
    }
    forEachDenseCellInColumn(colId, callback) {
        for (let blockIndex = this.denseRowMajorBlocks.length - 1; blockIndex >= 0; blockIndex -= 1) {
            const block = this.denseRowMajorBlocks[blockIndex];
            block.colOffsets ??= indexedAxisIds(block.colIds);
            const colOffset = block.colOffsets.get(colId);
            if (colOffset === undefined) {
                continue;
            }
            for (let rowOffset = 0; rowOffset < block.rowIds.length; rowOffset += 1) {
                const cellIndex = block.firstCellIndex + rowOffset * block.colCount + colOffset;
                if (this.isDenseCellShadowed(cellIndex)) {
                    continue;
                }
                callback(cellIndex);
            }
        }
    }
    getDenseRowMajorIdentity(cellIndex) {
        if (this.denseDeletedCellIndices.has(cellIndex)) {
            return undefined;
        }
        for (let blockIndex = this.denseRowMajorBlocks.length - 1; blockIndex >= 0; blockIndex -= 1) {
            const block = this.denseRowMajorBlocks[blockIndex];
            const offset = cellIndex - block.firstCellIndex;
            if (offset < 0 || offset >= block.cellCount) {
                continue;
            }
            const rowOffset = Math.floor(offset / block.colCount);
            const colOffset = offset - rowOffset * block.colCount;
            return {
                rowId: block.rowIds[rowOffset],
                colId: block.colIds[colOffset],
            };
        }
        return undefined;
    }
    isDenseCellShadowed(cellIndex) {
        return this.denseDeletedCellIndices.has(cellIndex) || (this.rowIds[cellIndex] !== undefined && this.colIds[cellIndex] !== undefined);
    }
}
function indexedAxisIds(axisIds) {
    const offsets = new Map();
    for (let index = 0; index < axisIds.length; index += 1) {
        offsets.set(axisIds[index], index);
    }
    return offsets;
}
function addToSetMap(map, key, value) {
    let values = map.get(key);
    if (!values) {
        values = new Set();
        map.set(key, values);
    }
    values.add(value);
}
function deleteFromSetMap(map, key, value) {
    const values = map.get(key);
    if (!values) {
        return;
    }
    values.delete(value);
    if (values.size === 0) {
        map.delete(key);
    }
}
function sortedCells(values) {
    return values ? [...values].toSorted((left, right) => left - right) : [];
}
function sortedUniqueCells(values) {
    return [...new Set(values)].toSorted((left, right) => left - right);
}
function uniqueCells(values) {
    return [...new Set(values)];
}
//# sourceMappingURL=axis-resident-cell-index.js.map