/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { getEventPath, isDefined } from '@tylertech/forge-core';
import { CALENDAR_MENU_CONSTANTS } from './calendar-menu-constants';
export class CalendarMenuFoundation {
    constructor(_adapter) {
        this._adapter = _adapter;
        this._animationType = 'scale';
        this._columnCount = 3;
        this._focusedIndex = 0;
        this._list = false; // This will be used to manage focus in a list view
        this._open = false;
        this._options = [];
        this._preventFocus = false;
        this._sliceEnd = -1;
        this._sliceStart = -1;
        this._clickListener = evt => this._onClick(evt);
        this._intersectionObserverCallback = (entries) => this._onIntersection(entries);
    }
    initialize() {
        this._applyAnimationType();
        this._applyPreventFocus();
        this._adapter.setHostAttribute('exportparts', Object.values(CALENDAR_MENU_CONSTANTS.parts).join(', '));
        this._adapter.toggleHostAttribute('hidden', true);
        this._adapter.registerClickListener(this._clickListener);
    }
    disconnect() {
        this._adapter.unregisterClickListener(this._clickListener);
        this._adapter.unregisterIntersectionObserver();
    }
    openAsGrid(options, setFocus) {
        this._list = false;
        this._options = options;
        this._focusedIndex = 0;
        this._setColumnCount();
        this._adapter.setOpenAsGrid(options, this._focusedIndex, setFocus, this._preventFocus, this._open);
        this._setGridListeners();
        this._open = true;
        this._emitFocusChangeEvent();
    }
    openAsList(options, setFocus) {
        this._list = true;
        this._options = options;
        const currentSlice = this._getOptionSliceIncludingSelected();
        this._focusedIndex = Math.max(options.findIndex(o => o.selected), this._sliceStart);
        this._adapter.setOpenAsList(currentSlice, this._focusedIndex - this._sliceStart, setFocus, this._preventFocus, this._open);
        this._setListListeners();
        this._open = true;
        this._emitFocusChangeEvent();
    }
    close() {
        this._open = false;
        this._options = [];
        this._adapter.setClosed();
    }
    animateIn(options, direction, setFocus) {
        const callback = () => {
            this._setFocusFromStoredIndex(setFocus);
        };
        if (direction === 'left') {
            this._adapter.animateInFromLeft(options, callback);
        }
        else {
            this._adapter.animateInFromRight(options, callback);
        }
        this._options = options;
        this._setColumnCount();
    }
    selectFocusedItem() {
        var _a;
        this._adapter.emitHostEvent(CALENDAR_MENU_CONSTANTS.events.SELECT, (_a = this._options[this._focusedIndex]) === null || _a === void 0 ? void 0 : _a.value);
    }
    moveFocusBackward() {
        if (this._list) {
            return true;
        }
        const index = this._focusedIndex - 1;
        const indexIsInView = index >= 0 && index % this._columnCount !== this._columnCount - 1;
        this._focusedIndexAfterAnimation = indexIsInView ? undefined : index + this._columnCount;
        return this._tryMoveFocus(index, indexIsInView);
    }
    moveFocusDown() {
        if (this._list) {
            const index = Math.min(this._focusedIndex += 1, this._options.length - 1);
            this._tryMoveFocus(index, true);
        }
        else {
            const index = this._focusedIndex + this._columnCount;
            const indexIsInView = index < this._options.length;
            this._focusedIndexAfterAnimation = indexIsInView ? undefined : index % this._columnCount;
            this._tryMoveFocus(index, indexIsInView);
        }
    }
    moveFocusForward() {
        if (this._list) {
            return true;
        }
        const index = this._focusedIndex + 1;
        const indexIsInView = index < this._options.length && index % this._columnCount !== 0;
        this._focusedIndexAfterAnimation = indexIsInView ? undefined : index - this._columnCount;
        return this._tryMoveFocus(index, indexIsInView);
    }
    moveFocusUp() {
        if (this._list) {
            const index = Math.max(this._focusedIndex -= 1, 0);
            this._tryMoveFocus(index, true);
        }
        else {
            const index = this._focusedIndex - this._columnCount;
            const indexIsInView = index >= 0;
            this._focusedIndexAfterAnimation = indexIsInView ? undefined : this._options.length + index;
            this._tryMoveFocus(index, indexIsInView);
        }
    }
    _tryMoveFocus(index, indexIsInView) {
        var _a;
        if (indexIsInView && !((_a = this._options[index]) === null || _a === void 0 ? void 0 : _a.disabled)) {
            this._focusedIndex = index;
            this._adapter.setFocusAtIndex(this._list ? this._focusedIndex - this._sliceStart : this._focusedIndex, true, this._preventFocus);
            this._emitFocusChangeEvent();
        }
        return indexIsInView;
    }
    _setFocusFromStoredIndex(setFocus) {
        var _a, _b;
        if (this._focusedIndexAfterAnimation === undefined) {
            return;
        }
        if (this._options[this._focusedIndexAfterAnimation] && !((_a = this._options[this._focusedIndexAfterAnimation]) === null || _a === void 0 ? void 0 : _a.disabled)) {
            this._focusedIndex = this._focusedIndexAfterAnimation;
        }
        else if ((_b = this._options[0]) === null || _b === void 0 ? void 0 : _b.disabled) {
            this._focusedIndex = this._options.findIndex(o => !o.disabled);
        }
        else {
            this._focusedIndex = this._options.length - this._options.slice().reverse().findIndex(o => !o.disabled) - 1;
        }
        this._focusedIndexAfterAnimation = undefined;
        this._adapter.setFocusAtIndex(this._focusedIndex, setFocus, this._preventFocus);
        this._emitFocusChangeEvent();
    }
    _onClick(evt) {
        const element = getEventPath(evt).find(p => p.classList && p.classList.contains(CALENDAR_MENU_CONSTANTS.classes.ITEM));
        const value = element === null || element === void 0 ? void 0 : element.getAttribute(CALENDAR_MENU_CONSTANTS.attributes.DATA_VALUE);
        if (isDefined(value)) {
            this._adapter.emitHostEvent(CALENDAR_MENU_CONSTANTS.events.SELECT, +value);
        }
    }
    _onIntersection(entries) {
        if (this._sliceStart > 0 && entries.some(e => e.target.id === CALENDAR_MENU_CONSTANTS.ids.SCROLL_SPY_TOP && e.isIntersecting)) {
            this._adapter.appendListItemsToStart(this._getPreviousOptionsSlice());
        }
        if (this._sliceEnd < this._options.length && entries.some(e => e.target.id === CALENDAR_MENU_CONSTANTS.ids.SCROLL_SPY_BOTTOM && e.isIntersecting)) {
            this._adapter.appendListItemsToEnd(this._getNextOptionsSlice());
        }
    }
    _setColumnCount() {
        this._columnCount = this._options.length >= CALENDAR_MENU_CONSTANTS.numbers.LARGE_ITEM_SET ? CALENDAR_MENU_CONSTANTS.numbers.FOUR_COL : CALENDAR_MENU_CONSTANTS.numbers.THREE_COL;
    }
    _emitFocusChangeEvent() {
        var _a;
        this._adapter.emitHostEvent(CALENDAR_MENU_CONSTANTS.events.FOCUS_CHANGE, (_a = this._options[this._focusedIndex]) === null || _a === void 0 ? void 0 : _a.value);
    }
    _setGridListeners() {
        this._adapter.unregisterIntersectionObserver();
    }
    _setListListeners() {
        this._adapter.unregisterIntersectionObserver();
        this._adapter.registerIntersectionObserver(this._intersectionObserverCallback);
    }
    _getOptionSliceIncludingSelected() {
        if (this._options.length <= CALENDAR_MENU_CONSTANTS.numbers.LIST_SLICE_SIZE) {
            this._sliceStart = 0;
            this._sliceEnd = this._options.length;
            return this._options.slice();
        }
        const selectedIndex = Math.max(this._options.findIndex(o => o.selected), 0);
        let start = Math.max(Math.floor(selectedIndex - CALENDAR_MENU_CONSTANTS.numbers.LIST_SLICE_SIZE / 2), 0);
        let end = start + CALENDAR_MENU_CONSTANTS.numbers.LIST_SLICE_SIZE;
        if (end > this._options.length) {
            start = this._options.length - 1 - CALENDAR_MENU_CONSTANTS.numbers.LIST_SLICE_SIZE;
            end = this._options.length;
        }
        this._sliceStart = start;
        this._sliceEnd = end;
        return this._options.slice(this._sliceStart, this._sliceEnd);
    }
    _getNextOptionsSlice() {
        const newEnd = Math.min(this._sliceEnd + CALENDAR_MENU_CONSTANTS.numbers.LIST_SLICE_SIZE, this._options.length);
        const slice = this._options.slice(this._sliceEnd, newEnd);
        this._sliceEnd = newEnd;
        return slice;
    }
    _getPreviousOptionsSlice() {
        const newStart = Math.max(this._sliceStart - CALENDAR_MENU_CONSTANTS.numbers.LIST_SLICE_SIZE, 0);
        const slice = this._options.slice(newStart, this._sliceStart);
        this._sliceStart = newStart;
        return slice;
    }
    _applyAnimationType() {
        this._adapter.setHostAttribute(CALENDAR_MENU_CONSTANTS.attributes.ANIMATION_TYPE, this._animationType);
        this._adapter.setAnimationType(this._animationType);
    }
    _applyPreventFocus() {
        this._adapter.toggleHostAttribute(CALENDAR_MENU_CONSTANTS.attributes.PREVENT_FOCUS, this._preventFocus);
        this._adapter.setFocusAtIndex(this._focusedIndex, false, this._preventFocus);
    }
    get animationType() {
        return this._animationType;
    }
    set animationType(value) {
        if (this._animationType !== value) {
            this._animationType = value;
            this._applyAnimationType();
        }
    }
    get preventFocus() {
        return this._preventFocus;
    }
    set preventFocus(value) {
        if (this._preventFocus !== value) {
            this._preventFocus = value;
            this._applyPreventFocus();
        }
    }
}
