import { PointRange } from './point-range';
import * as Matrix from './matrix';
import { RangeSelection, EntireColumnsSelection, EntireRowsSelection, EmptySelection, } from './selection';
import { createFormulaParser, Model } from './engine';
import { convertPtToPx, createEmptyCell, getEmptyRowFromTemplate, insertColumnDimension, insertRowDimension } from './util';
import { checkEmpty } from '../../../../utils/checkEmpty/checkEmpty';
export function applyBoldToCells(currentData, selectedRange, editable) {
    if (!selectedRange) {
        return currentData;
    }
    const { start, end } = selectedRange;
    let updatedData = currentData;
    let anyBold = false;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            if (currentCell.style?.fontWeight === 'bold') {
                anyBold = true;
                break;
            }
        }
        if (anyBold)
            break;
    }
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            const updatedCell = {
                ...currentCell,
                style: {
                    ...currentCell.style,
                    fontWeight: anyBold ? 'normal' : 'bold',
                },
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function applyItalicToCells(currentData, selectedRange, editable) {
    if (!selectedRange) {
        return currentData;
    }
    const { start, end } = selectedRange;
    let updatedData = currentData;
    let anyItalic = false;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            if (currentCell.style?.fontStyle === 'italic') {
                anyItalic = true;
                break;
            }
        }
        if (anyItalic)
            break;
    }
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            const updatedCell = {
                ...currentCell,
                style: {
                    ...currentCell.style,
                    fontStyle: anyItalic ? 'normal' : 'italic',
                },
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function applyFontFamily(currentData, selectedRange, value, editable) {
    if (!selectedRange) {
        return currentData;
    }
    const { start, end } = selectedRange;
    let updatedData = currentData;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            let updatedCell = {
                ...currentCell,
                style: {
                    ...currentCell.style,
                    fontFamily: value,
                },
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function applyFontSize(currentData, selectedRange, value, editable) {
    if (!selectedRange) {
        return currentData;
    }
    const conversion = Number(value);
    if (isNaN(conversion)) {
        return currentData;
    }
    const fontSizeInPx = convertPtToPx(conversion);
    const { start, end } = selectedRange;
    let updatedData = currentData;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            let updatedCell = {
                ...currentCell,
                style: {
                    ...currentCell.style,
                    fontSize: fontSizeInPx,
                },
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function applyBorderToCells(currentData, selectedRange, value, color, editable) {
    if (!selectedRange) {
        return currentData;
    }
    let updatedData = currentData;
    const { start, end } = selectedRange;
    const getCell = (row, col) => Matrix.get({ row, column: col }, updatedData);
    const setCell = (row, col, cell) => {
        updatedData = Matrix.set({ row, column: col }, cell, updatedData);
    };
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = getCell(row, col);
            if (!currentCell || !editable || currentCell.readOnly)
                continue;
            const updatedCell = { ...currentCell, style: { ...currentCell.style } };
            const aboveCell = getCell(row - 1, col);
            const belowCell = getCell(row + 1, col);
            const leftCell = getCell(row, col - 1);
            const rightCell = getCell(row, col + 1);
            const borderStyle = `2px solid ${color}`;
            const defaultBorder = `1px solid var(--excel-header-border)`;
            const applyBorder = (side) => {
                switch (side) {
                    case 'borderTop':
                        if (!aboveCell || aboveCell.style?.borderBottom !== borderStyle) {
                            updatedCell.style.borderTop = borderStyle;
                        }
                        else {
                            updatedCell.style.borderTop = 'none';
                        }
                        break;
                    case 'borderBottom':
                        if (!belowCell || belowCell.style?.borderTop !== borderStyle) {
                            updatedCell.style.borderBottom = borderStyle;
                        }
                        else {
                            updatedCell.style.borderBottom = 'none';
                        }
                        break;
                    case 'borderLeft':
                        if (!leftCell || leftCell.style?.borderRight !== borderStyle) {
                            updatedCell.style.borderLeft = borderStyle;
                        }
                        else {
                            updatedCell.style.borderLeft = 'none';
                        }
                        break;
                    case 'borderRight':
                        if (!rightCell || rightCell.style?.borderLeft !== borderStyle) {
                            updatedCell.style.borderRight = borderStyle;
                        }
                        else {
                            updatedCell.style.borderRight = 'none';
                        }
                        break;
                }
            };
            switch (value) {
                case 'border-all-sides':
                    applyBorder('borderTop');
                    applyBorder('borderBottom');
                    applyBorder('borderLeft');
                    applyBorder('borderRight');
                    break;
                case 'border-none':
                    updatedCell.style = {
                        ...updatedCell.style,
                        borderRight: defaultBorder,
                        borderLeft: defaultBorder,
                        borderTop: defaultBorder,
                        borderBottom: defaultBorder,
                    };
                    if (aboveCell) {
                        const updatedAbove = {
                            ...aboveCell,
                            style: { ...aboveCell.style },
                        };
                        if (updatedAbove.style.borderBottom === 'none') {
                            updatedAbove.style.borderBottom = borderStyle;
                            setCell(row - 1, col, updatedAbove);
                        }
                    }
                    if (belowCell) {
                        const updatedBelow = {
                            ...belowCell,
                            style: { ...belowCell.style },
                        };
                        if (updatedBelow.style.borderTop === 'none') {
                            updatedBelow.style.borderTop = borderStyle;
                            setCell(row + 1, col, updatedBelow);
                        }
                    }
                    if (leftCell) {
                        const updatedLeft = { ...leftCell, style: { ...leftCell.style } };
                        if (updatedLeft.style.borderRight === 'none') {
                            updatedLeft.style.borderRight = borderStyle;
                            setCell(row, col - 1, updatedLeft);
                        }
                    }
                    if (rightCell) {
                        const updatedRight = {
                            ...rightCell,
                            style: { ...rightCell.style },
                        };
                        if (updatedRight.style.borderLeft === 'none') {
                            updatedRight.style.borderLeft = borderStyle;
                            setCell(row, col + 1, updatedRight);
                        }
                    }
                    break;
                case 'border-right':
                    applyBorder('borderRight');
                    break;
                case 'border-left':
                    applyBorder('borderLeft');
                    break;
                case 'border-top':
                    applyBorder('borderTop');
                    break;
                case 'border-bottom':
                    applyBorder('borderBottom');
                    break;
            }
            setCell(row, col, updatedCell);
        }
    }
    return updatedData;
}
export function applyTextAlign(currentData, selectedRange, value, editable) {
    if (!selectedRange) {
        return currentData;
    }
    const { start, end } = selectedRange;
    let updatedData = currentData;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            let updatedCell = {
                ...currentCell,
                style: {
                    ...currentCell.style,
                    textAlign: value,
                },
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function applyUnderlineToCells(currentData, selectedRange, editable) {
    if (!selectedRange) {
        return currentData;
    }
    const { start, end } = selectedRange;
    let updatedData = currentData;
    let anyUnderlined = false;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            if (currentCell.style?.textDecoration === 'underline') {
                anyUnderlined = true;
                break;
            }
        }
        if (anyUnderlined)
            break;
    }
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            const updatedCell = {
                ...currentCell,
                style: {
                    ...currentCell.style,
                    textDecoration: anyUnderlined ? 'none' : 'underline',
                },
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function isValidHexColor(color) {
    if (color.length !== 6) {
        return false;
    }
    const hexChars = '0123456789ABCDEFabcdef';
    for (let i = 1; i < color.length; i++) {
        if (!hexChars.includes(color[i])) {
            return false;
        }
    }
    return true;
}
export function applyColorToCells(currentData, selectedRange, color, editable) {
    if (!selectedRange) {
        return currentData;
    }
    const { start, end } = selectedRange;
    let updatedData = currentData;
    const isHex = isValidHexColor(color);
    const applyColor = isHex ? `#${color}` : color;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || !editable)
                continue;
            if (currentCell.readOnly)
                continue;
            const updatedCell = {
                ...currentCell,
                style: {
                    ...currentCell.style,
                    color: applyColor,
                },
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function applyBackgroundColorToCells(currentData, selectedRange, backgroundColor, editable) {
    if (!selectedRange) {
        return currentData;
    }
    if (selectedRange) {
        const { start, end } = selectedRange;
        let updatedData = currentData;
        const isHex = isValidHexColor(backgroundColor);
        const applyColor = isHex ? `#${backgroundColor}` : backgroundColor;
        for (let row = start.row; row <= end.row; row++) {
            for (let col = start.column; col <= end.column; col++) {
                const currentCell = Matrix.get({ row, column: col }, updatedData);
                if (!currentCell || !editable)
                    continue;
                if (currentCell.readOnly)
                    continue;
                const updatedCell = {
                    ...currentCell,
                    style: {
                        ...currentCell.style,
                        backgroundColor: applyColor,
                    },
                };
                updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
            }
        }
        return updatedData;
    }
    return currentData;
}
export function applyFormatePainter(currentData, activePoint) {
    if (!activePoint) {
        return undefined;
    }
    const { row, column } = activePoint;
    let updatedData = { ...currentData };
    const currentCell = Matrix.get({ row, column }, updatedData);
    if (currentCell && currentCell.style) {
        return currentCell.style;
    }
    return undefined;
}
export function cellFormatePainter(currentData, formattedStyle, activePoint) {
    if (!activePoint || !formattedStyle.open) {
        return currentData;
    }
    let updatedData = currentData;
    const currentCell = Matrix.get(activePoint, updatedData);
    if (currentCell && !currentCell.readOnly) {
        const updatedCell = {
            ...currentCell,
            style: formattedStyle.style,
        };
        updatedData = Matrix.set(activePoint, updatedCell, updatedData);
    }
    return updatedData;
}
export function dragEndFormatePainter(currentData, selectedRange, formattedStyle) {
    if (!selectedRange || !formattedStyle.open) {
        return currentData;
    }
    const { start, end } = selectedRange;
    let updatedData = currentData;
    for (let row = start.row; row <= end.row; row++) {
        for (let col = start.column; col <= end.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell)
                continue;
            if (currentCell.readOnly)
                continue;
            const updatedCell = {
                ...currentCell,
                style: formattedStyle.style,
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function dragEndAutoFill(currentData, selectedRange, cellValue, activeCell, control) {
    if (!selectedRange) {
        return currentData;
    }
    const { start, end } = selectedRange;
    const rowCount = start.row - end.row;
    const columnCount = start.column - end.column;
    let startPoint = { row: 0, column: 0 };
    let endPoint = { row: 0, column: 0 };
    if (Math.abs(rowCount) > Math.abs(columnCount)) {
        // vertical drag
        startPoint = {
            row: Math.min(start.row, end.row),
            column: activeCell?.column ?? start.column,
        };
        endPoint = {
            row: Math.max(start.row, end.row),
            column: activeCell?.column ?? start.column,
        };
    }
    else {
        // horizontal drag
        startPoint = {
            row: activeCell?.row ?? start.row,
            column: Math.min(start.column, end.column),
        };
        endPoint = {
            row: activeCell?.row ?? start.row,
            column: Math.max(start.column, end.column),
        };
    }
    let updatedData = currentData;
    const baseValue = String(cellValue.value ?? '');
    const matches = [...baseValue.matchAll(/(-?\d+)/g)];
    const isSourceDropDownType = ['dropDown'].includes(cellValue?.inputType?.type || '');
    for (let row = startPoint.row; row <= endPoint.row; row++) {
        for (let col = startPoint.column; col <= endPoint.column; col++) {
            const currentCell = Matrix.get({ row, column: col }, updatedData);
            if (!currentCell || currentCell.readOnly) {
                continue;
            }
            const isTargetSpecialType = ['file', 'dropDown'].includes(currentCell?.inputType?.type || '');
            const isTargetFileType = ['file'].includes(currentCell?.inputType?.type || '');
            if (!isSourceDropDownType && isTargetSpecialType) {
                continue;
            }
            if (isSourceDropDownType && isTargetFileType) {
                continue;
            }
            let newValue = baseValue;
            if (!control && matches.length > 0) {
                let step = 0;
                if (Math.abs(rowCount) > Math.abs(columnCount)) {
                    step = row - (activeCell?.row ?? startPoint.row);
                }
                else {
                    step = col - (activeCell?.column ?? startPoint.column);
                }
                let index = 0;
                newValue = newValue.replace(/(-?\d+)/g, () => {
                    const match = matches[index++];
                    if (!match)
                        return '';
                    const originalNum = parseInt(match?.[1] ?? '', 10);
                    return (originalNum + step).toString();
                });
            }
            const updatedCell = {
                ...currentCell,
                style: cellValue.style,
                value: newValue,
                inputType: cellValue.inputType,
            };
            updatedData = Matrix.set({ row, column: col }, updatedCell, updatedData);
        }
    }
    return updatedData;
}
export function edit(state) {
    if (isActiveReadOnly(state) || !state.editable) {
        return state;
    }
    return { ...state, mode: 'edit' };
}
const canClearCell = (cell) => cell && !cell.readOnly;
const clearCell = (cell) => {
    if (!canClearCell(cell) || cell?.inputType?.type === 'file') {
        return cell;
    }
    return {
        ...cell,
        value: '',
        style: cell?.style,
    };
};
const nextLine = (cell) => {
    if (!cell)
        return undefined;
    return {
        ...cell,
        value: (cell.value || '') + '\n',
        style: cell.style,
    };
};
export function clear(state) {
    if (!state.active) {
        return state;
    }
    const selectedRange = state.selected.toRange(state.model.data);
    const changes = [];
    let newData = state.model.data;
    for (const point of selectedRange || []) {
        const cell = Matrix.get(point, state.model.data);
        const clearedCell = clearCell(cell);
        changes.push({
            prevCell: cell || null,
            nextCell: clearedCell || null,
        });
        newData = Matrix.set(point, clearedCell, newData);
    }
    return {
        ...state,
        model: new Model(createFormulaParser, newData),
        ...commit(changes),
    };
}
export function clearEditMode(state) {
    if (!state.active) {
        return state;
    }
    if (state.mode === 'view') {
        const resultState = clear(state);
        return resultState;
    }
    else {
        const changes = [];
        let newData = state.model.data;
        const cell = Matrix.get(state.active, state.model.data);
        const clearedCell = clearCell(cell);
        changes.push({
            prevCell: cell || null,
            nextCell: clearedCell || null,
        });
        newData = Matrix.set(state.active, clearedCell, newData);
        return {
            ...state,
            mode: 'edit',
            model: new Model(createFormulaParser, newData),
            ...commit(changes),
        };
    }
}
export function enterFunctionality(state) {
    if (!state.active) {
        return state;
    }
    const selectedRange = state.selected.toRange(state.model.data);
    const changes = [];
    let newData = state.model.data;
    for (const point of selectedRange || []) {
        const cell = Matrix.get(point, state.model.data);
        const nextLineCell = nextLine(cell);
        changes.push({
            prevCell: cell || null,
            nextCell: nextLineCell || null,
        });
        newData = Matrix.set(point, nextLineCell, newData);
    }
    return {
        ...state,
        mode: 'edit',
        model: new Model(createFormulaParser, newData),
        ...commit(changes),
    };
}
export function blur(state) {
    return {
        ...state,
        active: null,
        copied: null,
        formattedStyle: { open: false, style: undefined },
        selected: new EmptySelection(),
    };
}
export function view(state) {
    return { ...state, mode: 'view' };
}
export function commit(changes) {
    return { lastCommit: changes };
}
export const go = (rowDelta, columnDelta) => (state) => {
    if (!state.active) {
        return;
    }
    const size = Matrix.getSize(state.model.data);
    const newColumn = state.active.column + columnDelta;
    const shouldWrap = newColumn >= size.columns;
    const nextActive = {
        row: state.active.row + rowDelta + (shouldWrap ? 1 : 0),
        column: (state.active.column + columnDelta) % size.columns,
    };
    if (!Matrix.has(nextActive, state.model.data)) {
        return { ...state, mode: 'view' };
    }
    return {
        ...state,
        active: nextActive,
        selected: new RangeSelection(new PointRange(nextActive, nextActive)),
        mode: 'view',
    };
};
const keyDownHandlers = {
    ArrowUp: go(-1, 0),
    ArrowDown: go(+1, 0),
    ArrowLeft: go(0, -1),
    ArrowRight: go(0, +1),
    Tab: go(0, +1),
    Enter: edit,
    Backspace: clearEditMode,
    Delete: clear,
    Escape: blur,
};
const editKeyDownHandlers = {
    Escape: view,
    Tab: keyDownHandlers.Tab,
    Enter: keyDownHandlers.ArrowDown,
    Delete: clear,
};
const editShiftKeyDownHandlers = {
    Tab: go(0, -1),
};
export var Direction;
(function (Direction) {
    Direction["Left"] = "Left";
    Direction["Right"] = "Right";
    Direction["Top"] = "Top";
    Direction["Bottom"] = "Bottom";
})(Direction || (Direction = {}));
const shiftKeyDownHandlers = {
    ArrowUp: (state) => ({
        ...state,
        selected: modifyEdge(state.selected, state.active, state.model.data, Direction.Top),
    }),
    ArrowDown: (state) => ({
        ...state,
        selected: modifyEdge(state.selected, state.active, state.model.data, Direction.Bottom),
    }),
    ArrowLeft: (state) => ({
        ...state,
        selected: modifyEdge(state.selected, state.active, state.model.data, Direction.Left),
    }),
    ArrowRight: (state) => ({
        ...state,
        selected: modifyEdge(state.selected, state.active, state.model.data, Direction.Right),
    }),
    Tab: go(0, -1),
};
const shiftMetaKeyDownHandlers = {};
const metaKeyDownHandlers = {};
export function getKeyDownHandler(state, event) {
    const { key } = event;
    let handlers;
    if (state.mode === 'edit') {
        if (event.shiftKey && key === 'Enter') {
            handlers = keyDownHandlers;
        }
        else if (event.altKey && key === 'Enter') {
            return enterFunctionality;
        }
        else if (event.shiftKey) {
            handlers = editShiftKeyDownHandlers;
        }
        else {
            handlers = editKeyDownHandlers;
        }
    }
    else if (event.shiftKey && event.metaKey) {
        handlers = shiftMetaKeyDownHandlers;
    }
    else if (event.shiftKey) {
        handlers = shiftKeyDownHandlers;
    }
    else if (event.metaKey) {
        handlers = metaKeyDownHandlers;
    }
    else {
        handlers = keyDownHandlers;
    }
    return handlers[key];
}
/** Returns whether the reducer has a handler for the given keydown event */
export function hasKeyDownHandler(state, event) {
    return getKeyDownHandler(state, event) !== undefined;
}
/** Returns whether the active cell is read only */
export function isActiveReadOnly(state) {
    const activeCell = getActive(state);
    return Boolean(activeCell?.readOnly);
}
/** Gets active cell from given state */
export function getActive(state) {
    const activeCell = state.active && Matrix.get(state.active, state.model.data);
    return activeCell || null;
}
/** Modify given edge according to given active point and data */
export function modifyEdge(selection, active, data, direction) {
    if (!active) {
        return selection;
    }
    if (selection instanceof RangeSelection) {
        const nextSelection = modifyRangeSelectionEdge(selection, active, data, direction);
        // @ts-expect-error
        return nextSelection;
    }
    if (selection instanceof EntireColumnsSelection) {
        // @ts-expect-error
        return modifyEntireColumnsSelection(selection, active, data, direction);
    }
    if (selection instanceof EntireRowsSelection) {
        // @ts-expect-error
        return modifyEntireRowsSelection(selection, active, data, direction);
    }
    return selection;
}
export function modifyRangeSelectionEdge(rangeSelection, active, data, edge) {
    const field = edge === Direction.Left || edge === Direction.Right ? 'column' : 'row';
    const key = edge === Direction.Left || edge === Direction.Top ? 'start' : 'end';
    const delta = key === 'start' ? -1 : 1;
    const edgeOffsets = rangeSelection.range.has({
        ...active,
        [field]: active[field] + delta * -1,
    });
    const keyToModify = edgeOffsets ? (key === 'start' ? 'end' : 'start') : key;
    const nextRange = new PointRange(rangeSelection.range.start, rangeSelection.range.end);
    nextRange[keyToModify][field] += delta;
    const nextSelection = new RangeSelection(nextRange).normalizeTo(data);
    return nextSelection;
}
export function modifyEntireRowsSelection(selection, active, data, edge) {
    if (edge === Direction.Left || edge === Direction.Right) {
        return selection;
    }
    const delta = edge === Direction.Top ? -1 : 1;
    const property = edge === Direction.Top ? 'start' : 'end';
    const oppositeProperty = property === 'start' ? 'end' : 'start';
    const newSelectionData = { ...selection };
    if (edge === Direction.Top
        ? selection.end > active.row
        : selection.start < active.row) {
        newSelectionData[oppositeProperty] = selection[oppositeProperty] + delta;
    }
    else {
        newSelectionData[property] = selection[property] + delta;
    }
    const nextSelection = new EntireRowsSelection(Math.max(newSelectionData.start, 0), Math.max(newSelectionData.end, 0));
    return nextSelection.normalizeTo(data);
}
export function modifyEntireColumnsSelection(selection, active, data, edge) {
    if (edge === Direction.Top || edge === Direction.Bottom) {
        return selection;
    }
    const delta = edge === Direction.Left ? -1 : 1;
    const property = edge === Direction.Left ? 'start' : 'end';
    const oppositeProperty = property === 'start' ? 'end' : 'start';
    const newSelectionData = { ...selection };
    if (edge === Direction.Left
        ? selection.end > active.row
        : selection.start < active.row) {
        newSelectionData[oppositeProperty] = selection[oppositeProperty] + delta;
    }
    else {
        newSelectionData[property] = selection[property] + delta;
    }
    const nextSelection = new EntireColumnsSelection(Math.max(newSelectionData.start, 0), Math.max(newSelectionData.end, 0));
    return nextSelection.normalizeTo(data);
}
export const handleAddColumn = (state, action = {}, direction) => {
    const { selectedRow, selectedColumn, model, columnDimensions } = state;
    const { column, columnWidth = 100 } = action;
    const targetIdx = column ?? selectedColumn;
    if (checkEmpty(targetIdx))
        return state;
    // Calculate actual insertion point
    const insertIndex = direction === 'LEFT' ? targetIdx : targetIdx + 1;
    // Map data once: create new array, inject new cell at insertIndex
    const updatedData = model.data.map((row) => {
        const newRow = [...row];
        // Use the cell currently at the target index as a template for styles
        const templateCell = row[targetIdx];
        newRow.splice(insertIndex, 0, createEmptyCell(templateCell));
        return newRow;
    });
    const updatedModel = new Model(model.createFormulaParser, updatedData);
    const newSelectedRow = selectedRow ?? 0;
    return {
        ...state,
        model: updatedModel,
        selectedRow: newSelectedRow,
        selectedColumn: insertIndex,
        active: { row: newSelectedRow, column: insertIndex },
        selected: new EntireColumnsSelection(insertIndex, insertIndex),
        columnDimensions: insertColumnDimension(columnDimensions, insertIndex, columnWidth),
    };
};
export const handleAddRow = (state, action = {}, direction) => {
    const { selectedRow, selectedColumn, model, rowDimensions } = state;
    const { row } = action;
    const targetIdx = row ?? selectedRow;
    if (checkEmpty(targetIdx))
        return state;
    const insertIndex = direction === 'TOP' ? targetIdx : targetIdx + 1;
    const updatedData = [...model.data];
    // Use the row at targetIdx as a template for styles/formatting
    const templateRow = model.data[targetIdx] || [];
    const newRow = getEmptyRowFromTemplate(templateRow, updatedData[0]?.length);
    updatedData.splice(insertIndex, 0, newRow ?? []);
    const updatedModel = new Model(model.createFormulaParser, updatedData);
    const newCol = selectedColumn ?? 0;
    return {
        ...state,
        model: updatedModel,
        selectedRow: insertIndex,
        selectedColumn: newCol,
        active: { row: insertIndex, column: newCol },
        selected: new EntireRowsSelection(insertIndex, insertIndex),
        rowDimensions: insertRowDimension(rowDimensions, insertIndex, 32),
    };
};
//# sourceMappingURL=reducerFunctions.js.map