/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { getEventPath, getShadowElement, isDefined, isNumber, Platform } from '@tylertech/forge-core';
import { STEP_CONSTANTS } from '../step/step-constants';
import { StepperUtils } from '../core/stepper-utils';
import { STEPPER_CONSTANTS } from './stepper-constants';
export class StepperFoundation {
    constructor(_adapter) {
        this._adapter = _adapter;
        this._steps = [];
        this._selectedIndex = 0;
        this._linear = false;
        this._alternative = false;
        this._layoutMode = 'fixed';
        this._layoutAlign = 'center';
        this._initialize = false;
        this._clickListener = event => this._handleClick(event);
        this._keyListener = event => this._onKeydown(event);
        this._stepFocusListener = () => this._onStepFocus();
        this._stepBlurListener = () => this._onStepBlur();
        this._stepExpandedContentFocusInListener = event => this._onStepExpandedContentFocusIn(event);
        this._stepExpandedContentFocusOutListener = event => this._onStepExpandedContentFocusOut(event);
        this._slotChangeListener = event => this._onSlotChange(event);
    }
    initialize() {
        this._adapter.initializeAccessibility();
        this._adapter.assignIndices();
        this._adapter.setLinearState(this._linear);
        this._adapter.setAlternativeState(this._alternative);
        this._adapter.setLayoutAlign(this._layoutAlign);
        this._adapter.setLayoutMode(this._layoutMode);
        this._adapter.addRootListener('click', this._clickListener);
        this._adapter.addHostListener('keydown', this._keyListener);
        this._adapter.assignFirstLastStep();
        this._adapter.addRootListener(STEP_CONSTANTS.events.EXPANDED_CONTENT_FOCUSIN, this._stepExpandedContentFocusInListener);
        this._adapter.addRootListener(STEP_CONSTANTS.events.EXPANDED_CONTENT_FOCUSOUT, this._stepExpandedContentFocusOutListener);
        this._applySelectedIndex();
        this._patchBrowser();
        this._adapter.setStepsListener('blur', this._stepBlurListener);
        this._adapter.setStepsListener('focus', this._stepFocusListener);
        if (!this._initialize) {
            this._adapter.addSlotChangeListener(this._slotChangeListener);
        }
        this._initialize = true;
    }
    disconnect() {
        this._adapter.removeRootListener('click', this._clickListener);
        this._adapter.removeHostListener('keydown', this._keyListener);
        this._adapter.removeSlotChangeListener(this._slotChangeListener);
        this._adapter.removeStepsListener('blur', this._stepBlurListener);
        this._adapter.removeStepsListener('focus', this._stepFocusListener);
    }
    /** The step configurations. */
    get steps() {
        return this._steps.map(s => (Object.assign({}, s))); // Shallow clone
    }
    set steps(value) {
        if (Array.isArray(value) && value.length > 0) {
            this._steps = [...value.map(s => (Object.assign({}, s)))];
        }
        else {
            this._steps = [];
            return;
        }
        this._renderConfiguration();
    }
    /** The active step index. */
    get selectedIndex() {
        return this._selectedIndex;
    }
    set selectedIndex(value) {
        if (!isDefined(value)) {
            value = 0;
        }
        else if (!isNumber(value)) {
            const v = Number(value);
            value = !isNaN(v) ? v : 0;
        }
        if (this._selectedIndex !== value) {
            this._selectedIndex = value;
            this._adapter.setHostAttribute(STEPPER_CONSTANTS.attributes.SELECTED_INDEX, this._selectedIndex.toString());
            this._applySelectedIndex();
        }
    }
    /** Whether the stepper is linear or non-linear. */
    get linear() {
        return this._linear;
    }
    set linear(value) {
        value = Boolean(value);
        if (this._linear !== value) {
            this._linear = value;
            this._adapter.setLinearState(this._linear);
            this._adapter.setHostAttribute(STEPPER_CONSTANTS.attributes.LINEAR, this._linear.toString());
        }
    }
    /** Whether the stepper uses the default or alternative label layout mode. */
    get alternative() {
        return this._alternative;
    }
    set alternative(value) {
        value = Boolean(value);
        if (this._alternative !== value) {
            this._alternative = value;
            this._adapter.setAlternativeState(this._alternative);
            this._adapter.setHostAttribute(STEPPER_CONSTANTS.attributes.ALTERNATIVE, this._alternative.toString());
        }
    }
    /** The layout mode of the stepper. */
    get layoutMode() {
        return this._layoutMode;
    }
    set layoutMode(value) {
        if (!/^(fixed|clustered)$/.test(value)) {
            return;
        }
        if (this._layoutMode !== value) {
            this._layoutMode = value;
            this._adapter.setHostAttribute(STEPPER_CONSTANTS.attributes.LAYOUT_MODE, this._layoutMode);
            this._adapter.setLayoutMode(this._layoutMode);
        }
    }
    /** The layout alignment of the stepper. */
    get layoutAlign() {
        return this._layoutAlign;
    }
    set layoutAlign(value) {
        if (!/^(left|center|right)$/.test(value)) {
            return;
        }
        if (this._layoutAlign !== value) {
            this._layoutAlign = value;
            this._adapter.setLayoutAlign(this._layoutAlign);
            this._adapter.setHostAttribute(STEPPER_CONSTANTS.attributes.LAYOUT_ALIGN, this._layoutAlign);
        }
    }
    get disabled() {
        return this._disabled;
    }
    set disabled(value) {
        if (value !== this._disabled) {
            this._disabled = value;
            this._applyDisabled();
        }
    }
    get vertical() {
        return this._vertical;
    }
    set vertical(value) {
        if (value !== this._vertical) {
            this._vertical = value;
            this._applyVertical();
            this._adapter.toggleHostAttribute(STEPPER_CONSTANTS.attributes.VERTICAL, this._vertical);
        }
    }
    _applyVertical() {
        this._adapter.setVertical(this._vertical);
    }
    _applyDisabled() {
        if (this._disabled) {
            this._adapter.setHostAttribute(STEPPER_CONSTANTS.attributes.DISABLED, 'disabled');
        }
        else {
            this._adapter.removeHostAttribute(STEPPER_CONSTANTS.attributes.DISABLED);
        }
        this._adapter.toggleDisabled(this._disabled);
    }
    _applySelectedIndex() {
        this._adapter.setSelectedByIndex(this._selectedIndex);
    }
    _handleClick(event) {
        event.preventDefault();
        if (this._linear) {
            return;
        }
        const eventPath = getEventPath(event);
        const stepRoot = eventPath.find(el => el.classList && el.classList.contains(STEP_CONSTANTS.classes.STEP));
        const step = eventPath.find(el => el.localName === STEP_CONSTANTS.elementName);
        if (stepRoot && step && !step.selected && !step.disabled && this._adapter.emitHostEvent(STEP_CONSTANTS.events.SELECT, step.index, true, true)) {
            this._adapter.setSelected(step);
            this.selectedIndex = step.index;
        }
    }
    _renderConfiguration() {
        const steps = StepperUtils.createSteps({
            steps: this._steps,
            vertical: this._vertical,
            selectedIndex: this._selectedIndex,
            alternative: this._alternative,
            layoutAlign: this._layoutAlign,
            layoutMode: this._layoutMode,
            linear: this._linear
        });
        this.disconnect();
        this._adapter.applyConfiguredSteps(steps);
        this.initialize();
    }
    _onKeydown(event) {
        if (this._linear) {
            return;
        }
        const key = this._getKeyFromEvent(event);
        // If the expanded content is currently being focused, ignore stepper specific keys
        if (!this._adapter.isStepperFocused()) {
            return;
        }
        // Early exit if the event key isn't one of the keyboard navigation keys
        if (!key) {
            return;
        }
        if ([STEP_CONSTANTS.strings.HOME_KEY, STEP_CONSTANTS.strings.END_KEY, STEP_CONSTANTS.strings.ARROW_DOWN_KEY, STEPPER_CONSTANTS.strings.ARROW_UP_KEY].includes(key)) {
            event.preventDefault();
        }
        const step = this._vertical ? this._determineVerticalMoveTarget(key) : this._determineMoveTarget(key);
        if (step) {
            this._moveFocusTo(step);
        }
    }
    _moveFocusTo(step) {
        getShadowElement(step, STEP_CONSTANTS.selectors.STEP).focus();
    }
    _getKeyFromEvent(evt) {
        if (STEPPER_CONSTANTS.ACCEPTABLE_KEYS.includes(evt.key)) {
            return evt.key;
        }
        return STEPPER_CONSTANTS.KEYCODE_MAP[evt.keyCode];
    }
    _determineMoveTarget(key) {
        const step = this._adapter.getFocusedOrSelectedStep();
        if (step) {
            switch (key) {
                case STEPPER_CONSTANTS.strings.ARROW_RIGHT_KEY:
                    return this._adapter.getStep(step.index + 1);
                case STEPPER_CONSTANTS.strings.ARROW_LEFT_KEY:
                    return this._adapter.getStep(step.index - 1);
                case STEPPER_CONSTANTS.strings.HOME_KEY:
                    return this._adapter.getStep(0);
                case STEPPER_CONSTANTS.strings.END_KEY:
                    return this._adapter.getLastStep();
            }
        }
        return step;
    }
    _determineVerticalMoveTarget(key) {
        const step = this._adapter.getFocusedOrSelectedStep();
        if (step) {
            switch (key) {
                case STEPPER_CONSTANTS.strings.ARROW_DOWN_KEY:
                    return this._adapter.getStep(step.index + 1);
                case STEPPER_CONSTANTS.strings.ARROW_UP_KEY:
                    return this._adapter.getStep(step.index - 1);
                case STEPPER_CONSTANTS.strings.HOME_KEY:
                    return this._adapter.getStep(0);
                case STEPPER_CONSTANTS.strings.END_KEY:
                    return this._adapter.getLastStep();
            }
        }
        return step;
    }
    _onSlotChange(event) {
        this.disconnect();
        this.initialize();
    }
    _onStepExpandedContentFocusIn(event) {
        const step = this._adapter.getStep(event.detail.index + 1);
        if (step) {
            step.setStepTabIndex(0);
        }
        event.detail.setStepTabIndex(0);
    }
    _onStepExpandedContentFocusOut(event) {
        const focusOutStep = event.target;
        const step = this._adapter.getStep(focusOutStep.index + 1);
        if (step) {
            step.setStepTabIndex(-1);
        }
        focusOutStep.setStepTabIndex(-1);
    }
    _onStepFocus() {
        this._adapter.toggleRootClass(STEPPER_CONSTANTS.classes.FOCUSED, true);
    }
    _onStepBlur() {
        this._adapter.toggleRootClass(STEPPER_CONSTANTS.classes.FOCUSED, false);
    }
    _patchBrowser() {
        if (new Platform().SAFARI()) {
            this._adapter.patchSafari();
        }
    }
}
