import { useMemo } from 'react';
import { generateDateByUTCValue, getDateUTCValue, sameDate, sameYear, sameYearSameMonth } from '../../utils';
import { viewYears } from '../consts';
import { getNextDate, isDay, isExact, isFrom, isMonth, isRange, isTo, isYear } from '../utils';
import { useBoundaries } from './useBoundaries';
import { useSelectedDate } from './useSelectedDate';
/**
 * Main hook for doing calendar calculations
 * Calculates for all time unit modes: day, month and year
 * Depends on useBoundaries and useSelectedDate to do initial setup
 */
export const useCalendar = ({ boundaries, selectedDates, mode = 'exact', timeUnitMode, viewDate, fixedNumberOfWeeks, }) => {
    const boundariesProcessed = useBoundaries({ boundaries });
    const selectedProcessed = useSelectedDate({ selectedDates, mode });
    return useMemo(() => {
        const { maxDateTime, minDateTime } = boundariesProcessed;
        const { currentDateTime, maxSelectedDateTime, minSelectedDateTime } = selectedProcessed;
        const { utcYear: viewDateYear, utcMonth: viewDateMonth } = getDateUTCValue(viewDate);
        let calendarFirstDay;
        let calendarLastDay;
        let nextFirstDay;
        let prevLastDay;
        let firstDay;
        let lastDay;
        if (isDay(timeUnitMode)) {
            firstDay = generateDateByUTCValue(viewDateYear, viewDateMonth, 1);
            lastDay = generateDateByUTCValue(viewDateYear, viewDateMonth + 1, 0); // lastDay
            nextFirstDay = generateDateByUTCValue(viewDateYear, viewDateMonth + 1, 1);
            prevLastDay = generateDateByUTCValue(viewDateYear, viewDateMonth, 0);
            const weekdayFirst = firstDay.getUTCDay();
            const weekdayLast = lastDay.getUTCDay();
            // Monday preceding the month (or first day of the month if it's on a Monday)
            calendarFirstDay =
                weekdayFirst === 0
                    ? new Date(Date.UTC(viewDateYear, viewDateMonth, 2 - 7))
                    : new Date(Date.UTC(viewDateYear, viewDateMonth, 2 - weekdayFirst));
            // Sunday after the month (or last day of the month if it's on a Sunday)
            calendarLastDay =
                weekdayLast === 0
                    ? new Date(Date.UTC(viewDateYear, viewDateMonth + 1, 0))
                    : new Date(Date.UTC(viewDateYear, viewDateMonth + 1, 8 - weekdayLast));
            // If calendar must contain constant number of weeks (6 weeks)
            // Missing weeks will be added to the end of the month
            const numberOfWeeks = Math.round((+calendarLastDay - +calendarFirstDay) / (7 * 24 * 60 * 60 * 1000));
            if (fixedNumberOfWeeks) {
                for (let week = numberOfWeeks; week < 6; week++) {
                    calendarLastDay = generateDateByUTCValue(calendarLastDay.getUTCFullYear(), calendarLastDay.getUTCMonth(), calendarLastDay.getUTCDate() + 7);
                }
            }
        }
        else if (isMonth(timeUnitMode)) {
            calendarFirstDay = firstDay = generateDateByUTCValue(viewDateYear, 0, 1);
            calendarLastDay = lastDay = generateDateByUTCValue(viewDateYear, 11, 31);
            nextFirstDay = generateDateByUTCValue(viewDateYear + 1, 0, 1);
            prevLastDay = generateDateByUTCValue(viewDateYear - 1, 11, 31);
        }
        else {
            const calendarStartYear = new Date().getUTCFullYear() - viewYears / 2;
            const diffYears = calendarStartYear - viewDateYear;
            const startYear = calendarStartYear - Math.ceil(diffYears / viewYears) * viewYears;
            const endYear = startYear + viewYears - 1;
            if (startYear === 1753)
                calendarFirstDay = firstDay = generateDateByUTCValue(startYear, 0, 2);
            else
                calendarFirstDay = firstDay = generateDateByUTCValue(startYear, 0, 1);
            calendarLastDay = lastDay = generateDateByUTCValue(endYear, 11, 31);
            nextFirstDay = generateDateByUTCValue(endYear + 1, 0, 1);
            prevLastDay = generateDateByUTCValue(startYear - 1, 11, 31);
        }
        const items = [];
        // Loops between first and last date
        let dateItem = calendarFirstDay;
        do {
            const isHidden = dateItem < firstDay || dateItem >= lastDay;
            const isToday = sameDate(dateItem, new Date());
            const { utcYear: minDateYear, utcMonth: minDateMonth } = getDateUTCValue(minDateTime);
            let minUnitModeDate = minDateTime;
            if (isMonth(timeUnitMode))
                // Need to compare with the first day of the month to decide if the month is disabled in the month overview
                minUnitModeDate = generateDateByUTCValue(minDateYear, minDateMonth, 1);
            else if (isYear(timeUnitMode))
                // Need to compare with the first day of the year to decide if the year is disabled in the year overview
                minUnitModeDate = generateDateByUTCValue(minDateYear, 0, 1);
            const isDisabled = dateItem < minUnitModeDate || dateItem > maxDateTime ? true : false;
            // The calendar only select a single date
            const isExactSelected = isExact(mode) && minSelectedDateTime && +dateItem === +minSelectedDateTime;
            // The calendar selects a range
            const inRangeLow = minSelectedDateTime && dateItem >= minSelectedDateTime;
            const inRangeHigh = maxSelectedDateTime && dateItem <= maxSelectedDateTime;
            const inRange = !isExact(mode) &&
                ((!isDisabled && inRangeLow && (!maxSelectedDateTime || inRangeHigh)) ||
                    (inRangeHigh && (!minSelectedDateTime || inRangeLow)));
            const isRangeSelected = isRange(mode) && inRange;
            const isFromSelected = isFrom(mode) && inRangeLow;
            const isToSelected = isTo(mode) && inRangeHigh;
            const isSelected = !!(isExactSelected || isFromSelected || isToSelected || isRangeSelected);
            let maxSelectedCheck = maxSelectedDateTime;
            let minSelectedCheck = minSelectedDateTime;
            if (isYear(timeUnitMode)) {
                if (maxSelectedCheck)
                    maxSelectedCheck = generateDateByUTCValue(maxSelectedCheck.getUTCFullYear(), 0, 1);
                if (minSelectedCheck)
                    minSelectedCheck = generateDateByUTCValue(minSelectedCheck.getUTCFullYear(), 0, 1);
            }
            if (isMonth(timeUnitMode)) {
                if (maxSelectedCheck)
                    maxSelectedCheck = generateDateByUTCValue(maxSelectedCheck.getUTCFullYear(), maxSelectedCheck.getUTCMonth(), 1);
                if (minSelectedCheck)
                    minSelectedCheck = generateDateByUTCValue(minSelectedCheck.getUTCFullYear(), minSelectedCheck.getUTCMonth(), 1);
            }
            const boundary = !isTo(mode) && sameDate(dateItem, minSelectedCheck);
            let current;
            if (isDay(timeUnitMode))
                current = sameDate(dateItem, currentDateTime);
            else if (isMonth(timeUnitMode))
                current = sameYearSameMonth(dateItem, currentDateTime);
            else
                current = sameYear(dateItem, currentDateTime);
            items.push({
                date: dateItem,
                isHidden,
                isToday,
                isDisabled: boundary ? false : isDisabled,
                isSelected,
                boundary,
                current,
            });
            dateItem = getNextDate(dateItem, timeUnitMode);
        } while (dateItem <= calendarLastDay);
        // Disables next/prev buttons
        const prevDisabled = minDateTime > prevLastDay;
        const nextDisabled = maxDateTime < nextFirstDay;
        return {
            items,
            prevDisabled,
            nextDisabled,
        };
    }, [boundaries, selectedDates, mode, timeUnitMode, viewDate]);
};
//# sourceMappingURL=useCalendar.js.map