/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { DEFAULT_LIST_DROPDOWN_CONFIG } from './list-dropdown-constants';
import { getFlattenedOptions } from './list-dropdown-utils';
export class ListDropdownFoundation {
    constructor(_adapter, config) {
        this._adapter = _adapter;
        this._open = false;
        this._config = Object.assign(Object.assign({}, DEFAULT_LIST_DROPDOWN_CONFIG), Object.assign({}, config));
        this._selectListener = (value, id) => this._onSelect(value, id);
        this._closeListener = () => {
            if (typeof this._config.closeCallback === 'function') {
                this._config.closeCallback();
            }
        };
        this._scrollEndListener = () => {
            if (this._config.observeScroll && typeof this._config.scrollEndListener === 'function') {
                this._config.scrollEndListener();
            }
        };
    }
    destroy() {
        this._adapter.remove();
    }
    open() {
        if (!this._open) {
            this._open = true;
            this._adapter.open(this._config, this._selectListener, this._closeListener);
            window.requestAnimationFrame(() => {
                if (this._open) {
                    this.activateInitialOption();
                }
            });
            if (this._config.observeScroll && this._config.scrollEndListener) {
                this._adapter.setScrollBottomListener(this._scrollEndListener, this._config.observeScrollThreshold || 0);
            }
        }
    }
    async close() {
        if (this._open) {
            this._open = false;
            if (this._config.observeScroll && this._config.scrollEndListener) {
                this._adapter.removeScrollBottomListener(this._scrollEndListener);
            }
            await this._adapter.close();
        }
    }
    _onSelect(value, id) {
        if (typeof this._config.activeChangeCallback === 'function') {
            this._config.activeChangeCallback(id);
        }
        if (typeof this._config.selectCallback === 'function') {
            this._config.selectCallback(value);
        }
        if (this._open) {
            const sync = this._config.syncWidth === undefined ? true : false;
            // Keep the popup width at least the same as the target if the target size has now changed
            this._adapter.syncWidth(sync);
        }
    }
    getActiveOptionIndex() {
        return this._adapter.getActiveOptionIndex();
    }
    getActiveOption() {
        const index = this.getActiveOptionIndex();
        return index >= 0 ? this._nonDividerOptions[index] : undefined;
    }
    toggleOptionMultiple(index, isSelected) {
        return this._adapter.toggleOptionMultiple(index, isSelected);
    }
    activateSelectedOption() {
        this._adapter.activateSelectedOption(this._config);
    }
    activateFirstOption() {
        const index = this._nonDividerOptions.findIndex(o => !o.disabled);
        if (index !== -1) {
            this.activateOption(index);
        }
        return index;
    }
    activateLastOption() {
        const options = this._nonDividerOptions;
        const index = (options.length - 1) - options.findIndex(o => !o.disabled);
        if (index !== -1) {
            this.activateOption(index);
        }
        return index;
    }
    activateOption(index, animate) {
        return this._adapter.activateOption(index, this._config.activeChangeCallback, animate);
    }
    activateInitialOption() {
        if (typeof this._config.activeStartIndex === 'number' && this._nonDividerOptions[this._config.activeStartIndex]) {
            this.activateOption(this._config.activeStartIndex, false);
            this._adapter.scrollSelectedOptionIntoView(false);
        }
        else if (this._config.selectedValues && this._config.selectedValues.length) {
            this._adapter.scrollSelectedOptionIntoView(false);
        }
        else if (typeof this._config.visibleStartIndex === 'number' && this._nonDividerOptions[this._config.visibleStartIndex]) {
            this._adapter.scrollOptionIntoView(this._config.visibleStartIndex);
        }
    }
    setSelectedValues(values) {
        if (!Array.isArray(values)) {
            values = [values];
        }
        if (!this._config.multiple && values.length > 1) {
            values = [values[0]];
        }
        this._config.selectedValues = values;
        this._adapter.setSelectedValues(values, this._config.multiple);
    }
    clearActiveOption() {
        this._adapter.clearActiveOption();
    }
    setOptions(options) {
        this._config.options = options;
        if (!this._open) {
            return;
        }
        this._adapter.setOptions(this._config);
        this.activateInitialOption();
    }
    appendOptions(options) {
        this._config.options = [...this._config.options, ...options];
        if (!this._open) {
            return;
        }
        this._adapter.appendOptions(options, this._config);
    }
    get dropdownElement() {
        return this._adapter.dropdownElement;
    }
    scrollSelectedOptionIntoView(animate = true) {
        this._adapter.scrollSelectedOptionIntoView(animate);
    }
    setScrollBottomListener(listener, threshold) {
        this._config.scrollEndListener = listener;
        this._config.observeScroll = true;
        this._config.observeScrollThreshold = threshold;
        this._adapter.setScrollBottomListener(this._scrollEndListener, this._config.observeScrollThreshold || 0);
    }
    removeScrollBottomListener() {
        this._config.scrollEndListener = undefined;
        this._adapter.removeScrollBottomListener(this._scrollEndListener);
    }
    setBusyVisibility(isVisible) {
        if (!this._config.allowBusy) {
            return;
        }
        this._adapter.setBusyVisibility(isVisible);
    }
    handleKey(key) {
        switch (key) {
            case 'Enter':
            case 'NumpadEnter':
                const activeOptionIndex = this.getActiveOptionIndex();
                const activeOption = this._nonDividerOptions[activeOptionIndex];
                if (this._canSelectOption(activeOption)) {
                    const id = this._adapter.getActiveOptionIdByIndex(activeOptionIndex);
                    if (id) {
                        this._onSelect(activeOption.value, id);
                    }
                }
                break;
            case 'Up':
            case 'ArrowUp':
            case 'Down':
            case 'ArrowDown':
                const options = this._nonDividerOptions;
                if (options.length && options.every(o => !this._canSelectOption(o))) {
                    return;
                }
                const index = this._getNextActiveOptionIndex(key);
                this.activateOption(index);
                break;
            case 'Home':
                this.activateFirstOption();
                break;
            case 'End':
                this.activateLastOption();
                break;
        }
    }
    _canSelectOption(option) {
        return option && !option.disabled && !option.divider;
    }
    _getNextActiveOptionIndex(key) {
        let index = this._adapter.getActiveOptionIndex();
        if (index === -1) {
            index = this._adapter.getSelectedOptionIndex();
        }
        if (key === 'ArrowUp' || key === 'Up') {
            return this._getPreviousHighlightableOptionIndex(index, this._nonDividerOptions);
        }
        return this._getNextHighlightableOptionIndex(index, this._nonDividerOptions);
    }
    _getPreviousHighlightableOptionIndex(startIndex, options) {
        const index = startIndex <= 0 ? options.length - 1 : startIndex - 1;
        if (options[index].disabled) {
            return this._getPreviousHighlightableOptionIndex(index, options);
        }
        return index;
    }
    _getNextHighlightableOptionIndex(startIndex, options) {
        const index = startIndex === options.length - 1 ? 0 : startIndex + 1;
        if (options[index].disabled) {
            return this._getNextHighlightableOptionIndex(index, options);
        }
        return index;
    }
    get _flatOptions() {
        return getFlattenedOptions(this._config.options);
    }
    get _nonDividerOptions() {
        return this._flatOptions.filter(o => !o.divider);
    }
}
