"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "useKeyNavigation", {
    enumerable: true,
    get: function() {
        return useKeyNavigation;
    }
});
const _react = require("react");
const _Calendartypes = require("../Calendar.types");
const _utils = require("../utils");
const _reducer = require("../store/reducer");
/**
 * Метод для получения стороны двойного календаря.
 */ const getDoubleCalendarSide = (currentIndex, calendarState)=>{
    if (currentIndex >= 0 && currentIndex < _utils.offsetMap[calendarState]) {
        return 'first';
    }
    if (currentIndex >= _utils.offsetMap[calendarState] && currentIndex < _utils.offsetMap[calendarState] * 2) {
        return 'second';
    }
    return '';
};
const isOutOfBound = ([rowIndex, columnIndex], rowSize, columnSize)=>columnIndex === -1 || columnIndex === columnSize + 1 || rowIndex === -1 || rowIndex === rowSize + 1;
const isVisible = (refs, row, column)=>refs.current?.[row]?.[column];
const isAriaDisableItem = (item)=>item?.getAttribute('aria-disabled') === 'true';
const hasDisabledArrowKey = ({ refs, payload, key })=>{
    const [previousRowIndex, previousColumnIndex] = payload;
    const disabledArrowKey = refs.current?.[previousRowIndex]?.[previousColumnIndex]?.dataset.disabledArrowKey;
    return Boolean(disabledArrowKey?.includes(key));
};
const hasDisabledMonths = ({ item, key })=>{
    return item?.dataset?.disabledMonths ? item?.dataset?.disabledMonths.includes(key) : false;
};
const getNextCorrectPosition = ({ refs, rowSize, newRowIndex, newColumnIndex, columnSize, minColumnIndex, defaultState = [] })=>{
    let item = refs.current?.[newRowIndex]?.[newColumnIndex];
    while(isAriaDisableItem(item) && newColumnIndex <= columnSize){
        newColumnIndex++;
        if (newColumnIndex > columnSize && newRowIndex < rowSize) {
            newRowIndex++;
            newColumnIndex = minColumnIndex;
        }
        item = refs.current?.[newRowIndex]?.[newColumnIndex];
        if (item) {
            const isDisabledArrowDown = hasDisabledArrowKey({
                refs,
                payload: [
                    newRowIndex,
                    newColumnIndex
                ],
                key: 'down'
            });
            const isDisabledArrowRight = hasDisabledArrowKey({
                refs,
                payload: [
                    newRowIndex,
                    newColumnIndex
                ],
                key: 'right'
            });
            if (isDisabledArrowDown || isDisabledArrowRight) {
                return defaultState;
            }
        }
    }
    return [
        newRowIndex,
        newColumnIndex
    ];
};
const getPreviousCorrectPosition = ({ refs, rowSize, newRowIndex, newColumnIndex, columnSize, minColumnIndex, defaultState = [] })=>{
    let item = refs.current?.[newRowIndex]?.[newColumnIndex];
    while(isAriaDisableItem(item) && newColumnIndex >= minColumnIndex){
        newColumnIndex--;
        if (newColumnIndex < minColumnIndex && newRowIndex <= rowSize) {
            newRowIndex--;
            newColumnIndex = columnSize;
        }
        item = refs.current?.[newRowIndex]?.[newColumnIndex];
        if (item && item.dataset.day === '1') {
            const isDisabledArrowUp = hasDisabledArrowKey({
                refs,
                payload: [
                    newRowIndex,
                    newColumnIndex
                ],
                key: 'up'
            });
            const isDisabledArrowLeft = hasDisabledArrowKey({
                refs,
                payload: [
                    newRowIndex,
                    newColumnIndex
                ],
                key: 'left'
            });
            if (isDisabledArrowUp || isDisabledArrowLeft) {
                return defaultState;
            }
        }
    }
    return [
        newRowIndex,
        newColumnIndex
    ];
};
/**
 * Метод для нахождения стартового индекса дня в следующем/предыдущем месяце
 */ function getCorrectColumnIndex({ refs, rowSize, isNext }) {
    if (isNext) {
        let index = refs[rowSize].filter(Boolean).length;
        if (!index) {
            index = refs[rowSize - 1].filter(Boolean).length;
        }
        return index;
    }
    // Смещение влево - начинаем поиск первого не-nullable элемента в первой строке
    return refs[0].findIndex((item)=>item) - 1;
}
const getCorrectIndexes = (refs, [rowIndex, columnIndex], rowSize, columnSize, withShift, defaultState, calendarState)=>{
    let newRowIndex = rowIndex;
    let newColumnIndex = columnIndex;
    const minRowIndex = newRowIndex + 1;
    const maxRowIndex = newRowIndex - 1;
    const minColumnIndex = 0;
    const maxColumnIndex = columnSize;
    if (newColumnIndex === minColumnIndex - 1) {
        newColumnIndex += 1;
        while(newColumnIndex < maxColumnIndex && !isVisible(refs, newRowIndex, newColumnIndex)){
            newColumnIndex++;
        }
    }
    if (newColumnIndex === columnSize + 1) {
        newColumnIndex -= 1;
        while(newColumnIndex > minColumnIndex && !isVisible(refs, newRowIndex, newColumnIndex)){
            newColumnIndex--;
        }
    }
    if (newRowIndex === minColumnIndex - 1) {
        newRowIndex = _utils.offsetMap[calendarState] - 1;
        while(newRowIndex > minRowIndex && !isVisible(refs, newRowIndex, newColumnIndex)){
            newRowIndex--;
        }
    }
    if (newRowIndex === rowSize + 1) {
        newRowIndex = rowSize + 1 - _utils.offsetMap[calendarState];
        while(newRowIndex <= maxRowIndex && !isVisible(refs, newRowIndex, newColumnIndex)){
            newRowIndex++;
        }
    }
    // INFO: Логика для получения правильной позиции дня, когда переключились на другой месяц
    // INFO: и ставим указатель на первый доступный день. Только при зажатой клавиши Shift.
    if (isAriaDisableItem(refs.current?.[newRowIndex]?.[newColumnIndex]) && withShift) {
        const isNext = rowIndex === rowSize + 1;
        const isPrevious = rowIndex === minColumnIndex - 1;
        const state = {
            refs,
            rowSize,
            newColumnIndex,
            minColumnIndex,
            columnSize,
            newRowIndex,
            defaultState
        };
        if (isNext) {
            return getNextCorrectPosition(state);
        }
        if (isPrevious) {
            return getPreviousCorrectPosition(state);
        }
    }
    return [
        newRowIndex,
        newColumnIndex
    ];
};
const useKeyNavigation = ({ isDouble = false, size, onPrev, onNext, calendarState })=>{
    const [rowSize, columnSize] = size;
    const [selectIndexes, setSelectIndexes] = (0, _react.useState)([
        0,
        0
    ]);
    const [isOutOfMinMaxRange, setIsOutOfMinMaxRange] = (0, _react.useState)(false);
    const withShiftState = (0, _react.useRef)(false);
    const currentIndexes = (0, _react.useRef)([
        0,
        0
    ]);
    const outerRefs = (0, _react.useRef)(Array(rowSize + 1).fill(0).map(()=>Array(columnSize + 1)));
    (0, _react.useLayoutEffect)(()=>{
        if (!isOutOfBound(selectIndexes, rowSize, columnSize)) {
            return;
        }
        if (withShiftState.current) {
            const isNext = selectIndexes[0] === rowSize + 1;
            let refs = outerRefs.current;
            const isSecond = isDouble && isNext;
            const isFirst = isDouble && !isNext;
            const firstEndIndex = Math.floor(_reducer.sizeMap[calendarState].double[0] / 2);
            // Определяем какую часть сдвоенного календаря взять
            if (isFirst) {
                refs = outerRefs.current.slice(0, firstEndIndex);
            } else if (isSecond) {
                refs = outerRefs.current.slice(firstEndIndex, _reducer.sizeMap[calendarState].double[1]);
            }
            const refsList = refs?.flatMap((items)=>items.filter(Boolean));
            // Если в месяце нет хотя бы одной none-disabled даты значит весь месяц выключен.
            const hasSomeEnabledDay = refsList.some((item)=>!isAriaDisableItem(item));
            const isDisabledNextMonth = hasDisabledMonths({
                item: refsList[refsList.length - 1],
                key: 'next'
            });
            const isDisabledPreviousMonth = hasDisabledMonths({
                item: refsList[0],
                key: 'previous'
            });
            // Если следующий/предыдущий месяц находится за границами мин/макс, то переходить на него
            // с текущего выключенного, нет смысла.
            if (!hasSomeEnabledDay && (isDisabledNextMonth || isDisabledPreviousMonth)) {
                const inverseHandle = isNext ? onPrev : onNext;
                inverseHandle();
                setSelectIndexes(currentIndexes.current);
                // Если индексы равны, значит мы остались на текущей дате по причине, того
                // что в следующем(-их)/предыдущем(-их) месяце нет доступных дат
                setIsOutOfMinMaxRange(true);
                return;
            }
            if (!hasSomeEnabledDay) {
                const handle = isNext ? onNext : onPrev;
                const startColumnIndex = getCorrectColumnIndex({
                    refs,
                    rowSize: isDouble ? refs.length - 1 : rowSize,
                    isNext
                });
                handle();
                setSelectIndexes([
                    selectIndexes[0],
                    startColumnIndex
                ]);
                return;
            }
        }
        const [newRowIndex, newColumnIndex] = getCorrectIndexes(outerRefs, selectIndexes, rowSize, columnSize, withShiftState.current, currentIndexes.current, calendarState);
        if (selectIndexes[0] === newRowIndex && selectIndexes[1] === newColumnIndex) {
            return;
        }
        /**
         * Изменение состояния необходимо сделать здесь, т.к.
         * требуется дождаться обновление DOM и outerRefs
         */ setSelectIndexes([
            newRowIndex,
            newColumnIndex
        ]);
    }, [
        onPrev,
        onNext,
        selectIndexes,
        rowSize,
        columnSize,
        withShiftState,
        currentIndexes,
        isDouble
    ]);
    const isKeyboardNavigation = (0, _react.useRef)(false);
    (0, _react.useLayoutEffect)(()=>{
        if (!isKeyboardNavigation.current) {
            return;
        }
        const [rowIndex, columnIndex] = selectIndexes;
        const item = outerRefs?.current?.[rowIndex]?.[columnIndex];
        if (item) {
            isKeyboardNavigation.current = false;
            item.focus();
        }
    }, [
        selectIndexes,
        outerRefs
    ]);
    const onKeyDown = (0, _react.useCallback)((event)=>{
        isKeyboardNavigation.current = true;
        setIsOutOfMinMaxRange(false);
        const { keyCode, shiftKey: withShift } = event;
        const [currentRowIndex, currentColumnIndex] = selectIndexes;
        let newRowIndex = currentRowIndex;
        let newColumnIndex = currentColumnIndex;
        const minColumnIndex = 0;
        const minRowIndex = 0;
        const prevRowIndex = currentRowIndex - 1;
        const nextRowIndex = currentRowIndex + 1;
        const prevColumnIndex = currentColumnIndex - 1;
        const nextColumnIndex = currentColumnIndex + 1;
        currentIndexes.current = [
            currentRowIndex,
            currentColumnIndex
        ];
        const positionState = {
            refs: outerRefs,
            rowSize,
            columnSize,
            minColumnIndex,
            defaultState: [
                currentRowIndex,
                currentColumnIndex
            ]
        };
        switch(keyCode){
            case _Calendartypes.Keys.pageUp:
                {
                    event.preventDefault();
                    const isDisabledPreviousMonth = hasDisabledMonths({
                        item: outerRefs.current[currentRowIndex][currentColumnIndex],
                        key: 'previous'
                    });
                    if (isDisabledPreviousMonth) {
                        setIsOutOfMinMaxRange(true);
                        break;
                    }
                    onPrev(withShift);
                    break;
                }
            case _Calendartypes.Keys.pageDown:
                {
                    event.preventDefault();
                    const isDisabledNextMonth = hasDisabledMonths({
                        item: outerRefs.current[currentRowIndex][currentColumnIndex],
                        key: 'next'
                    });
                    if (isDisabledNextMonth) {
                        setIsOutOfMinMaxRange(true);
                        break;
                    }
                    onNext(withShift);
                    break;
                }
            case _Calendartypes.Keys.home:
                {
                    event.preventDefault();
                    newColumnIndex = minColumnIndex;
                    if (isVisible(outerRefs, newRowIndex, newColumnIndex)) {
                        break;
                    }
                    newColumnIndex = minColumnIndex - 1;
                    break;
                }
            case _Calendartypes.Keys.end:
                {
                    event.preventDefault();
                    newColumnIndex = columnSize;
                    if (isVisible(outerRefs, newRowIndex, newColumnIndex)) {
                        break;
                    }
                    newColumnIndex = columnSize + 1;
                    break;
                }
            case _Calendartypes.Keys.left:
                {
                    event.preventDefault();
                    newRowIndex = prevColumnIndex < minColumnIndex ? prevRowIndex : currentRowIndex;
                    newColumnIndex = prevColumnIndex < minColumnIndex ? columnSize : prevColumnIndex;
                    withShiftState.current = withShift;
                    const isCurrentDateDisabledArrowLeft = hasDisabledArrowKey({
                        refs: outerRefs,
                        payload: [
                            currentRowIndex,
                            currentColumnIndex
                        ],
                        key: 'left'
                    });
                    setIsOutOfMinMaxRange(isCurrentDateDisabledArrowLeft);
                    if (isCurrentDateDisabledArrowLeft) {
                        newRowIndex = currentRowIndex;
                        newColumnIndex = currentColumnIndex;
                        break;
                    }
                    // INFO: Для навигации только по доступным дням
                    if (withShift) {
                        const [rowIndex, columnIndex] = getPreviousCorrectPosition({
                            ...positionState,
                            newRowIndex,
                            newColumnIndex
                        });
                        // Если индексы равны, значит мы остались на текущей дате по причине, того
                        // что в следующем(-их)/предыдущем(-их) месяце нет доступных дат
                        setIsOutOfMinMaxRange(rowIndex === currentRowIndex && currentColumnIndex === columnIndex);
                        newRowIndex = rowIndex;
                        newColumnIndex = columnIndex;
                    }
                    if (isVisible(outerRefs, newRowIndex, newColumnIndex)) {
                        break;
                    }
                    if (!isDouble || getDoubleCalendarSide(currentRowIndex, calendarState) === 'first') {
                        if (isCurrentDateDisabledArrowLeft) {
                            newRowIndex = currentRowIndex;
                            newColumnIndex = currentColumnIndex;
                            break;
                        }
                        onPrev();
                    }
                    newRowIndex = minRowIndex - 1;
                    break;
                }
            case _Calendartypes.Keys.up:
                {
                    event.preventDefault();
                    newRowIndex = prevRowIndex < minRowIndex ? rowSize : prevRowIndex;
                    withShiftState.current = withShift;
                    const isCurrentDateDisabledArrowUp = hasDisabledArrowKey({
                        refs: outerRefs,
                        payload: [
                            currentRowIndex,
                            currentColumnIndex
                        ],
                        key: 'up'
                    });
                    setIsOutOfMinMaxRange(isCurrentDateDisabledArrowUp);
                    if (isCurrentDateDisabledArrowUp) {
                        newRowIndex = currentRowIndex;
                        newColumnIndex = currentColumnIndex;
                        break;
                    }
                    if (withShift) {
                        const item = outerRefs.current[newRowIndex][newColumnIndex];
                        const isNextDateDisabledArrowUp = !!item && hasDisabledArrowKey({
                            refs: outerRefs,
                            payload: [
                                newRowIndex,
                                newColumnIndex
                            ],
                            key: 'up'
                        });
                        const [rowIndex, columnIndex] = getPreviousCorrectPosition({
                            ...positionState,
                            newRowIndex,
                            newColumnIndex
                        });
                        // Если индексы равны, значит мы остались на текущей дате по причине, того
                        // что в следующем(-их)/предыдущем(-их) месяце нет доступных дат
                        const haseCurrentPosition = rowIndex === currentRowIndex && currentColumnIndex === columnIndex;
                        setIsOutOfMinMaxRange(isNextDateDisabledArrowUp || haseCurrentPosition);
                        newRowIndex = rowIndex;
                        newColumnIndex = columnIndex;
                    }
                    if (isVisible(outerRefs, newRowIndex, newColumnIndex)) {
                        break;
                    }
                    if (!isDouble || getDoubleCalendarSide(currentRowIndex, calendarState) === 'first') {
                        if (isCurrentDateDisabledArrowUp) {
                            newRowIndex = currentRowIndex;
                            newColumnIndex = currentColumnIndex;
                            break;
                        }
                        onPrev();
                    }
                    newRowIndex = minRowIndex - 1;
                    break;
                }
            case _Calendartypes.Keys.right:
                {
                    event.preventDefault();
                    newRowIndex = nextColumnIndex > columnSize ? nextRowIndex : currentRowIndex;
                    newColumnIndex = nextColumnIndex > columnSize ? minColumnIndex : nextColumnIndex;
                    withShiftState.current = withShift;
                    const isCurrentDateDisabledArrowRight = hasDisabledArrowKey({
                        refs: outerRefs,
                        payload: [
                            currentRowIndex,
                            currentColumnIndex
                        ],
                        key: 'right'
                    });
                    setIsOutOfMinMaxRange(isCurrentDateDisabledArrowRight);
                    if (isCurrentDateDisabledArrowRight) {
                        newRowIndex = currentRowIndex;
                        newColumnIndex = currentColumnIndex;
                        break;
                    }
                    if (withShift) {
                        const [rowIndex, columnIndex] = getNextCorrectPosition({
                            ...positionState,
                            newRowIndex,
                            newColumnIndex
                        });
                        // Если индексы равны, значит мы остались на текущей дате по причине, того
                        // что в следующем(-их)/предыдущем(-их) месяце нет доступных дат
                        setIsOutOfMinMaxRange(rowIndex === currentRowIndex && currentColumnIndex === columnIndex);
                        newRowIndex = rowIndex;
                        newColumnIndex = columnIndex;
                    }
                    if (isVisible(outerRefs, newRowIndex, newColumnIndex)) {
                        break;
                    }
                    if (!isDouble || getDoubleCalendarSide(currentRowIndex, calendarState) === 'second') {
                        if (isCurrentDateDisabledArrowRight) {
                            newRowIndex = currentRowIndex;
                            newColumnIndex = currentColumnIndex;
                            break;
                        }
                        onNext();
                    }
                    newRowIndex = rowSize + 1;
                    break;
                }
            case _Calendartypes.Keys.down:
                {
                    event.preventDefault();
                    newRowIndex = nextRowIndex > rowSize ? minRowIndex : nextRowIndex;
                    withShiftState.current = withShift;
                    const isCurrentDateDisabledArrowDown = hasDisabledArrowKey({
                        refs: outerRefs,
                        payload: [
                            currentRowIndex,
                            currentColumnIndex
                        ],
                        key: 'down'
                    });
                    setIsOutOfMinMaxRange(isCurrentDateDisabledArrowDown);
                    if (isCurrentDateDisabledArrowDown) {
                        newRowIndex = currentRowIndex;
                        newColumnIndex = currentColumnIndex;
                        break;
                    }
                    if (withShift) {
                        const item = outerRefs.current[newRowIndex][newColumnIndex];
                        const isNextDateDisabledArrowUp = !!item && hasDisabledArrowKey({
                            refs: outerRefs,
                            payload: [
                                newRowIndex,
                                newColumnIndex
                            ],
                            key: 'down'
                        });
                        const [rowIndex, columnIndex] = getNextCorrectPosition({
                            ...positionState,
                            newRowIndex,
                            newColumnIndex
                        });
                        // Если индексы равны, значит мы остались на текущей дате по причине, того
                        // что в следующем(-их)/предыдущем(-их) месяце нет доступных дат
                        const haseCurrentPosition = rowIndex === currentRowIndex && currentColumnIndex === columnIndex;
                        setIsOutOfMinMaxRange(isNextDateDisabledArrowUp || haseCurrentPosition);
                        newRowIndex = rowIndex;
                        newColumnIndex = columnIndex;
                    }
                    if (isVisible(outerRefs, newRowIndex, newColumnIndex)) {
                        break;
                    }
                    if (!isDouble || getDoubleCalendarSide(currentRowIndex, calendarState) === 'second') {
                        if (isCurrentDateDisabledArrowDown) {
                            newRowIndex = currentRowIndex;
                            newColumnIndex = currentColumnIndex;
                            break;
                        }
                        onNext();
                    }
                    newRowIndex = rowSize + 1;
                    break;
                }
            case _Calendartypes.Keys.enter:
            case _Calendartypes.Keys.space:
                {
                    outerRefs.current?.[newRowIndex]?.[newColumnIndex].click();
                    break;
                }
            default:
                return;
        }
        setSelectIndexes([
            newRowIndex,
            newColumnIndex
        ]);
    }, [
        selectIndexes,
        outerRefs,
        rowSize,
        columnSize,
        onNext,
        onPrev,
        isDouble
    ]);
    return [
        selectIndexes,
        onKeyDown,
        setSelectIndexes,
        outerRefs,
        isOutOfMinMaxRange
    ];
};
