"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NavigationGroup = void 0;
var navigationalgorithm_1 = require("./navigationalgorithm");
var gethtmlelementsfromcomponents_1 = require("./gethtmlelementsfromcomponents");
var nodeeventsubscriber_1 = require("./nodeeventsubscriber");
var typeguards_1 = require("./typeguards");
var types_1 = require("./types");
/**
 * Used as part of spatial navigation. Groups together different components to which you can navigate to, in a single
 * navigation group.
 *
 * Responsible for finding elements in direction on navigation and for tracking active element inside the group.
 * Triggers blur and focus on element when active element is changed, as well as click on element on `Action.SELECT`.
 * Will call `hideUi()` on passed in container if `Action.BACK` is called.
 *
 * @category Components
 */
var NavigationGroup = /** @class */ (function () {
    function NavigationGroup(container) {
        var components = [];
        for (var _i = 1; _i < arguments.length; _i++) {
            components[_i - 1] = arguments[_i];
        }
        this.container = container;
        this.removeElementHoverEventListeners = function () { };
        this.components = components;
        this.eventSubscriber = new nodeeventsubscriber_1.NodeEventSubscriber();
    }
    /**
     * Returns the active HTMLElement.
     */
    NavigationGroup.prototype.getActiveElement = function () {
        return this.activeElement;
    };
    NavigationGroup.prototype.focusElement = function (element) {
        this.blurActiveElement();
        this.activeElement = element;
        this.activeElement.focus();
    };
    NavigationGroup.prototype.blurActiveElement = function () {
        var _a;
        (_a = this.activeElement) === null || _a === void 0 ? void 0 : _a.blur();
    };
    NavigationGroup.prototype.focusFirstElement = function () {
        var element = (0, gethtmlelementsfromcomponents_1.getHtmlElementsFromComponents)(this.components)[0];
        if (element) {
            this.focusElement(element);
        }
    };
    NavigationGroup.prototype.defaultNavigationHandler = function (direction) {
        var targetElement = (0, navigationalgorithm_1.getElementInDirection)(this.activeElement, (0, gethtmlelementsfromcomponents_1.getHtmlElementsFromComponents)(this.components), direction);
        if (targetElement) {
            this.focusElement(targetElement);
        }
    };
    NavigationGroup.prototype.defaultActionHandler = function (action) {
        switch (action) {
            case (types_1.Action.SELECT):
                this.activeElement.click();
                break;
            case (types_1.Action.BACK):
                this.container.hide();
                break;
        }
    };
    NavigationGroup.prototype.handleInput = function (data, defaultHandler, userHandler) {
        var handleDefault = true;
        var preventDefault = function () { return (handleDefault = false); };
        userHandler === null || userHandler === void 0 ? void 0 : userHandler(data, this.activeElement, preventDefault);
        if (handleDefault) {
            defaultHandler.call(this, data);
        }
    };
    /**
     * Handles a navigation event.
     *
     * @param direction The direction of the navigation event
     */
    NavigationGroup.prototype.handleNavigation = function (direction) {
        if (!this.activeElement) {
            // If we do not have an active element, the active element has been disabled by a mouseleave
            // event. We should continue the navigation at the exact place where we left off.
            if (this.activeElementBeforeDisable) {
                this.focusElement(this.activeElementBeforeDisable);
            }
            else {
                this.focusFirstElement();
            }
            return;
        }
        // eslint-disable-next-line @typescript-eslint/unbound-method
        this.handleInput(direction, this.defaultNavigationHandler, this.onNavigation);
    };
    /**
     * Handles an action event.
     *
     * @param action The action of the event
     */
    NavigationGroup.prototype.handleAction = function (action) {
        // eslint-disable-next-line @typescript-eslint/unbound-method
        this.handleInput(action, this.defaultActionHandler, this.onAction);
    };
    /**
     * Disable navigation group
     *
     * Call blur on active element, set as undefined, and track it as element before disable.
     */
    NavigationGroup.prototype.disable = function () {
        if (this.activeElement) {
            this.activeElementBeforeDisable = this.activeElement;
            this.blurActiveElement();
            this.activeElement = undefined;
        }
    };
    /**
     * Enable navigation group
     *
     * Sets active element to either element that was active before disable, or first element of tracked elements.
     * If it is settings panel, it will always focus first element in the list.
     */
    NavigationGroup.prototype.enable = function () {
        if (this.activeElementBeforeDisable && !(0, typeguards_1.isSettingsPanel)(this.container)) {
            this.focusElement(this.activeElementBeforeDisable);
            this.activeElementBeforeDisable = undefined;
        }
        else {
            this.focusFirstElement();
        }
        this.trackElementHover();
    };
    /**
     * Adds event listener for `mouseenter` on tracked elements to ensure tracking of active element will work together
     * in combination of using mouse and key events.
     */
    NavigationGroup.prototype.trackElementHover = function () {
        var _this = this;
        this.removeElementHoverEventListeners();
        var removeEventListenerFunctions = (0, gethtmlelementsfromcomponents_1.getHtmlElementsFromComponents)(this.components).map(function (htmlElem) {
            var enterListener = _this.focusElement.bind(_this, htmlElem);
            var exitListener = function () { return _this.disable(); };
            _this.eventSubscriber.on(htmlElem, 'mouseenter', enterListener);
            _this.eventSubscriber.on(htmlElem, 'mouseleave', exitListener);
            return function () {
                _this.eventSubscriber.off(htmlElem, 'mouseenter', enterListener);
                _this.eventSubscriber.off(htmlElem, 'mouseleave', exitListener);
            };
        });
        this.removeElementHoverEventListeners = function () { return removeEventListenerFunctions.forEach(function (fn) { return fn(); }); };
    };
    /**
     * Dispose of navigation group
     */
    NavigationGroup.prototype.release = function () {
        this.eventSubscriber.release();
        this.activeElement = undefined;
        this.components.splice(0, this.components.length);
        this.removeElementHoverEventListeners();
    };
    return NavigationGroup;
}());
exports.NavigationGroup = NavigationGroup;
