// This file is part of Moodle - http://moodle.org/
//
// Moodle 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, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle 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 Moodle.  If not, see <http://www.gnu.org/licenses/>.

/**
 * Contain the logic for a drawer.
 *
 * @module theme_universe/drawer
 * @copyright  2016 Damyon Wiese
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
define([
  "jquery",
  "core/custom_interaction_events",
  "core/log",
  "core/pubsub",
  "core/aria",
  "core_user/repository",
], function ($, CustomEvents, Log, PubSub, Aria, UserRepository) {
  var SELECTORS = {
    TOGGLE_REGION: '[data-region="drawer-toggle"]',
    TOGGLE_ACTION: '[data-action="toggle-drawer"]',
    TOGGLE_TARGET: "aria-controls",
    TOGGLE_SIDE: "left",
    BODY: "body",
    SECTION: '.list-group-item[href*="#section-"]',
    DRAWER: "#nav-drawer",
  };

  var small = $(document).width() < 768;

  /**
   * Constructor for the Drawer.
   */
  var Drawer = function () {
    if (!$(SELECTORS.TOGGLE_REGION).length) {
      Log.debug("Page is missing a drawer region");
    }
    if (!$(SELECTORS.TOGGLE_ACTION).length) {
      Log.debug("Page is missing a drawer toggle link");
    }
    $(SELECTORS.TOGGLE_REGION).each(
      function (index, ele) {
        var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION);
        var drawerid = trigger.attr("aria-controls");
        var drawer = $(document.getElementById(drawerid));
        var hidden = trigger.attr("aria-expanded") == "false";
        var side = trigger.attr("data-side");
        var body = $(SELECTORS.BODY);
        var preference = trigger.attr("data-preference");
        if (small) {
          UserRepository.setUserPreference(preference, false);
        }

        drawer.on("mousewheel DOMMouseScroll", this.preventPageScroll);

        if (!hidden) {
          body.addClass("drawer-open-" + side);
          trigger.attr("aria-expanded", "true");
        } else {
          trigger.attr("aria-expanded", "false");
        }
      }.bind(this)
    );

    this.registerEventListeners();
    if (small) {
      this.closeAll();
    }
  };

  Drawer.prototype.closeAll = function () {
    $(SELECTORS.TOGGLE_REGION).each(function (index, ele) {
      var trigger = $(ele).find(SELECTORS.TOGGLE_ACTION);
      var side = trigger.attr("data-side");
      var body = $(SELECTORS.BODY);
      var drawerid = trigger.attr("aria-controls");
      var drawer = $(document.getElementById(drawerid));
      var preference = trigger.attr("data-preference");

      trigger.attr("aria-expanded", "false");
      body.removeClass("drawer-open-" + side);
      Aria.hide(drawer.get());
      drawer.addClass("closed");
      if (!small) {
        UserRepository.setUserPreference(preference, false);
      }
    });
  };

  /**
   * Open / close the blocks drawer.
   *
   * @method toggleDrawer
   * @param {Event} e
   */
  Drawer.prototype.toggleDrawer = function (e) {
    var trigger = $(e.target).closest("[data-action=toggle-drawer]");
    var drawerid = trigger.attr("aria-controls");
    var drawer = $(document.getElementById(drawerid));
    var body = $(SELECTORS.BODY);
    var side = trigger.attr("data-side");
    var preference = trigger.attr("data-preference");
    if (small) {
      UserRepository.setUserPreference(preference, false);
    }

    body.addClass("drawer-ease");
    var open = trigger.attr("aria-expanded") == "true";
    if (!open) {
      // Open.
      trigger.attr("aria-expanded", "true");
      Aria.unhide(drawer.get());
      drawer.focus();
      body.addClass("drawer-open-" + side);
      drawer.removeClass("closed");
      if (!small) {
        UserRepository.setUserPreference(preference, true);
      }
    } else {
      // Close.
      body.removeClass("drawer-open-" + side);
      trigger.attr("aria-expanded", "false");
      drawer
        .addClass("closed")
        .delay(500)
        .queue(function () {
          // Ensure that during the delay, the drawer wasn't re-opened.
          if ($(this).hasClass("closed")) {
            Aria.hide(this);
          }
          $(this).dequeue();
        });
      if (!small) {
        UserRepository.setUserPreference(preference, false);
      }
    }

    // Publish an event to tell everything that the drawer has been toggled.
    // The drawer transitions closed so another event will fire once teh transition
    // has completed.
    PubSub.publish("nav-drawer-toggle-start", open);
  };

  /**
   * Prevent the page from scrolling when the drawer is at max scroll.
   *
   * @method preventPageScroll
   * @param  {Event} e
   */
  Drawer.prototype.preventPageScroll = function (e) {
    var delta =
        e.wheelDelta ||
        (e.originalEvent && e.originalEvent.wheelDelta) ||
        -e.originalEvent.detail,
      bottomOverflow =
        this.scrollTop + $(this).outerHeight() - this.scrollHeight >= 0,
      topOverflow = this.scrollTop <= 0;

    if ((delta < 0 && bottomOverflow) || (delta > 0 && topOverflow)) {
      e.preventDefault();
    }
  };

  /**
   * Set up all of the event handling for the modal.
   *
   * @method registerEventListeners
   */
  Drawer.prototype.registerEventListeners = function () {
    $(SELECTORS.TOGGLE_ACTION).each(
      function (index, element) {
        CustomEvents.define($(element), [CustomEvents.events.activate]);
        $(element).on(
          CustomEvents.events.activate,
          function (e, data) {
            this.toggleDrawer(data.originalEvent);
            data.originalEvent.preventDefault();
          }.bind(this)
        );
      }.bind(this)
    );

    $(SELECTORS.SECTION).click(
      function () {
        if (small) {
          this.closeAll();
        }
      }.bind(this)
    );

    // Publish an event to tell everything that the drawer completed the transition
    // to either an open or closed state.
    $(SELECTORS.DRAWER).on(
      "webkitTransitionEnd msTransitionEnd transitionend",
      function (e) {
        var drawer = $(e.target).closest(SELECTORS.DRAWER);
        // Note: aria-hidden is either present, or absent. It should not be set to false.
        var open = !!drawer.attr("aria-hidden");
        PubSub.publish("nav-drawer-toggle-end", open);
      }
    );
  };

  return {
    init: function () {
      return new Drawer();
    },
  };
});
