/**
 * @license
 * Copyright Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { SLIDER_CONSTANTS } from './slider-constants';
export class SliderFoundation {
    constructor(_adapter) {
        this._adapter = _adapter;
        // State
        this._value = SLIDER_CONSTANTS.numbers.DEFAULT_VALUE;
        this._valueStart = SLIDER_CONSTANTS.numbers.DEFAULT_START_VALUE;
        this._valueEnd = SLIDER_CONSTANTS.numbers.DEFAULT_END_VALUE;
        this._min = 0;
        this._max = 100;
        this._step = SLIDER_CONSTANTS.numbers.DEFAULT_STEP;
        this._tickmarks = false;
        this._labeled = true;
        this._range = false;
        this._disabled = false;
        this._readonly = false;
        this._pointerEnterListener = (evt) => this._handlePointerEnter(evt);
        this._pointerMoveListener = (evt) => this._handlePointerMove(evt);
        this._pointerLeaveListener = (evt) => this._handlePointerLeave(evt);
        this._changeUpdateListener = (evt) => this._handleInputUpdate(evt);
        this._focusListener = (evt) => this._handleFocus(evt);
    }
    initialize() {
        this._applyInputListeners();
        this._update();
    }
    _update() {
        var _a, _b;
        const { value: renderValue, valueStart: renderValueStart, valueEnd: renderValueEnd } = this._clampMinMax();
        const step = this._step <= 0 ? 1 : this._step;
        const range = Math.max(this._max - this._min, step);
        const startFraction = this._range ? ((renderValueStart !== null && renderValueStart !== void 0 ? renderValueStart : this._min) - this._min) / range : 0;
        const valueEnd = this._range ? renderValueEnd : renderValue;
        const endFraction = ((valueEnd !== null && valueEnd !== void 0 ? valueEnd : this.min) - this.min) / range;
        const tickCount = range / step;
        this._adapter.update({ startFraction, endFraction, tickCount });
        this._adapter.syncInputValues(renderValueStart, this._range ? renderValueEnd : renderValue);
        if (this._range) {
            this._adapter.tryDetectOverlap();
        }
        let labelStart = (_a = this._labelStart) !== null && _a !== void 0 ? _a : String(renderValueStart);
        let labelEnd = (_b = this._labelEnd) !== null && _b !== void 0 ? _b : String(this._range ? renderValueEnd : renderValue);
        if (typeof this._labelBuilder === 'function') {
            if (this._range) {
                labelStart = this._labelBuilder(renderValueStart, 'start');
                labelEnd = this._labelBuilder(renderValueEnd, 'end');
            }
            else {
                labelEnd = this._labelBuilder(renderValue);
            }
        }
        this._adapter.updateLabels(labelStart, labelEnd);
    }
    _clampMinMax() {
        // We make copies of our values so we can mutate them without affecting the original values
        let valueCopy = this._value;
        let valueStartCopy = this._valueStart;
        let valueEndCopy = this._valueEnd;
        if (this._range) {
            if (valueStartCopy > this._max) {
                valueStartCopy = this._max;
            }
            else if (valueStartCopy < this._min) {
                valueStartCopy = this._min;
            }
            if (valueEndCopy > this._max) {
                valueEndCopy = this._max;
            }
            else if (valueEndCopy < this._min) {
                valueEndCopy = this._min;
            }
        }
        else {
            if (valueCopy > this._max) {
                valueCopy = this._max;
            }
            else if (this._value < this._min) {
                valueCopy = this._min;
            }
        }
        return {
            value: valueCopy,
            valueStart: valueStartCopy,
            valueEnd: valueEndCopy
        };
    }
    _applyInputListeners() {
        this._adapter.addInputListener('pointerenter', this._pointerEnterListener);
        this._adapter.addInputListener('pointermove', this._pointerMoveListener);
        this._adapter.addInputListener('pointerleave', this._pointerLeaveListener);
        this._adapter.addInputListener('input', this._changeUpdateListener);
        this._adapter.addInputListener('change', this._changeUpdateListener);
        this._adapter.addInputListener('focus', this._focusListener);
    }
    _handlePointerEnter(evt) {
        this._handlePointerMove(evt);
    }
    _handlePointerMove({ target, x, y }) {
        const isStart = target.id === 'start';
        if (isStart) {
            this._adapter.tryHoverStartHandle({ x, y });
        }
        else {
            this._adapter.tryHoverEndHandle({ x, y });
        }
    }
    _handlePointerLeave(_evt) {
        this._adapter.leaveHandleContainer();
    }
    _handleInputUpdate(evt) {
        evt.stopPropagation(); // We don't allow the native input & change events to bubble outside of the component
        if (this._disabled || this._readonly) {
            this._adapter.syncInputValues(this._valueStart, this._range ? this._valueEnd : this._value);
            return;
        }
        const input = evt.target;
        if (this._range) {
            const isStart = input.id === 'start';
            if (isStart) {
                this._valueStart = input.valueAsNumber;
            }
            else {
                this._valueEnd = input.valueAsNumber;
            }
            // Clamp values to keep start and end from going past each other
            if (this._canClamp()) {
                if (isStart) {
                    this._valueStart = this._valueEnd;
                }
                else {
                    this._valueEnd = this._valueStart;
                }
                // Ensure our underlying inputs are synchronized with our state since they continue moving even when we clamp
                this._adapter.syncInputValues(this._valueStart, this._valueEnd);
                this._update();
                return; // We don't dispatch events when clamping
            }
        }
        else {
            this._value = input.valueAsNumber;
        }
        const eventMap = {
            default: {
                change: SLIDER_CONSTANTS.events.CHANGE,
                input: SLIDER_CONSTANTS.events.INPUT
            },
            range: {
                change: SLIDER_CONSTANTS.events.RANGE_CHANGE,
                input: SLIDER_CONSTANTS.events.RANGE_INPUT
            }
        };
        const type = this._range ? eventMap.range[evt.type] : eventMap.default[evt.type];
        const detail = this._range ? { valueStart: this._valueStart, valueEnd: this._valueEnd } : { value: this._value };
        const event = new CustomEvent(type, { detail, bubbles: true });
        this._adapter.dispatchHostEvent(event);
        this._update();
    }
    _handleFocus(_evt) {
        this._adapter.updateHandleLayering();
    }
    _canClamp() {
        return this._valueStart > this._valueEnd || this._valueEnd < this._valueStart;
    }
    get value() {
        return this._value;
    }
    set value(value) {
        if (this._value !== value) {
            this._value = value;
            this._update();
        }
    }
    get valueStart() {
        return this._valueStart;
    }
    set valueStart(value) {
        if (this._valueStart !== value) {
            this._valueStart = value;
            this._update();
        }
    }
    get valueEnd() {
        return this._valueEnd;
    }
    set valueEnd(value) {
        if (this._valueEnd !== value) {
            this._valueEnd = value;
            this._update();
        }
    }
    get label() {
        return this.labelEnd;
    }
    set label(value) {
        this.labelEnd = value;
    }
    get labelStart() {
        return this._labelStart;
    }
    set labelStart(value) {
        if (this._labelStart !== value) {
            this._labelStart = value;
            this._adapter.updateLabels(this._labelStart, this._labelEnd);
        }
    }
    get labelEnd() {
        return this._labelEnd;
    }
    set labelEnd(value) {
        if (this._labelEnd !== value) {
            this._labelEnd = value;
            this._adapter.updateLabels(this._labelStart, this._labelEnd);
        }
    }
    get labelBuilder() {
        return this._labelBuilder;
    }
    set labelBuilder(cb) {
        this._labelBuilder = cb;
        this._update();
    }
    get min() {
        return this._min;
    }
    set min(value) {
        if (this._min !== value) {
            this._min = value;
            this._adapter.setMin(this._min);
            this._update();
            this._adapter.setHostAttribute(SLIDER_CONSTANTS.attributes.MIN, String(this._min));
        }
    }
    get max() {
        return this._max;
    }
    set max(value) {
        if (this._max !== value) {
            this._max = value;
            this._adapter.setMax(this._max);
            this._update();
            this._adapter.setHostAttribute(SLIDER_CONSTANTS.attributes.MAX, String(this._max));
        }
    }
    get step() {
        return this._step;
    }
    set step(value) {
        if (this._step !== value) {
            this._step = value;
            this._adapter.setStep(this._step);
            this._update();
            this._adapter.setHostAttribute(SLIDER_CONSTANTS.attributes.STEP, String(this._step));
        }
    }
    get tickmarks() {
        return this._tickmarks;
    }
    set tickmarks(value) {
        if (this._tickmarks !== value) {
            this._tickmarks = value;
            this._adapter.setTickmarks(this._tickmarks);
            this._adapter.toggleHostAttribute(SLIDER_CONSTANTS.attributes.TICKMARKS, this._tickmarks);
        }
    }
    get labeled() {
        return this._labeled;
    }
    set labeled(value) {
        if (this._labeled !== value) {
            this._labeled = value;
            this._adapter.toggleLabels(this._labeled);
            this._update();
            this._adapter.toggleHostAttribute(SLIDER_CONSTANTS.attributes.LABELED, this._labeled);
        }
    }
    get range() {
        return this._range;
    }
    set range(value) {
        if (this._range !== value) {
            this._range = value;
            this._adapter.setRange(this._range);
            if (this._range) {
                this._applyInputListeners();
            }
            this._update();
            this._adapter.toggleHostAttribute(SLIDER_CONSTANTS.attributes.RANGE, this._range);
        }
    }
    get disabled() {
        return this._disabled;
    }
    set disabled(value) {
        if (this._disabled !== value) {
            this._disabled = value;
            this._adapter.setDisabled(value);
            this._adapter.toggleHostAttribute(SLIDER_CONSTANTS.attributes.DISABLED, this._disabled);
        }
    }
    get readonly() {
        return this._readonly;
    }
    set readonly(value) {
        if (this._readonly !== value) {
            this._readonly = value;
            this._adapter.setReadonly(value);
            this._adapter.toggleHostAttribute(SLIDER_CONSTANTS.attributes.READONLY, this._readonly);
        }
    }
    set ariaLabel(value) {
        this.ariaLabelEnd = value;
    }
    set ariaLabelStart(value) {
        this._adapter.setStartAriaLabel(value);
    }
    set ariaLabelEnd(value) {
        this._adapter.setEndAriaLabel(value);
    }
}
