import { MAX_COLS, MAX_ROWS, MAX_WASM_RANGE_CELLS } from '@bilig/protocol';
import { makeCellEntity, makeRangeEntity } from './entity-ids.js';
import { EdgeArena } from './edge-arena.js';
import { addEngineCounter } from './perf/engine-counters.js';
export class RangeRegistry {
    counters;
    descriptors = [];
    byKey = new Map();
    dynamicBySheet = new Map();
    members = new EdgeArena();
    memberSlices = [];
    formulaMembers = new EdgeArena();
    formulaMemberSlices = [];
    dependencySources = new EdgeArena();
    dependencySourceSlices = [];
    constructor(counters) {
        this.counters = counters;
    }
    get size() {
        return this.descriptors.length;
    }
    reset() {
        this.descriptors.length = 0;
        this.byKey.clear();
        this.dynamicBySheet.clear();
        this.members.reset();
        this.memberSlices.length = 0;
        this.formulaMembers.reset();
        this.formulaMemberSlices.length = 0;
        this.dependencySources.reset();
        this.dependencySourceSlices.length = 0;
    }
    intern(sheetId, range, materializer) {
        const descriptorKey = keyForRange(sheetId, range);
        const existingIndex = this.byKey.get(descriptorKey);
        if (existingIndex !== undefined) {
            const existing = this.descriptors[existingIndex];
            existing.refCount += 1;
            return {
                rangeIndex: existingIndex,
                cellRange: toCellRange(range),
                materialized: false,
            };
        }
        const cellRange = toCellRange(range);
        const dynamic = range.kind !== 'cells' || cellRangeMemberCount(cellRange) > MAX_WASM_RANGE_CELLS;
        const descriptor = {
            index: this.descriptors.length,
            sheetId,
            kind: range.kind,
            row1: cellRange.start.row,
            col1: cellRange.start.col,
            row2: cellRange.end.row,
            col2: cellRange.end.col,
            membersOffset: 0,
            membersLength: 0,
            formulaMembersOffset: 0,
            formulaMembersLength: 0,
            dependencySourcesOffset: 0,
            dependencySourcesLength: 0,
            refCount: 1,
            dynamic,
            parentRangeIndex: undefined,
        };
        const sourceReuse = range.kind === 'cells' && !dynamic ? resolveCellRangeDependencyReuse(this.descriptors, this.byKey, sheetId, cellRange) : undefined;
        const isFormulaCell = materializer.isFormulaCell === undefined ? undefined : (cellIndex) => materializer.isFormulaCell(cellIndex);
        const memberIndices = range.kind === 'cells' && !dynamic
            ? materializeBoundedMembers(sheetId, cellRange, materializer, this, sourceReuse)
            : materializeDynamicMembers(sheetId, cellRange, range.kind, materializer);
        const dependencySourceEntities = range.kind === 'cells' && !dynamic
            ? materializeCellRangeDependencySources(sourceReuse, memberIndices)
            : materializeDynamicDependencySources(memberIndices);
        const formulaMemberIndices = materializeFormulaMembers(memberIndices, isFormulaCell, sourceReuse ? this.getFormulaMembersView(sourceReuse.parentRangeIndex) : undefined, sourceReuse ? this.getMembersView(sourceReuse.parentRangeIndex).length : 0);
        const memberSlice = this.members.replace(this.members.empty(), memberIndices);
        const formulaMemberSlice = this.formulaMembers.replace(this.formulaMembers.empty(), formulaMemberIndices);
        const dependencySourceSlice = this.dependencySources.replace(this.dependencySources.empty(), dependencySourceEntities);
        if (this.counters) {
            addEngineCounter(this.counters, 'rangesMaterialized');
            addEngineCounter(this.counters, 'rangeMembersExpanded', memberIndices.length);
        }
        this.memberSlices[descriptor.index] = memberSlice;
        this.formulaMemberSlices[descriptor.index] = formulaMemberSlice;
        this.dependencySourceSlices[descriptor.index] = dependencySourceSlice;
        syncDescriptorMembers(descriptor, memberSlice);
        syncDescriptorFormulaMembers(descriptor, formulaMemberSlice);
        syncDescriptorDependencySources(descriptor, dependencySourceSlice);
        if (sourceReuse) {
            descriptor.parentRangeIndex = sourceReuse.parentRangeIndex;
            this.descriptors[sourceReuse.parentRangeIndex].refCount += 1;
        }
        this.descriptors.push(descriptor);
        this.byKey.set(descriptorKey, descriptor.index);
        if (dynamic) {
            const entries = this.dynamicBySheet.get(sheetId) ?? [];
            entries.push({ sheetId, rangeIndex: descriptor.index });
            this.dynamicBySheet.set(sheetId, entries);
        }
        return {
            rangeIndex: descriptor.index,
            cellRange,
            materialized: true,
        };
    }
    release(rangeIndex) {
        const descriptor = this.descriptors[rangeIndex];
        if (!descriptor) {
            return { removed: false, members: new Uint32Array() };
        }
        const memberSlice = this.memberSlices[rangeIndex] ?? this.members.empty();
        descriptor.refCount -= 1;
        if (descriptor.refCount > 0) {
            return { removed: false, members: this.members.read(memberSlice) };
        }
        this.byKey.delete(keyForDescriptor(descriptor));
        const members = this.members.read(memberSlice);
        this.members.free(memberSlice);
        this.memberSlices[rangeIndex] = this.members.empty();
        const formulaMemberSlice = this.formulaMemberSlices[rangeIndex] ?? this.formulaMembers.empty();
        this.formulaMembers.free(formulaMemberSlice);
        this.formulaMemberSlices[rangeIndex] = this.formulaMembers.empty();
        const dependencySourceSlice = this.dependencySourceSlices[rangeIndex] ?? this.dependencySources.empty();
        this.dependencySources.free(dependencySourceSlice);
        this.dependencySourceSlices[rangeIndex] = this.dependencySources.empty();
        syncDescriptorMembers(descriptor, this.memberSlices[rangeIndex]);
        syncDescriptorFormulaMembers(descriptor, this.formulaMemberSlices[rangeIndex]);
        syncDescriptorDependencySources(descriptor, this.dependencySourceSlices[rangeIndex]);
        if (descriptor.dynamic) {
            const dynamic = this.dynamicBySheet.get(descriptor.sheetId);
            if (dynamic) {
                this.dynamicBySheet.set(descriptor.sheetId, dynamic.filter((entry) => entry.rangeIndex !== rangeIndex));
            }
        }
        const parentRangeIndex = descriptor.parentRangeIndex;
        descriptor.parentRangeIndex = undefined;
        descriptor.refCount = 0;
        if (parentRangeIndex !== undefined) {
            this.release(parentRangeIndex);
        }
        return { removed: true, members };
    }
    getDescriptor(rangeIndex) {
        const descriptor = this.descriptors[rangeIndex];
        if (!descriptor) {
            throw new Error(`Unknown range index: ${rangeIndex}`);
        }
        return descriptor;
    }
    getMembers(rangeIndex) {
        return this.members.read(this.memberSlices[rangeIndex] ?? this.members.empty());
    }
    getDependencySourceEntities(rangeIndex) {
        return this.dependencySources.read(this.dependencySourceSlices[rangeIndex] ?? this.dependencySources.empty());
    }
    getFormulaMembers(rangeIndex) {
        return this.formulaMembers.read(this.formulaMemberSlices[rangeIndex] ?? this.formulaMembers.empty());
    }
    getMembersView(rangeIndex) {
        return this.members.readView(this.memberSlices[rangeIndex] ?? this.members.empty());
    }
    getFormulaMembersView(rangeIndex) {
        return this.formulaMembers.readView(this.formulaMemberSlices[rangeIndex] ?? this.formulaMembers.empty());
    }
    getMemberPoolView() {
        return this.members.view();
    }
    addDynamicMember(sheetId, row, col, cellIndex, includeAsFormulaMember = false) {
        const entries = this.dynamicBySheet.get(sheetId);
        if (!entries || entries.length === 0) {
            return [];
        }
        const matched = [];
        for (let index = 0; index < entries.length; index += 1) {
            const rangeIndex = entries[index].rangeIndex;
            const descriptor = this.descriptors[rangeIndex];
            if (!matchesDynamicRange(descriptor, row, col)) {
                continue;
            }
            const currentSlice = this.memberSlices[rangeIndex] ?? this.members.empty();
            const nextMembers = this.members.appendUnique(currentSlice, cellIndex);
            if (nextMembers.ptr !== currentSlice.ptr || nextMembers.len !== currentSlice.len) {
                this.memberSlices[rangeIndex] = nextMembers;
                syncDescriptorMembers(descriptor, nextMembers);
                if (includeAsFormulaMember) {
                    const currentFormulaSlice = this.formulaMemberSlices[rangeIndex] ?? this.formulaMembers.empty();
                    const nextFormulaMembers = this.formulaMembers.appendUnique(currentFormulaSlice, cellIndex);
                    this.formulaMemberSlices[rangeIndex] = nextFormulaMembers;
                    syncDescriptorFormulaMembers(descriptor, nextFormulaMembers);
                }
                matched.push(rangeIndex);
            }
        }
        return matched;
    }
    expandToCells(rangeIndex) {
        const members = this.getMembersView(rangeIndex);
        if (this.counters) {
            addEngineCounter(this.counters, 'rangeMembersExpanded', members.length);
        }
        return members;
    }
    refresh(rangeIndex, materializer) {
        const descriptor = this.getDescriptor(rangeIndex);
        const oldDependencySources = this.getDependencySourceEntities(rangeIndex);
        const previousParentRangeIndex = descriptor.parentRangeIndex;
        let memberIndices;
        let formulaMemberIndices;
        let dependencySourceEntities;
        let nextParentRangeIndex;
        if (descriptor.kind === 'cells' && !descriptor.dynamic) {
            const range = descriptorToCellRangeAddress(descriptor);
            const sourceReuse = resolveCellRangeDependencyReuse(this.descriptors, this.byKey, descriptor.sheetId, range);
            nextParentRangeIndex = sourceReuse?.parentRangeIndex;
            const isFormulaCell = materializer.isFormulaCell === undefined ? undefined : (cellIndex) => materializer.isFormulaCell(cellIndex);
            memberIndices = materializeBoundedMembers(descriptor.sheetId, range, materializer, this, sourceReuse);
            dependencySourceEntities = materializeCellRangeDependencySources(sourceReuse, memberIndices);
            formulaMemberIndices = materializeFormulaMembers(memberIndices, isFormulaCell, sourceReuse ? this.getFormulaMembersView(sourceReuse.parentRangeIndex) : undefined, sourceReuse ? this.getMembersView(sourceReuse.parentRangeIndex).length : 0);
        }
        else {
            const range = descriptorToCellRangeAddress(descriptor);
            memberIndices = materializeDynamicMembers(descriptor.sheetId, range, descriptor.kind, materializer);
            dependencySourceEntities = materializeDynamicDependencySources(memberIndices);
            const isFormulaCell = materializer.isFormulaCell === undefined ? undefined : (cellIndex) => materializer.isFormulaCell(cellIndex);
            formulaMemberIndices = materializeFormulaMembers(memberIndices, isFormulaCell);
        }
        const memberSlice = this.members.replace(this.memberSlices[rangeIndex] ?? this.members.empty(), memberIndices);
        const formulaMemberSlice = this.formulaMembers.replace(this.formulaMemberSlices[rangeIndex] ?? this.formulaMembers.empty(), formulaMemberIndices);
        const dependencySourceSlice = this.dependencySources.replace(this.dependencySourceSlices[rangeIndex] ?? this.dependencySources.empty(), dependencySourceEntities);
        if (this.counters) {
            addEngineCounter(this.counters, 'rangesMaterialized');
            addEngineCounter(this.counters, 'rangeMembersExpanded', memberIndices.length);
        }
        this.memberSlices[rangeIndex] = memberSlice;
        this.formulaMemberSlices[rangeIndex] = formulaMemberSlice;
        this.dependencySourceSlices[rangeIndex] = dependencySourceSlice;
        syncDescriptorMembers(descriptor, memberSlice);
        syncDescriptorFormulaMembers(descriptor, formulaMemberSlice);
        syncDescriptorDependencySources(descriptor, dependencySourceSlice);
        if (previousParentRangeIndex !== nextParentRangeIndex) {
            if (previousParentRangeIndex !== undefined) {
                this.release(previousParentRangeIndex);
            }
            if (nextParentRangeIndex !== undefined) {
                this.descriptors[nextParentRangeIndex].refCount += 1;
            }
            descriptor.parentRangeIndex = nextParentRangeIndex;
        }
        return {
            oldDependencySources,
            newDependencySources: this.getDependencySourceEntities(rangeIndex),
        };
    }
    applyStructuralTransaction(transaction, rangeIndices, materializer) {
        const touched = [];
        const seen = new Set();
        rangeIndices.forEach((rangeIndex) => {
            if (seen.has(rangeIndex)) {
                return;
            }
            seen.add(rangeIndex);
            const descriptor = this.descriptors[rangeIndex];
            if (!descriptor || descriptor.refCount <= 0 || descriptor.sheetId !== transaction.sheetId) {
                return;
            }
            const nextBounds = remapDescriptorBounds(descriptor, transaction.transform);
            if (!nextBounds) {
                return;
            }
            const keyBefore = keyForDescriptor(descriptor);
            this.byKey.delete(keyBefore);
            descriptor.row1 = nextBounds.row1;
            descriptor.row2 = nextBounds.row2;
            descriptor.col1 = nextBounds.col1;
            descriptor.col2 = nextBounds.col2;
            this.byKey.set(keyForDescriptor(descriptor), rangeIndex);
            touched.push({
                rangeIndex,
                ...this.refresh(rangeIndex, materializer),
            });
        });
        if (this.counters && touched.length > 0) {
            addEngineCounter(this.counters, 'structuralRangeRetargets', touched.length);
        }
        return touched;
    }
}
function remapDescriptorBounds(descriptor, transform) {
    const rowInterval = transform.axis === 'row'
        ? mapDescriptorInterval(descriptor.row1, descriptor.row2, transform)
        : { start: descriptor.row1, end: descriptor.row2 };
    const colInterval = transform.axis === 'column'
        ? mapDescriptorInterval(descriptor.col1, descriptor.col2, transform)
        : { start: descriptor.col1, end: descriptor.col2 };
    if (!rowInterval || !colInterval) {
        return undefined;
    }
    return {
        row1: rowInterval.start,
        row2: rowInterval.end,
        col1: colInterval.start,
        col2: colInterval.end,
    };
}
function mapDescriptorInterval(start, end, transform) {
    switch (transform.kind) {
        case 'insert': {
            if (transform.start <= start) {
                return { start: start + transform.count, end: end + transform.count };
            }
            if (transform.start <= end) {
                return { start, end: end + transform.count };
            }
            return { start, end };
        }
        case 'delete': {
            const deleteEnd = transform.start + transform.count - 1;
            if (deleteEnd < start) {
                return { start: start - transform.count, end: end - transform.count };
            }
            if (transform.start > end) {
                return { start, end };
            }
            const survivingStart = start < transform.start ? start : deleteEnd + 1;
            const survivingEnd = end > deleteEnd ? end : transform.start - 1;
            if (survivingStart > survivingEnd) {
                return undefined;
            }
            return {
                start: survivingStart < transform.start ? survivingStart : survivingStart - transform.count,
                end: survivingEnd - (survivingEnd > deleteEnd ? transform.count : 0),
            };
        }
        case 'move': {
            const segments = transform.target < transform.start
                ? [
                    { start: 0, end: transform.target - 1, delta: 0 },
                    { start: transform.target, end: transform.start - 1, delta: transform.count },
                    {
                        start: transform.start,
                        end: transform.start + transform.count - 1,
                        delta: transform.target - transform.start,
                    },
                    { start: transform.start + transform.count, end: Number.MAX_SAFE_INTEGER, delta: 0 },
                ]
                : [
                    { start: 0, end: transform.start - 1, delta: 0 },
                    {
                        start: transform.start,
                        end: transform.start + transform.count - 1,
                        delta: transform.target - transform.start,
                    },
                    {
                        start: transform.start + transform.count,
                        end: transform.target + transform.count - 1,
                        delta: -transform.count,
                    },
                    { start: transform.target + transform.count, end: Number.MAX_SAFE_INTEGER, delta: 0 },
                ];
            let nextStart;
            let nextEnd;
            segments.forEach((segment) => {
                const overlapStart = Math.max(start, segment.start);
                const overlapEnd = Math.min(end, segment.end);
                if (overlapStart > overlapEnd) {
                    return;
                }
                const mappedStart = overlapStart + segment.delta;
                const mappedEnd = overlapEnd + segment.delta;
                nextStart = nextStart === undefined ? mappedStart : Math.min(nextStart, mappedStart);
                nextEnd = nextEnd === undefined ? mappedEnd : Math.max(nextEnd, mappedEnd);
            });
            if (nextStart === undefined || nextEnd === undefined) {
                return undefined;
            }
            return { start: nextStart, end: nextEnd };
        }
        default: {
            const exhaustive = transform;
            return exhaustive;
        }
    }
}
function materializeBoundedMembers(sheetId, range, materializer, registry, reuse) {
    if (reuse) {
        const tailCells = materializeBoundedTailCells(sheetId, reuse, materializer);
        const parentMembers = registry.getMembersView(reuse.parentRangeIndex);
        const members = new Uint32Array(parentMembers.length + tailCells.length);
        members.set(parentMembers, 0);
        members.set(tailCells, parentMembers.length);
        return members;
    }
    const rowCount = range.end.row - range.start.row + 1;
    const colCount = range.end.col - range.start.col + 1;
    const memberCount = rowCount * colCount;
    if (memberCount > MAX_WASM_RANGE_CELLS) {
        throw new Error(`Bounded range exceeds fast-path cap: ${memberCount}`);
    }
    const members = new Uint32Array(memberCount);
    let cursor = 0;
    for (let row = range.start.row; row <= range.end.row; row += 1) {
        for (let col = range.start.col; col <= range.end.col; col += 1) {
            members[cursor] = materializer.ensureCell(sheetId, row, col);
            cursor += 1;
        }
    }
    return members;
}
function materializeDynamicMembers(sheetId, range, kind, materializer) {
    let matchCount = 0;
    materializer.forEachSheetCell(sheetId, (_cellIndex, row, col) => {
        if (kind === 'cells') {
            if (row >= range.start.row && row <= range.end.row && col >= range.start.col && col <= range.end.col) {
                matchCount += 1;
            }
            return;
        }
        if (kind === 'rows') {
            if (row >= range.start.row && row <= range.end.row) {
                matchCount += 1;
            }
            return;
        }
        if (col >= range.start.col && col <= range.end.col) {
            matchCount += 1;
        }
    });
    const matches = new Uint32Array(matchCount);
    let cursor = 0;
    materializer.forEachSheetCell(sheetId, (cellIndex, row, col) => {
        if (kind === 'cells') {
            if (row >= range.start.row && row <= range.end.row && col >= range.start.col && col <= range.end.col) {
                matches[cursor] = cellIndex;
                cursor += 1;
            }
            return;
        }
        if (kind === 'rows') {
            if (row >= range.start.row && row <= range.end.row) {
                matches[cursor] = cellIndex;
                cursor += 1;
            }
            return;
        }
        if (col >= range.start.col && col <= range.end.col) {
            matches[cursor] = cellIndex;
            cursor += 1;
        }
    });
    return matches.toSorted();
}
function materializeDynamicDependencySources(memberIndices) {
    const dependencySources = new Uint32Array(memberIndices.length);
    for (let index = 0; index < memberIndices.length; index += 1) {
        dependencySources[index] = makeCellEntity(memberIndices[index]);
    }
    return dependencySources;
}
function materializeFormulaMembers(memberIndices, isFormulaCell, parentFormulaMembers, parentMemberCount = 0) {
    if (!isFormulaCell) {
        return parentFormulaMembers ? Uint32Array.from(parentFormulaMembers) : new Uint32Array();
    }
    let appendedFormulaCount = 0;
    const startIndex = parentMemberCount;
    for (let index = startIndex; index < memberIndices.length; index += 1) {
        if (isFormulaCell(memberIndices[index])) {
            appendedFormulaCount += 1;
        }
    }
    const parentCount = parentFormulaMembers?.length ?? 0;
    const formulaMembers = new Uint32Array(parentCount + appendedFormulaCount);
    if (parentFormulaMembers && parentCount > 0) {
        formulaMembers.set(parentFormulaMembers, 0);
    }
    let cursor = parentCount;
    for (let index = startIndex; index < memberIndices.length; index += 1) {
        const cellIndex = memberIndices[index];
        if (!isFormulaCell(cellIndex)) {
            continue;
        }
        formulaMembers[cursor] = cellIndex;
        cursor += 1;
    }
    return formulaMembers;
}
function materializeCellRangeDependencySources(reuse, memberIndices) {
    if (reuse) {
        const tailMemberCount = (reuse.tailEndRow - reuse.tailStartRow + 1) * (reuse.tailEndCol - reuse.tailStartCol + 1);
        const dependencySources = new Uint32Array(tailMemberCount + 1);
        dependencySources[0] = makeRangeEntity(reuse.parentRangeIndex);
        const tailStartIndex = memberIndices.length - tailMemberCount;
        for (let index = 0; index < tailMemberCount; index += 1) {
            dependencySources[index + 1] = makeCellEntity(memberIndices[tailStartIndex + index]);
        }
        return dependencySources;
    }
    return materializeDynamicDependencySources(memberIndices);
}
function resolveCellRangeDependencyReuse(descriptors, byKey, sheetId, range) {
    const width = range.end.col - range.start.col + 1;
    const height = range.end.row - range.start.row + 1;
    if (width === 1 && height > 1) {
        const prefixKey = keyForRange(sheetId, {
            kind: 'cells',
            start: range.start,
            end: { ...range.end, row: range.end.row - 1 },
        });
        const prefixRangeIndex = byKey.get(prefixKey);
        if (prefixRangeIndex !== undefined && descriptors[prefixRangeIndex]?.refCount) {
            return {
                parentRangeIndex: prefixRangeIndex,
                tailStartRow: range.end.row,
                tailEndRow: range.end.row,
                tailStartCol: range.start.col,
                tailEndCol: range.end.col,
            };
        }
    }
    if (height === 1 && width > 1) {
        const prefixKey = keyForRange(sheetId, {
            kind: 'cells',
            start: range.start,
            end: { ...range.end, col: range.end.col - 1 },
        });
        const prefixRangeIndex = byKey.get(prefixKey);
        if (prefixRangeIndex !== undefined && descriptors[prefixRangeIndex]?.refCount) {
            return {
                parentRangeIndex: prefixRangeIndex,
                tailStartRow: range.start.row,
                tailEndRow: range.end.row,
                tailStartCol: range.end.col,
                tailEndCol: range.end.col,
            };
        }
    }
    return undefined;
}
function materializeBoundedTailCells(sheetId, reuse, materializer) {
    const tailRowCount = reuse.tailEndRow - reuse.tailStartRow + 1;
    const tailColCount = reuse.tailEndCol - reuse.tailStartCol + 1;
    const tailCells = new Uint32Array(tailRowCount * tailColCount);
    let cursor = 0;
    for (let row = reuse.tailStartRow; row <= reuse.tailEndRow; row += 1) {
        for (let col = reuse.tailStartCol; col <= reuse.tailEndCol; col += 1) {
            tailCells[cursor] = materializer.ensureCell(sheetId, row, col);
            cursor += 1;
        }
    }
    return tailCells;
}
function matchesDynamicRange(descriptor, row, col) {
    if (descriptor.kind === 'cells') {
        return row >= descriptor.row1 && row <= descriptor.row2 && col >= descriptor.col1 && col <= descriptor.col2;
    }
    if (descriptor.kind === 'rows') {
        return row >= descriptor.row1 && row <= descriptor.row2;
    }
    if (descriptor.kind === 'cols') {
        return col >= descriptor.col1 && col <= descriptor.col2;
    }
    return false;
}
function cellRangeMemberCount(range) {
    return (range.end.row - range.start.row + 1) * (range.end.col - range.start.col + 1);
}
function syncDescriptorMembers(descriptor, slice) {
    descriptor.membersOffset = slice.ptr < 0 ? 0 : slice.ptr;
    descriptor.membersLength = slice.len;
}
function syncDescriptorFormulaMembers(descriptor, slice) {
    descriptor.formulaMembersOffset = slice.ptr < 0 ? 0 : slice.ptr;
    descriptor.formulaMembersLength = slice.len;
}
function syncDescriptorDependencySources(descriptor, slice) {
    descriptor.dependencySourcesOffset = slice.ptr < 0 ? 0 : slice.ptr;
    descriptor.dependencySourcesLength = slice.len;
}
function toCellRange(range) {
    if (range.kind === 'cells') {
        return range;
    }
    if (range.kind === 'rows') {
        const cellRange = {
            kind: 'cells',
            start: toCellLikeAddress(range.sheetName, range.start.row, 0),
            end: toCellLikeAddress(range.sheetName, range.end.row, MAX_COLS - 1),
        };
        if (range.sheetName !== undefined) {
            cellRange.sheetName = range.sheetName;
        }
        return cellRange;
    }
    const cellRange = {
        kind: 'cells',
        start: toCellLikeAddress(range.sheetName, 0, range.start.col),
        end: toCellLikeAddress(range.sheetName, MAX_ROWS - 1, range.end.col),
    };
    if (range.sheetName !== undefined) {
        cellRange.sheetName = range.sheetName;
    }
    return cellRange;
}
function descriptorToCellRangeAddress(descriptor) {
    return {
        kind: 'cells',
        start: toCellLikeAddress(undefined, descriptor.row1, descriptor.col1),
        end: toCellLikeAddress(undefined, descriptor.row2, descriptor.col2),
    };
}
function toCellLikeAddress(sheetName, row, col) {
    const address = {
        row,
        col,
        text: '',
    };
    if (sheetName !== undefined) {
        address.sheetName = sheetName;
    }
    return address;
}
function keyForRange(sheetId, range) {
    if (range.kind === 'cells') {
        return `cells:${sheetId}:${range.start.row}:${range.start.col}:${range.end.row}:${range.end.col}`;
    }
    if (range.kind === 'rows') {
        return `rows:${sheetId}:${range.start.row}:${range.end.row}`;
    }
    return `cols:${sheetId}:${range.start.col}:${range.end.col}`;
}
function keyForDescriptor(descriptor) {
    if (descriptor.kind === 'cells') {
        return `cells:${descriptor.sheetId}:${descriptor.row1}:${descriptor.col1}:${descriptor.row2}:${descriptor.col2}`;
    }
    if (descriptor.kind === 'rows') {
        return `rows:${descriptor.sheetId}:${descriptor.row1}:${descriptor.row2}`;
    }
    return `cols:${descriptor.sheetId}:${descriptor.col1}:${descriptor.col2}`;
}
//# sourceMappingURL=range-registry.js.map