/**
 * @license
 * Copyright 2023 Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { isDefined, getEventPath } from '@tylertech/forge-core';
import { BaseSelectFoundation } from '../core';
import { SELECT_CONSTANTS } from './select-constants';
import { FIELD_CONSTANTS } from '../../field/field-constants';
/**
 * The foundation class behind the `<forge-select>` component.
 */
export class SelectFoundation extends BaseSelectFoundation {
    constructor(adapter) {
        super(adapter);
        this._shape = 'default';
        this._label = '';
        this._disabled = false;
        this._invalid = false;
        this._required = false;
        this._floatLabelType = 'auto';
        this._density = 'default';
        this._isInitialized = false;
        this._leadingChangeListener = evt => this._onLeadingSlotChanged(evt);
        this._addonEndChangeListener = evt => this._onAddonEndSlotChanged(evt);
        this._targetWidthCallback = () => this._adapter.getTargetWidth();
        this._mousedownListener = evt => this._onMouseDown(evt);
    }
    initialize() {
        super.initialize();
        super.initializeTarget();
        this._initializeLabel();
        this._initializeAccessibility();
        this._adapter.setPlaceholderText(this._placeholder);
        this._applyDensity();
        this._setShapeType();
        this._detectLeadingElement();
        this._detectAddonEndContent();
        this._adapter.addMouseDownListener(this._mousedownListener);
        this._adapter.setLeadingListener(this._leadingChangeListener);
        this._adapter.setAddonEndListener(this._addonEndChangeListener);
        if (this._disabled) {
            this._adapter.setDisabled(true);
        }
        if (this._invalid) {
            this._adapter.setInvalid(true);
        }
        this._isInitialized = true;
    }
    disconnect() {
        super.disconnect();
        this._adapter.removeMouseDownListener(this._mousedownListener);
        this._adapter.removeLeadingListener(this._leadingChangeListener);
        this._adapter.removeAddonEndListener(this._leadingChangeListener);
        if (this._floatingLabelInstance) {
            this._floatingLabelInstance.destroy();
            this._floatingLabelInstance = undefined;
        }
    }
    _initializeLabel() {
        this._adapter.initializeLabel();
        this._updateLabel();
        this._floatingLabelInstance = this._adapter.initializeFloatingLabel();
        if (this._floatLabelType === 'always') {
            this._floatLabel(true);
        }
        else {
            const hasText = !!this._getSelectedText();
            const hasPlaceholder = !!this._placeholder;
            this._floatLabel(hasText || hasPlaceholder);
        }
    }
    _destroyLabel() {
        if (this._floatingLabelInstance) {
            this._floatingLabelInstance.destroy();
            this._floatingLabelInstance = undefined;
        }
    }
    _initializeValue() {
        super._initializeValue();
        if (this._selectedValues.length) {
            this._floatLabel(true);
        }
    }
    _initializeAccessibility() {
        if (this._required) {
            this._adapter.setHostAttribute('aria-required', 'true');
        }
        if (this._disabled) {
            this._adapter.setHostAttribute('aria-disabled', 'true');
        }
        if (this._invalid) {
            this._adapter.setHostAttribute('aria-invalid', 'true');
        }
    }
    _onMouseDown(evt) {
        const isElementWithinSelf = this._adapter.isWithinSelf(evt.target);
        if (isElementWithinSelf) {
            evt.preventDefault();
        }
    }
    /** Called when the `slotchange` event fires on the "leading" slot element. */
    _onLeadingSlotChanged(evt) {
        this._detectLeadingElement();
    }
    /** Called when the `slotchange` event fires on the "addon-end" slot element. */
    _onAddonEndSlotChanged(evt) {
        this._detectAddonEndContent();
    }
    /** Updates the component state based on the existance of elements within the "leading" slot. */
    _detectLeadingElement() {
        if (this._adapter.hasLeadingElement()) {
            this._adapter.addRootClass(FIELD_CONSTANTS.classes.LEADING);
        }
        else {
            this._adapter.removeRootClass(FIELD_CONSTANTS.classes.LEADING);
        }
    }
    _detectAddonEndContent() {
        if (this._adapter.hasAddonEndNodes()) {
            this._adapter.addRootClass(FIELD_CONSTANTS.classes.ADDON_END);
        }
        else {
            this._adapter.removeRootClass(FIELD_CONSTANTS.classes.ADDON_END);
        }
    }
    _onClick(evt) {
        if (this._disabled) {
            return;
        }
        // We ignore clicks events that originate from without our addon-end container
        const composedPath = getEventPath(evt);
        if (composedPath.find(el => el.classList && el.classList.contains(FIELD_CONSTANTS.classes.ADDON_END_CONTAINER))) {
            return;
        }
        this.setFocus();
        super._onClick(evt);
    }
    /** Handles receiving focus on the selected text element. */
    _onFocus(evt) {
        if (this._disabled) {
            return;
        }
        this._setFocused();
        super._onFocus(evt);
    }
    _floatLabel(value) {
        if (!this._floatingLabelInstance) {
            return;
        }
        if (value) {
            this._adapter.addRootClass(SELECT_CONSTANTS.classes.LABEL_FLOAT);
        }
        else {
            this._adapter.removeRootClass(SELECT_CONSTANTS.classes.LABEL_FLOAT);
        }
        if (this._floatLabelType === 'always') {
            value = true;
        }
        this._floatingLabelInstance.float(value, this._floatLabelType === 'always');
    }
    /** Handles losing focus on the selected text element. */
    _onBlur(evt) {
        super._onBlur(evt);
        this._setBlurred();
    }
    _onDismiss() {
        super._onDismiss();
        this._setBlurred();
    }
    _onDropdownScrollEnd() {
        this._adapter.emitHostEvent(SELECT_CONSTANTS.events.SCROLLED_BOTTOM);
    }
    /**
     * Handles selecting an item in the dropdown.
     * @param {ISelectOption} option The selected option.
     * @param {number} optionIndex The index of the selected option.
     */
    async _onSelect(option, optionIndex, closeDropdown = true) {
        const result = await super._onSelect(option, optionIndex, closeDropdown);
        if (result) {
            this._adapter.setSelectedText(this._getSelectedText());
            if (closeDropdown && !this._multiple) {
                this._adapter.setFocus();
            }
        }
        return result;
    }
    /** Updates the state of the component to contain focus. */
    _setFocused() {
        this._adapter.addRootClass(FIELD_CONSTANTS.classes.FOCUSED);
        this._floatLabel(true);
    }
    /** Updates the state of the component to not contain focus. */
    _setBlurred() {
        var _a;
        this._adapter.removeRootClass(FIELD_CONSTANTS.classes.FOCUSED);
        if (!this._selectedValues.length && !((_a = this._placeholder) === null || _a === void 0 ? void 0 : _a.length)) {
            this._floatLabel(false);
        }
    }
    /** Resets the state of the component to original values. */
    _reset() {
        var _a;
        super._reset();
        this._adapter.setSelectedText('');
        this._floatLabel(!((_a = this._placeholder) === null || _a === void 0 ? void 0 : _a.length));
    }
    _updateLabel() {
        if (this._adapter.hasLabel()) {
            this._adapter.setLabel(this._label);
        }
        if (this._label && this._density !== 'dense') {
            this._adapter.addRootClass(FIELD_CONSTANTS.classes.LABEL);
        }
        else {
            this._adapter.removeRootClass(FIELD_CONSTANTS.classes.LABEL);
        }
    }
    _applyValue(value) {
        super._applyValue(value);
        // Update the state of the component based on the existence of a selected value
        const text = this._getSelectedText();
        this._adapter.setSelectedText(text);
        if (!this._open) {
            this._floatLabel(!!text || !!this._placeholder);
        }
    }
    _applyDensity() {
        this._adapter.setRoomy(this._density === 'roomy');
        this._adapter.setDense(this._density === 'dense');
    }
    _setShapeType() {
        if (this._shape === 'rounded') {
            this._adapter.addRootClass(FIELD_CONSTANTS.classes.SHAPE_ROUNDED);
        }
        else {
            this._adapter.removeRootClass(FIELD_CONSTANTS.classes.SHAPE_ROUNDED);
        }
    }
    setFocus() {
        this._adapter.setFocus();
    }
    /** Gets/sets the label text. */
    get label() {
        return this._label;
    }
    set label(value) {
        if (this._label !== value) {
            this._label = value;
            this._updateLabel();
            if (this._label) {
                this._initializeLabel();
                this._adapter.setHostAttribute(SELECT_CONSTANTS.attributes.LABEL, this._label);
            }
            else {
                this._destroyLabel();
                this.required = false;
                this._adapter.removeHostAttribute(SELECT_CONSTANTS.attributes.LABEL);
            }
        }
    }
    get shape() {
        return this._shape;
    }
    set shape(value) {
        if (this._shape !== value) {
            this._shape = value;
            if (this._isInitialized) {
                this._setShapeType();
            }
            this._adapter.setHostAttribute(FIELD_CONSTANTS.attributes.SHAPE, this._shape);
        }
    }
    /** Gets/sets the disabled state. */
    get disabled() {
        return this._disabled;
    }
    set disabled(value) {
        if (this._disabled !== value) {
            this._disabled = value;
            this._adapter.setDisabled(this._disabled);
            this._initializeLabel();
        }
    }
    /** Gets/sets the invalid state. */
    get invalid() {
        return this._invalid;
    }
    set invalid(value) {
        if (this._invalid !== value) {
            this._invalid = value;
            this._adapter.setInvalid(this._invalid);
        }
    }
    /** Gets/sets the required state which controls the visibility of the asterisk in the label. */
    get required() {
        return this._required;
    }
    set required(value) {
        if (this._required !== value) {
            this._required = value;
            this._adapter.setRequired(this._required);
            this._adapter.toggleHostAttribute(FIELD_CONSTANTS.attributes.REQUIRED, this._required);
        }
    }
    /** Gets/sets the dense state. */
    get density() {
        return this._density;
    }
    set density(value) {
        if (this._density !== value) {
            this._density = value;
            this._applyDensity();
            this._adapter.setHostAttribute(FIELD_CONSTANTS.attributes.DENSITY, this._density.toString());
            this._initializeLabel(); // This ensures label is removed while dense
        }
    }
    /** Gets/sets the floating label type. */
    get floatLabelType() {
        return this._floatLabelType;
    }
    set floatLabelType(value) {
        if (this._floatLabelType !== value) {
            this._floatLabelType = value;
            this._floatLabel(this._floatLabelType === 'always' || !!this._placeholder);
            this._adapter.setHostAttribute(FIELD_CONSTANTS.attributes.FLOAT_LABEL_TYPE, isDefined(this._floatLabelType) ? this._floatLabelType.toString() : '');
        }
    }
    /** Gets/sets the placeholder text. */
    get placeholder() {
        return this._placeholder;
    }
    set placeholder(value) {
        if (this._placeholder !== value) {
            this._placeholder = value;
            this._adapter.setPlaceholderText(this._placeholder);
            this._initializeLabel();
        }
    }
}
