import { parseA1RowNumber } from '../../formula/a1-row-number.js';
import { buildFormulaFamilyShapeKey } from '../../formula/formula-family-deps.js';
const INITIAL_PREFIX_AGGREGATE_RE = /^=?(SUM|AVERAGE|AVG|COUNT|MIN|MAX)\(([A-Z]+)1:\2([1-9]\d*)\)(?:\+(\d+(?:\.\d+)?))?$/i;
const INITIAL_PREFIX_AGGREGATE_KIND_BY_CALLEE = {
    SUM: 'sum',
    AVERAGE: 'average',
    AVG: 'average',
    COUNT: 'count',
    MIN: 'min',
    MAX: 'max',
};
function initialColumnToIndex(column) {
    let value = 0;
    for (let index = 0; index < column.length; index += 1) {
        const code = column.charCodeAt(index);
        value = value * 26 + (code >= 97 && code <= 122 ? code - 96 : code - 64);
    }
    return value - 1;
}
function initialReadColumn(source, start) {
    let next = start;
    while (next < source.length) {
        const code = source.charCodeAt(next);
        if (!((code >= 65 && code <= 90) || (code >= 97 && code <= 122))) {
            break;
        }
        next += 1;
    }
    return next === start ? undefined : { column: source.slice(start, next), next };
}
function initialReadRowNumber(source, start) {
    if (start >= source.length) {
        return undefined;
    }
    const firstCode = source.charCodeAt(start);
    if (firstCode < 49 || firstCode > 57) {
        return undefined;
    }
    let row = firstCode - 48;
    let next = start + 1;
    while (next < source.length) {
        const code = source.charCodeAt(next);
        if (code < 48 || code > 57) {
            break;
        }
        row = row * 10 + (code - 48);
        if (!Number.isSafeInteger(row)) {
            return undefined;
        }
        next += 1;
    }
    return { row, next };
}
function initialReadNumberLiteral(source, start) {
    let next = start;
    while (next < source.length) {
        const code = source.charCodeAt(next);
        if (code < 48 || code > 57) {
            break;
        }
        next += 1;
    }
    if (next < source.length && source.charCodeAt(next) === 46) {
        const fractionStart = next + 1;
        next = fractionStart;
        while (next < source.length) {
            const code = source.charCodeAt(next);
            if (code < 48 || code > 57) {
                break;
            }
            next += 1;
        }
        if (next === fractionStart) {
            return undefined;
        }
    }
    return next === start ? undefined : { text: source.slice(start, next), next };
}
function initialReadRowLiteralSuffix(source, start, ownerRow) {
    if (source.charCodeAt(start) !== 43) {
        return undefined;
    }
    const row = initialReadRowNumber(source, start + 1);
    if (!row || row.row !== ownerRow + 1) {
        return undefined;
    }
    return {
        token: '+r0',
        next: row.next,
    };
}
function initialReadRelativeCellToken(source, start, ownerRow, ownerCol) {
    const column = initialReadColumn(source, start);
    if (!column) {
        return undefined;
    }
    const row = initialReadRowNumber(source, column.next);
    if (!row || row.row - 1 !== ownerRow) {
        return undefined;
    }
    const col = initialColumnToIndex(column.column);
    if (col < 0) {
        return undefined;
    }
    const normalizedColumn = column.column.toUpperCase();
    return {
        token: `c${col - ownerCol}`,
        next: row.next,
        ref: {
            address: `${normalizedColumn}${row.row}`,
            row: row.row - 1,
            col,
            rowAbsolute: false,
            colAbsolute: false,
        },
    };
}
function initialReadRelativeCellKeyToken(source, start, ownerRow, ownerCol) {
    let next = start;
    let oneBasedCol = 0;
    while (next < source.length) {
        const code = source.charCodeAt(next);
        if (code >= 65 && code <= 90) {
            oneBasedCol = oneBasedCol * 26 + code - 64;
            next += 1;
            continue;
        }
        if (code >= 97 && code <= 122) {
            oneBasedCol = oneBasedCol * 26 + code - 96;
            next += 1;
            continue;
        }
        break;
    }
    if (next === start) {
        return undefined;
    }
    const row = initialReadRowNumber(source, next);
    if (!row || row.row - 1 !== ownerRow) {
        return undefined;
    }
    const col = oneBasedCol - 1;
    if (col < 0) {
        return undefined;
    }
    return {
        token: `c${col - ownerCol}`,
        next: row.next,
    };
}
export function tryBuildInitialSimpleRowRelativeBinaryTemplateKey(source, ownerRow, ownerCol) {
    return tryBuildInitialSimpleRowRelativeBinaryTemplateKeyInfo(source, ownerRow, ownerCol)?.key;
}
export function tryBuildInitialSimpleRowRelativeBinaryTemplateKeyInfo(source, ownerRow, ownerCol) {
    let index = source.charCodeAt(0) === 61 ? 1 : 0;
    const left = initialReadRelativeCellKeyToken(source, index, ownerRow, ownerCol);
    if (!left) {
        return undefined;
    }
    index = left.next;
    const operator = source[index];
    if (operator !== '+' && operator !== '-' && operator !== '*' && operator !== '/') {
        return undefined;
    }
    index += 1;
    const rightCell = initialReadRelativeCellKeyToken(source, index, ownerRow, ownerCol);
    if (rightCell) {
        if (rightCell.next === source.length) {
            return { key: `${left.token}${operator}${rightCell.token}`, usesRowLiteralSuffix: false };
        }
        const suffix = initialReadRowLiteralSuffix(source, rightCell.next, ownerRow);
        return suffix && suffix.next === source.length
            ? { key: `${left.token}${operator}${rightCell.token}${suffix.token}`, usesRowLiteralSuffix: true }
            : undefined;
    }
    const rightNumber = initialReadNumberLiteral(source, index);
    if (!rightNumber) {
        return undefined;
    }
    if (rightNumber.next === source.length) {
        return { key: `${left.token}${operator}n${rightNumber.text}`, usesRowLiteralSuffix: false };
    }
    const suffix = initialReadRowLiteralSuffix(source, rightNumber.next, ownerRow);
    return suffix && suffix.next === source.length
        ? { key: `${left.token}${operator}n${rightNumber.text}${suffix.token}`, usesRowLiteralSuffix: true }
        : undefined;
}
export function tryBuildInitialSimpleRowRelativeBinaryTemplate(source, ownerRow, ownerCol) {
    let index = source.charCodeAt(0) === 61 ? 1 : 0;
    const left = initialReadRelativeCellToken(source, index, ownerRow, ownerCol);
    if (!left) {
        return undefined;
    }
    index = left.next;
    const operator = source[index];
    if (operator !== '+' && operator !== '-' && operator !== '*' && operator !== '/') {
        return undefined;
    }
    index += 1;
    const rightCell = initialReadRelativeCellToken(source, index, ownerRow, ownerCol);
    if (rightCell) {
        if (rightCell.next === source.length) {
            return createInitialSimpleBinaryTemplate(`${left.token}${operator}${rightCell.token}`, [left.ref, rightCell.ref], false);
        }
        const suffix = initialReadRowLiteralSuffix(source, rightCell.next, ownerRow);
        return suffix && suffix.next === source.length
            ? createInitialSimpleBinaryTemplate(`${left.token}${operator}${rightCell.token}${suffix.token}`, [left.ref, rightCell.ref], true)
            : undefined;
    }
    const rightNumber = initialReadNumberLiteral(source, index);
    if (!rightNumber) {
        return undefined;
    }
    if (rightNumber.next === source.length) {
        return createInitialSimpleBinaryTemplate(`${left.token}${operator}n${rightNumber.text}`, [left.ref], false);
    }
    const suffix = initialReadRowLiteralSuffix(source, rightNumber.next, ownerRow);
    return suffix && suffix.next === source.length
        ? createInitialSimpleBinaryTemplate(`${left.token}${operator}n${rightNumber.text}${suffix.token}`, [left.ref], true)
        : undefined;
}
function createInitialSimpleBinaryTemplate(key, parsedSymbolicRefs, usesRowLiteralSuffix) {
    const symbolicRefs = Array(parsedSymbolicRefs.length);
    const parsedDeps = Array(parsedSymbolicRefs.length);
    for (let index = 0; index < parsedSymbolicRefs.length; index += 1) {
        const ref = parsedSymbolicRefs[index];
        symbolicRefs[index] = ref.address;
        parsedDeps[index] = { kind: 'cell', ...ref };
    }
    return {
        key,
        parsedRefs: {
            symbolicRefs,
            parsedDeps,
            parsedSymbolicRefs,
        },
        usesRowLiteralSuffix,
    };
}
export function tryBuildInitialPrefixSumTemplateKey(source, ownerRow, ownerCol) {
    const match = INITIAL_PREFIX_AGGREGATE_RE.exec(source);
    if (!match) {
        return undefined;
    }
    const endRow = parseA1RowNumber(match[3]);
    if (endRow === undefined || endRow !== ownerRow + 1) {
        return undefined;
    }
    const offsetText = match[4];
    const usesRowLiteralSuffix = offsetText !== undefined && Number(offsetText) === ownerRow + 1;
    const resultOffset = offsetText === undefined ? 0 : Number(offsetText);
    if (!Number.isFinite(resultOffset)) {
        return undefined;
    }
    const callee = match[1].toUpperCase();
    const aggregateKind = INITIAL_PREFIX_AGGREGATE_KIND_BY_CALLEE[callee];
    if (aggregateKind === undefined) {
        return undefined;
    }
    const rangeColumn = match[2].toUpperCase();
    const rangeCol = initialColumnToIndex(rangeColumn);
    if (rangeCol < 0) {
        return undefined;
    }
    const relativeRangeCol = rangeCol - ownerCol;
    const offsetKey = offsetText === undefined ? 'none' : usesRowLiteralSuffix ? 'row' : offsetText;
    return {
        aggregateKind,
        callee,
        key: `prefix-aggregate:${callee}:c${relativeRangeCol}:offset:${offsetKey}`,
        rangeCol,
        rangeColumn,
        resultOffset,
        usesRowLiteralSuffix,
    };
}
export function translateInitialPrefixSumFormula(entry, source, ownerRow, ownerCol, templateKey) {
    const column = templateKey.rangeColumn;
    const startAddress = `${column}1`;
    const endAddress = `${column}${ownerRow + 1}`;
    const rangeAddress = `${startAddress}:${endAddress}`;
    const resultOffset = templateKey.usesRowLiteralSuffix ? ownerRow + 1 : templateKey.resultOffset;
    const range = {
        address: rangeAddress,
        kind: 'range',
        refKind: 'cells',
        startAddress,
        endAddress,
        startRow: 0,
        endRow: ownerRow,
        startCol: templateKey.rangeCol,
        endCol: templateKey.rangeCol,
    };
    const rangeNode = {
        kind: 'RangeRef',
        refKind: 'cells',
        start: startAddress,
        end: endAddress,
    };
    const aggregateNode = {
        kind: 'CallExpr',
        callee: templateKey.callee,
        args: [rangeNode],
    };
    const ast = resultOffset === 0
        ? aggregateNode
        : {
            kind: 'BinaryExpr',
            operator: '+',
            left: aggregateNode,
            right: { kind: 'NumberLiteral', value: resultOffset },
        };
    const rowDelta = ownerRow - entry.anchorRow;
    const colDelta = ownerCol - entry.anchorCol;
    return {
        ...entry.resolution,
        compiled: {
            ...entry.anchorCompiled,
            source,
            ast,
            optimizedAst: ast,
            astMatchesSource: true,
            directAggregateCandidate: {
                callee: templateKey.callee,
                aggregateKind: templateKey.aggregateKind,
                symbolicRangeIndex: 0,
                ...(resultOffset !== 0 ? { resultOffset } : {}),
            },
            deps: [rangeAddress],
            parsedDeps: [range],
            symbolicRanges: [rangeAddress],
            parsedSymbolicRanges: [range],
        },
        translated: entry.resolution.translated || rowDelta !== 0 || colDelta !== 0,
        rowDelta: entry.resolution.rowDelta + rowDelta,
        colDelta: entry.resolution.colDelta + colDelta,
    };
}
export function initialFormulaFamilyShapeKey(formula) {
    return buildFormulaFamilyShapeKey({
        compiled: formula.compiled,
        dependencyCount: formula.dependencyIndices.length,
        rangeDependencyCount: formula.rangeDependencies.length,
        directAggregateKind: formula.directAggregate?.aggregateKind,
        directLookupKind: formula.directLookup?.kind,
        directScalarKind: formula.directScalar?.kind,
        directCriteriaKind: formula.directCriteria?.aggregateKind,
    });
}
//# sourceMappingURL=formula-initialization-template-keys.js.map