/**
 * @license
 * Copyright 2023 Tyler Technologies, Inc. 
 * License: Apache-2.0
 */
import { throttle, Platform, isDefined } from '@tylertech/forge-core';
import { TOOLTIP_CONSTANTS } from './tooltip-constants';
/**
 * The foundation class behind the tooltip component.
 */
export class TooltipFoundation {
    constructor(_adapter) {
        this._adapter = _adapter;
        this._delay = TOOLTIP_CONSTANTS.numbers.DEFAULT_DELAY;
        this._position = TOOLTIP_CONSTANTS.strings.DEFAULT_POSITION;
        this._isOpen = false;
        this._mouseOverListener = evt => this._onMouseOver(evt);
        this._mouseOutListener = evt => this._onMouseOut(evt);
        this._touchStartListener = evt => this._onTouchStart(evt);
        this._touchEndListener = evt => this._onTouchEnd(evt);
        this._scrollListener = throttle((evt) => this._onScroll(evt), 100);
        this._clickListener = () => this._targetInteracted();
        this._mouseDownListener = () => this._targetInteracted();
        this._dragListener = () => this._targetInteracted();
    }
    initialize() {
        this._adapter.initializeTargetElement(this._target);
        if (!this._adapter.hasTargetElement()) {
            throw new Error('Unable to locate target element.');
        }
        // Set a unique id for this tooltip (unless it already has one)
        this._identifier = this._adapter.getHostAttribute('id');
        if (!this._identifier) {
            this._identifier = this._getNextIdentifier();
            this._adapter.setHostAttribute('id', this._identifier);
        }
        this._addTargetListeners();
        this._adapter.initializeAccessibility(this._identifier);
    }
    disconnect() {
        if (this._mouseOverTimeout) {
            window.clearTimeout(this._mouseOverTimeout);
            this._mouseOverTimeout = undefined;
        }
        if (this._touchTimeout) {
            window.clearTimeout(this._touchTimeout);
            this._touchTimeout = undefined;
        }
        this._adapter.destroy(this._identifier);
        if (this._isOpen) {
            this.hide();
        }
        if (this._adapter.hasTargetElement()) {
            this._removeTargetListeners();
        }
    }
    /**
     * Generates the next available tooltip identifier.
     */
    _getNextIdentifier() {
        return `forge-tooltip-${TooltipFoundation._tooltipIdentifier++}`;
    }
    /** Adds event listeners to the target element. */
    _addTargetListeners() {
        if (Platform.isMobile) {
            this._adapter.addTargetEventListener('touchstart', this._touchStartListener);
            this._adapter.addTargetEventListener('touchend', this._touchEndListener);
        }
        else {
            this._adapter.addTargetEventListener('mouseover', this._mouseOverListener);
            this._adapter.addTargetEventListener('mouseout', this._mouseOutListener);
        }
        this._adapter.addTargetEventListener('click', this._clickListener);
        this._adapter.addTargetEventListener('mousedown', this._mouseDownListener);
        this._adapter.addTargetEventListener('dragstart', this._dragListener);
    }
    /**
     * Removes the event listeners from the target element.
     * @param targetElement The target element instance.
     */
    _removeTargetListeners() {
        if (Platform.isMobile) {
            this._adapter.removeTargetEventListener('touchstart', this._touchStartListener);
            this._adapter.removeTargetEventListener('touchend', this._touchEndListener);
        }
        else {
            this._adapter.removeTargetEventListener('mouseover', this._mouseOverListener);
            this._adapter.removeTargetEventListener('mouseout', this._mouseOutListener);
        }
        this._adapter.removeTargetEventListener('click', this._clickListener);
        this._adapter.removeTargetEventListener('mousedown', this._mouseDownListener);
        this._adapter.removeTargetEventListener('dragstart', this._dragListener);
    }
    /**
     * Handles the touchstart event on the target element to detect long press.
     */
    _onTouchStart(evt) {
        this._touchTimeout = window.setTimeout(() => {
            this._show();
            window.setTimeout(() => this.hide(), TOOLTIP_CONSTANTS.numbers.LONGPRESS_VISIBILITY_DURATION);
            this._touchTimeout = undefined;
        }, TOOLTIP_CONSTANTS.numbers.LONGPRESS_THRESHOLD);
    }
    /**
     * Handles the touchend event on the target element to cancel a long press action.
     */
    _onTouchEnd(evt) {
        if (this._touchTimeout) {
            this._clearTouchTimer();
        }
    }
    _clearTouchTimer() {
        window.clearTimeout(this._touchTimeout);
        this._touchTimeout = undefined;
    }
    /**
     * Handles the mouseover event on the target element.
     */
    _onMouseOver(evt) {
        if (this._isOpen || !this._adapter.hasTargetElement() || !this._adapter.isTargetElementConnected()) {
            return;
        }
        if (this._delay) {
            this._mouseOverTimeout = window.setTimeout(() => {
                this._show();
                this._mouseOverTimeout = undefined;
            }, this._delay);
        }
        else {
            this._show();
        }
    }
    /**
     * Handles the mouseout event on the target element.
     */
    _onMouseOut(evt) {
        if (this._mouseOverTimeout) {
            window.clearTimeout(this._mouseOverTimeout);
            this._mouseOverTimeout = undefined;
            return;
        }
        this.hide();
    }
    /**
     * Displays the tooltip.
     */
    _show() {
        let content;
        if (this._builder && typeof this._builder === 'function') {
            content = this._builder();
        }
        this._adapter.showTooltip(this._position, content);
        if (this._adapter.hasTooltipElement()) {
            this._isOpen = true;
            this._adapter.addWindowListener('scroll', this._scrollListener);
        }
    }
    /**
     * Hides the tooltip.
     */
    hide() {
        if (!this._isOpen) {
            return;
        }
        if (this._touchTimeout) {
            this._clearTouchTimer();
        }
        this._isOpen = false;
        this._adapter.removeWindowListener('scroll', this._scrollListener);
        this._adapter.hideTooltip();
    }
    /**
     * Handles scrolling events when the tooltip is open.
     */
    _onScroll(evt) {
        if (!this._isOpen) {
            return;
        }
        this.hide();
    }
    _targetInteracted() {
        if (this._mouseOverTimeout) {
            window.clearTimeout(this._mouseOverTimeout);
        }
        if (this._isOpen) {
            this.hide();
        }
    }
    /** Gets/sets the tooltip text. */
    get text() {
        return this._text || this._adapter.getInnerText();
    }
    set text(value) {
        if (this._text !== value) {
            this._text = value;
            this._adapter.setTextContent(this._text);
            this._adapter.setHostAttribute(TOOLTIP_CONSTANTS.attributes.TEXT, this._text);
        }
    }
    /** Sets the tooltip builder function. */
    get builder() {
        return this._builder;
    }
    set builder(value) {
        this._builder = value;
    }
    /** Gets/sets the target element CSS selector. */
    get target() {
        return this._target;
    }
    set target(value) {
        if (this._target !== value) {
            this._target = value;
            if (this._adapter.hasTargetElement()) {
                this._removeTargetListeners();
            }
            this._adapter.initializeTargetElement(this._target);
            if (this._adapter.hasTargetElement()) {
                this._addTargetListeners();
            }
            this._adapter.setHostAttribute(TOOLTIP_CONSTANTS.attributes.TARGET, isDefined(this._target) ? this._target : '');
        }
    }
    /** Gets/sets the interaction delay. */
    get delay() {
        return this._delay;
    }
    set delay(value) {
        if (this._delay !== value) {
            this._delay = value;
            this._adapter.setHostAttribute(TOOLTIP_CONSTANTS.attributes.DELAY, isDefined(this._delay) ? this._delay.toString() : '');
        }
    }
    /** Gets/sets the tooltip position. */
    get position() {
        return this._position;
    }
    set position(value) {
        if (this._position !== value) {
            this._position = value;
            this._adapter.setHostAttribute(TOOLTIP_CONSTANTS.attributes.POSITION, isDefined(this._position) ? this._position.toString() : '');
        }
    }
    set open(value) {
        this._show();
    }
    get open() {
        return this._isOpen;
    }
    get tooltipElement() {
        return this._adapter.getTooltipElement();
    }
}
TooltipFoundation._tooltipIdentifier = 0;
