define(['lodash', 'ui/keyNavigation/navigator', 'ui/keyNavigation/navigableDomElement', 'taoQtiTest/runner/plugins/content/accessibility/keyNavigation/helpers'], function (_, keyNavigator, navigableDomElement, helpers) { 'use strict';

    _ = _ && Object.prototype.hasOwnProperty.call(_, 'default') ? _['default'] : _;
    keyNavigator = keyNavigator && Object.prototype.hasOwnProperty.call(keyNavigator, 'default') ? keyNavigator['default'] : keyNavigator;
    navigableDomElement = navigableDomElement && Object.prototype.hasOwnProperty.call(navigableDomElement, 'default') ? navigableDomElement['default'] : navigableDomElement;

    /**
     * This program is free software; you can redistribute it and/or
     * modify it under the terms of the GNU General Public License
     * as published by the Free Software Foundation; under version 2
     * of the License (non-upgradable).
     *
     * This program is distributed in the hope that it will be useful,
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     * GNU General Public License for more details.
     *
     * You should have received a copy of the GNU General Public License
     * along with this program; if not, write to the Free Software
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
     *
     * Copyright (c) 2020 Open Assessment Technologies SA ;
     */

    /**
     * List of CSS selectors for the navigables
     * @type {Object}
     */
    const selectors = {
      filters: '.qti-navigator-filters .qti-navigator-filter',
      enabledItems: '.qti-navigator-tree .qti-navigator-item:not(.disabled) .qti-navigator-label'
    };

    /**
     * For 'fizzy' navigator layout, builds the navigator navigation strategy.
     *
     * @returns {keyNavigationStrategy}
     */
    function initFizzy(config, $panel, $navigator, keyNavigators, managedNavigators) {
      let itemsNavigator;
      const $navigatorTree = $panel.find('.qti-navigator-tree');
      const $enabledItems = $navigator.find('.qti-navigator-tree .buttonlist-item:not(.disabled) .buttonlist-btn');
      const navigableItems = navigableDomElement.createFromDoms($enabledItems);

      /**
       * Get item wrapper from navigable button
       * (`navigable`: '.buttonlist-btn'; `parent()`: '.buttonlist-item')
       * @returns {jQuery}
       */
      const getItemFromNavigable = navigable => {
        return navigable.getElement().parent();
      };
      if (navigableItems.length) {
        itemsNavigator = keyNavigator({
          id: 'navigator-items',
          elements: navigableItems,
          group: $navigatorTree,
          loop: true,
          defaultPosition(navigableElements) {
            let pos = _.findIndex(navigableElements, navigable => {
              const $parent = getItemFromNavigable(navigable);
              if ($parent.hasClass('buttonlist-item-active') && $parent.is(':visible')) {
                return true;
              }
            });
            return pos >= 0 ? pos : 0;
          }
        }).on('focus', cursor => {
          getItemFromNavigable(cursor.navigable).addClass('key-navigation-highlight');
        }).on('blur', cursor => {
          getItemFromNavigable(cursor.navigable).removeClass('key-navigation-highlight');
        });
        helpers.setupItemsNavigator(itemsNavigator, {
          keyNextItem: config.keyNextItem,
          keyPrevItem: config.keyPrevItem
        });
        helpers.setupClickableNavigator(itemsNavigator);
        keyNavigators.push(itemsNavigator);
        managedNavigators.push(itemsNavigator);
      }
    }

    /**
     * Key navigator strategy applying onto the navigation panel.
     * @type {Object} keyNavigationStrategy
     */
    var navigatorNavigation = {
      name: 'navigator',
      /**
       * Builds the navigator navigation strategy.
       *
       * @returns {keyNavigationStrategy}
       */
      init() {
        const config = this.getConfig();
        const $panel = this.getTestRunner().getAreaBroker().getPanelArea();
        const $navigator = $panel.find('.qti-navigator');
        const isFizzyLayout = $navigator.hasClass('qti-navigator-fizzy');
        this.managedNavigators = [];
        this.keyNavigators = [];
        if ($navigator.length && !$navigator.hasClass('disabled')) {
          if (isFizzyLayout) {
            initFizzy(config, $panel, $navigator, this.keyNavigators, this.managedNavigators);
          } else {
            const $testStatusHeader = $navigator.find('.qti-navigator-info.collapsible > .qti-navigator-label');
            const navigableTestStatus = navigableDomElement.createFromDoms($testStatusHeader);
            $testStatusHeader.addClass('key-navigation-actionable');
            if (navigableTestStatus.length) {
              const testStatusNavigation = keyNavigator({
                keepState: config.keepState,
                id: 'navigator-test-status',
                propagateTab: false,
                elements: navigableTestStatus,
                group: $testStatusHeader
              });
              helpers.setupItemsNavigator(testStatusNavigation, {
                keyNextItem: config.keyNextTab || config.keyNextItem,
                keyPrevItem: config.keyPrevTab || config.keyPrevItem
              });
              this.keyNavigators.push(testStatusNavigation);
              this.managedNavigators.push(testStatusNavigation);
            }
            let filtersNavigator;
            let itemsNavigator;
            //the tag to identify if the item listing has been browsed, to only "smart jump" to active item only on the first visit
            let itemListingVisited = false;
            //the position of the filter in memory, to only "smart jump" to active item only on the first visit
            let currentFilter;
            const $filters = $navigator.find(selectors.filters);
            const navigableFilters = navigableDomElement.createFromDoms($filters);
            if (navigableFilters.length) {
              filtersNavigator = keyNavigator({
                keepState: config.keepState,
                id: 'navigator-filters',
                propagateTab: false,
                elements: navigableFilters,
                group: $navigator.find('.qti-navigator-filters')
              });
              helpers.setupItemsNavigator(filtersNavigator, {
                keyNextItem: config.keyNextTab || config.keyNextItem,
                keyPrevItem: config.keyPrevTab || config.keyPrevItem
              });
              helpers.setupClickableNavigator(filtersNavigator);
              if (config.keepState) {
                filtersNavigator.on('focus', cursor => {
                  if (config.keepState) {
                    const $element = cursor.navigable.getElement();
                    const filter = $element.data('mode');
                    $element.click();
                    if (currentFilter !== filter) {
                      itemListingVisited = false;
                    }
                    currentFilter = filter;
                  }
                });
              }
              if (config.keyNextContent) {
                filtersNavigator.on(config.keyNextContent, elem => {
                  if (helpers.allowedToNavigateFrom(elem) && itemsNavigator) {
                    _.defer(() => {
                      if (itemListingVisited) {
                        itemsNavigator.first();
                      } else {
                        itemsNavigator.focus();
                      }
                    });
                  }
                });
              }
              if (config.keyPrevContent) {
                filtersNavigator.on(config.keyPrevContent, elem => {
                  if (helpers.allowedToNavigateFrom(elem) && itemsNavigator) {
                    _.defer(() => {
                      itemsNavigator.last();
                    });
                  }
                });
              }
              this.keyNavigators.push(filtersNavigator);
              this.managedNavigators.push(filtersNavigator);
            }
            const $navigatorTree = $panel.find('.qti-navigator-tree');
            const $trees = $navigator.find(selectors.enabledItems);
            const navigableTrees = navigableDomElement.createFromDoms($trees);
            $trees.first().addClass('key-navigation-scrollable-up');
            $trees.last().addClass('key-navigation-scrollable-down');
            if (navigableTrees.length) {
              //instantiate a key navigator but do not add it to the returned list of navigators as this is not supposed to be reached with tab key
              itemsNavigator = keyNavigator({
                id: 'navigator-items',
                elements: navigableTrees,
                group: $navigatorTree,
                defaultPosition(navigableElements) {
                  let pos = 0;
                  if (config.flatNavigation || currentFilter !== 'flagged') {
                    pos = _.findIndex(navigableElements, navigable => {
                      const $parent = navigable.getElement().parent('.qti-navigator-item');
                      if ($parent.hasClass('active') && $parent.is(':visible')) {
                        return true;
                      }
                    });
                  }
                  return pos;
                }
              }).on('focus', cursor => {
                itemListingVisited = true;
                cursor.navigable.getElement().parent().addClass('key-navigation-highlight');
              }).on('blur', cursor => {
                cursor.navigable.getElement().parent().removeClass('key-navigation-highlight');
              });
              helpers.setupItemsNavigator(itemsNavigator, {
                keyNextItem: config.keyNextContent || config.keyNextItem,
                keyPrevItem: config.keyPrevContent || config.keyPrevItem
              });
              helpers.setupClickableNavigator(itemsNavigator);
              if (config.keepState) {
                itemsNavigator.on('lowerbound upperbound', () => {
                  if (filtersNavigator) {
                    filtersNavigator.focus();
                  }
                });
              }
              if (config.keyNextTab && config.keyPrevTab) {
                itemsNavigator.on(config.keyNextTab, function (elem) {
                  if (helpers.allowedToNavigateFrom(elem) && filtersNavigator) {
                    filtersNavigator.focus().next();
                  }
                });
                itemsNavigator.on(config.keyPrevTab, function (elem) {
                  if (helpers.allowedToNavigateFrom(elem) && filtersNavigator) {
                    filtersNavigator.focus().previous();
                  }
                });
              } else {
                this.keyNavigators.push(itemsNavigator);
              }
              this.managedNavigators.push(itemsNavigator);
            }
          }
        }
        return this;
      },
      /**
       * Gets the list of applied navigators
       * @returns {keyNavigator[]}
       */
      getNavigators() {
        return this.keyNavigators;
      },
      /**
       * Tears down the keyNavigator strategy
       * @returns {keyNavigationStrategy}
       */
      destroy() {
        this.managedNavigators.forEach(navigator => navigator.destroy());
        this.managedNavigators = [];
        this.keyNavigators = [];
        return this;
      }
    };

    return navigatorNavigation;

});
