define(['jquery', 'i18n', 'ui/hider', 'taoTests/runner/plugin', 'taoQtiTest/runner/plugins/navigation/next/nextWarningHelper', 'taoQtiTest/runner/helpers/messages', 'taoQtiTest/runner/helpers/map', 'taoQtiTest/runner/helpers/navigation', 'taoQtiTest/runner/helpers/stats', 'util/shortcut', 'util/namespace', 'handlebars', 'lib/handlebars/helpers'], function ($$1, __, hider, pluginFactory, nextWarningHelper, messages, mapHelper, navigationHelper, statsHelper, shortcut, namespaceHelper, Handlebars, Helpers0) { 'use strict';

    $$1 = $$1 && Object.prototype.hasOwnProperty.call($$1, 'default') ? $$1['default'] : $$1;
    __ = __ && Object.prototype.hasOwnProperty.call(__, 'default') ? __['default'] : __;
    hider = hider && Object.prototype.hasOwnProperty.call(hider, 'default') ? hider['default'] : hider;
    pluginFactory = pluginFactory && Object.prototype.hasOwnProperty.call(pluginFactory, 'default') ? pluginFactory['default'] : pluginFactory;
    nextWarningHelper = nextWarningHelper && Object.prototype.hasOwnProperty.call(nextWarningHelper, 'default') ? nextWarningHelper['default'] : nextWarningHelper;
    messages = messages && Object.prototype.hasOwnProperty.call(messages, 'default') ? messages['default'] : messages;
    mapHelper = mapHelper && Object.prototype.hasOwnProperty.call(mapHelper, 'default') ? mapHelper['default'] : mapHelper;
    navigationHelper = navigationHelper && Object.prototype.hasOwnProperty.call(navigationHelper, 'default') ? navigationHelper['default'] : navigationHelper;
    statsHelper = statsHelper && Object.prototype.hasOwnProperty.call(statsHelper, 'default') ? statsHelper['default'] : statsHelper;
    shortcut = shortcut && Object.prototype.hasOwnProperty.call(shortcut, 'default') ? shortcut['default'] : shortcut;
    namespaceHelper = namespaceHelper && Object.prototype.hasOwnProperty.call(namespaceHelper, 'default') ? namespaceHelper['default'] : namespaceHelper;
    Handlebars = Handlebars && Object.prototype.hasOwnProperty.call(Handlebars, 'default') ? Handlebars['default'] : Handlebars;
    Helpers0 = Helpers0 && Object.prototype.hasOwnProperty.call(Helpers0, 'default') ? Helpers0['default'] : Helpers0;

    if (!Helpers0.__initialized) {
      Helpers0(Handlebars);
      Helpers0.__initialized = true;
    }
    var Template = Handlebars.template(function (Handlebars,depth0,helpers,partials,data) {
      this.compilerInfo = [4,'>= 1.0.0'];
    helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
      var buffer = "", stack1, helper, functionType="function", escapeExpression=this.escapeExpression, self=this;

    function program1(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += " ";
      if (helper = helpers.className) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.className); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1);
      return buffer;
      }

    function program3(depth0,data) {
      
      var buffer = "", stack1;
      buffer += "\n        aria-"
        + escapeExpression(((stack1 = (data == null || data === false ? data : data.key)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
        + "=\""
        + escapeExpression((typeof depth0 === functionType ? depth0.apply(depth0) : depth0))
        + "\"\n    ";
      return buffer;
      }

    function program5(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "<span class=\"icon icon-";
      if (helper = helpers.icon) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.icon); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1);
      stack1 = helpers.unless.call(depth0, (depth0 && depth0.text), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\"></span>";
      return buffer;
      }
    function program6(depth0,data) {
      
      
      return " no-label";
      }

    function program8(depth0,data) {
      
      var buffer = "", stack1, helper;
      buffer += "<span class=\"text\">";
      if (helper = helpers.text) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.text); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "</span>";
      return buffer;
      }

      buffer += "<li\n    data-control=\"";
      if (helper = helpers.control) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.control); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\"\n    class=\"small btn-info action";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.className), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\"\n    title=\"";
      if (helper = helpers.title) { stack1 = helper.call(depth0, {hash:{},data:data}); }
      else { helper = (depth0 && depth0.title); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
      buffer += escapeExpression(stack1)
        + "\"\n    role=\"button\"\n    ";
      stack1 = helpers.each.call(depth0, (depth0 && depth0.aria), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n>\n    <a class=\"li-inner\" href=\"#\" onclick=\"return false\" aria-hidden=\"true\" >\n        ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.icon), {hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n        ";
      stack1 = helpers['if'].call(depth0, (depth0 && depth0.text), {hash:{},inverse:self.noop,fn:self.program(8, program8, data),data:data});
      if(stack1 || stack1 === 0) { buffer += stack1; }
      buffer += "\n    </a>\n</li>\n";
      return buffer;
      });
    function buttonTpl(data, options, asString) {
      var html = Template(data, options);
      return (asString || true) ? html : $(html);
    }

    /**
     * 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) 2016-2019 (original work) Open Assessment Technologies SA ;
     */

    /**
     * The display of the next button
     */
    const buttonData = {
      next: {
        control: 'move-forward',
        title: __('Submit and go to the next item'),
        specificTitle: __('Submit and go to the item %s'),
        icon: 'forward',
        text: __('Next')
      },
      end: {
        control: 'move-end',
        title: __('Submit and go to the end of the test'),
        icon: 'fast-forward',
        text: __('End test')
      }
    };

    /**
     * Create the button based on the current context
     * @param {Boolean} [isLast=false] - is the current item the last
     * @returns {jQueryElement} the button
     */
    const createElement = function () {
      let isLast = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      const dataType = isLast ? 'end' : 'next';
      return $$1(buttonTpl(buttonData[dataType]));
    };

    /**
     * Makes an element enabled
     * @param  {jQuery} $element
     * @returns {jQuery}
     */
    const enableElement = $element => $element.removeProp('disabled').removeClass('disabled');

    /**
     * Makes an element disabled
     * @param  {jQuery} $element
     * @returns {jQuery}
     */
    const disableElement = $element => $element.prop('disabled', true).addClass('disabled');

    /**
     * Update the button based on the context
     * @param {jQueryElement} $element - the element to update
     * @param {TestRunner} [testRunner] - the test runner instance
     * @param {Boolean} [isLast=false] - is the current item the last
     */
    const updateElement = function ($element, testRunner) {
      let isLast = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
      const dataType = isLast ? 'end' : 'next';
      const testContext = testRunner.getTestContext();
      if (dataType === 'next' && !testContext.isAdaptive && !testContext.isCatAdaptive) {
        const testMap = testRunner.getTestMap();
        const nextItem = navigationHelper.getNextItem(testMap, testContext.itemPosition);
        $element.attr('title', __(buttonData.next.specificTitle, nextItem.label));
      } else {
        $element.attr('title', buttonData[dataType].title);
      }
      if ($element.attr('data-control') !== buttonData[dataType].control) {
        $element.attr('data-control', buttonData[dataType].control).find('.text').text(buttonData[dataType].text);
        if (dataType === 'next') {
          $element.find(`.icon-${buttonData.end.icon}`).removeClass(`icon-${buttonData.end.icon}`).addClass(`icon-${buttonData.next.icon}`);
        } else {
          $element.find(`.icon-${buttonData.next.icon}`).removeClass(`icon-${buttonData.next.icon}`).addClass(`icon-${buttonData.end.icon}`);
        }
      }
    };

    /**
     * Returns the configured plugin
     */
    var next = pluginFactory({
      name: 'next',
      /**
       * Initialize the plugin (called during runner's init)
       */
      init() {
        const testRunner = this.getTestRunner();
        const testRunnerOptions = testRunner.getOptions();
        const pluginShortcuts = (testRunnerOptions.shortcuts || {})[this.getName()] || {};

        /**
         * Check if the current item is the last item
         * @returns {Boolean} true if the last
         */
        const isLastItem = () => {
          const testContext = testRunner.getTestContext();
          const testMap = testRunner.getTestMap();
          const itemIdentifier = testContext.itemIdentifier;
          return navigationHelper.isLast(testMap, itemIdentifier);
        };

        //plugin behavior
        /**
         * @param {Boolean} nextItemWarning - enable the display of a warning when going to the next item.
         * Note: the actual display of the warning depends on other conditions (see nextWarningHelper)
         */
        const doNext = nextItemWarning => {
          const testContext = testRunner.getTestContext();
          const testMap = testRunner.getTestMap();
          const testPart = testRunner.getCurrentPart();
          const nextItemPosition = testContext.itemPosition + 1;
          const itemIdentifier = testContext.itemIdentifier;

          // x-tao-option-unansweredWarning is a deprecated option whose behavior now matches the one of
          const unansweredWarning = mapHelper.hasItemCategory(testMap, itemIdentifier, 'unansweredWarning', true);

          // x-tao-option-nextPartWarning with the unansweredOnly option
          const nextPartWarning = mapHelper.hasItemCategory(testMap, itemIdentifier, 'nextPartWarning', true) || unansweredWarning;
          const endTestWarning = mapHelper.hasItemCategory(testMap, itemIdentifier, 'endTestWarning', true);

          // this check to avoid an edge case where having both endTestWarning
          // and unansweredWarning options would prevent endTestWarning to behave normally
          const unansweredOnly = !endTestWarning && unansweredWarning;
          const warningScope = nextPartWarning ? 'part' : 'test';
          const enableNav = () => testRunner.trigger('enablenav');
          const triggerNextAction = () => {
            if (isLastItem()) {
              this.trigger('end');
            }
            testRunner.next();
          };
          testRunner.trigger('disablenav');
          if (this.getState('enabled') !== false) {
            const warningHelper = nextWarningHelper({
              endTestWarning: endTestWarning,
              isLast: isLastItem(),
              isLinear: testPart.isLinear,
              nextItemWarning: nextItemWarning,
              nextPartWarning: nextPartWarning,
              nextPart: mapHelper.getItemPart(testMap, nextItemPosition),
              remainingAttempts: testContext.remainingAttempts,
              testPartId: testContext.testPartId,
              unansweredWarning: unansweredWarning,
              stats: statsHelper.getInstantStats(warningScope, testRunner),
              unansweredOnly: unansweredOnly
            });
            if (warningHelper.shouldWarnBeforeEndPart()) {
              const submitButtonLabel = __('SUBMIT THIS PART');
              testRunner.trigger('confirm.endTestPart', messages.getExitMessage(warningScope, testRunner, '', false, submitButtonLabel), triggerNextAction,
              // if the test taker accept
              enableNav,
              // if he refuse
              {
                buttons: {
                  labels: {
                    ok: submitButtonLabel,
                    cancel: __('CANCEL')
                  }
                }
              });
            } else if (warningHelper.shouldWarnBeforeEnd()) {
              const submitButtonLabel = __('SUBMIT THE TEST');
              testRunner.trigger('confirm.endTest', messages.getExitMessage(warningScope, testRunner, '', false, submitButtonLabel), triggerNextAction,
              // if the test taker accept
              enableNav,
              // if he refuse
              {
                buttons: {
                  labels: {
                    ok: submitButtonLabel,
                    cancel: __('CANCEL')
                  }
                }
              });
            } else if (warningHelper.shouldWarnBeforeNext()) {
              testRunner.trigger('confirm.next', __('You are about to go to the next item. Click OK to continue and go to the next item.'), triggerNextAction,
              // if the test taker accept
              enableNav // if he refuse
              );
            } else {
              triggerNextAction();
            }
          }
        };

        //create the button (detached)
        this.$element = createElement(isLastItem());

        //attach behavior
        this.$element.on('click', e => {
          e.preventDefault();
          disableElement(this.$element);
          testRunner.trigger('nav-next');
        });
        const registerShortcut = kbdShortcut => {
          if (testRunnerOptions.allowShortcuts && kbdShortcut) {
            shortcut.add(namespaceHelper.namespaceAll(kbdShortcut, this.getName(), true), () => {
              if (this.getState('enabled') === true) {
                testRunner.trigger('nav-next', true);
              }
            }, {
              avoidInput: true,
              prevent: true
            });
          }
        };
        registerShortcut(pluginShortcuts.trigger);

        //disabled by default
        this.disable();

        //change plugin state
        testRunner.on('loaditem', () => {
          updateElement(this.$element, testRunner, isLastItem());
        }).on('enablenav', () => this.enable()).on('disablenav', () => this.disable()).on('hidenav', () => this.hide()).on('shownav', () => this.show()).on('nav-next', nextItemWarning => doNext(nextItemWarning)).on('enableaccessibilitymode', () => {
          const kbdShortcut = pluginShortcuts.triggerAccessibility;
          if (kbdShortcut && !this.getState('eaccessibilitymode')) {
            shortcut.remove(`.${this.getName()}`);
            registerShortcut(kbdShortcut);
            this.setState('eaccessibilitymode');
          }
        });
      },
      /**
       * Called during the runner's render phase
       */
      render() {
        //attach the element to the navigation area
        const $container = this.getAreaBroker().getNavigationArea();
        $container.append(this.$element);
      },
      /**
       * Called during the runner's destroy phase
       */
      destroy() {
        shortcut.remove(`.${this.getName()}`);
        this.$element.remove();
      },
      /**
       * Enable the button
       */
      enable() {
        enableElement(this.$element);
      },
      /**
       * Disable the button
       */
      disable() {
        disableElement(this.$element);
      },
      /**
       * Show the button
       */
      show() {
        hider.show(this.$element);
      },
      /**
       * Hide the button
       */
      hide() {
        hider.hide(this.$element);
      }
    });

    return next;

});
