/**
 * @module tgam-patterns/scroll-manager
 */
import ee from "./event-emitter";

const eventNamespace = "tgam-scroll-manager";

let layers = [];
let scrollPosition = null;
let scrollPositionSaved = false;

/**
 * @method registerLayer
 * @description Registers a layer to keep track of how many elements are overlaid
 * @param {Object} c - collection of properties
 * @param {String} c.key - a unique id to register and unregister layers
 * @param {Function} [c.isOpen] - a method which determines whether the layer is enabled/visible on the page
 * @param {String} [c.saveScrollEvent] - event that will save the scroll position (e.g. before the position changes)
 * @param {String} [c.lockEvent] - name of event that will initiate scroll lock (e.g. when layer has finished opening)
 * @param {String} [c.unlockEvent] - name of event that will release scroll lock
 * @param {String} [c.resetScrollEvent] - name of event that will reset scroll (e.g. when layer has finished closing)
 */
export function registerLayer({ key, isOpen, saveScrollEvent, lockEvent, unlockEvent, resetScrollEvent }) {
  if (typeof key === "undefined") {
    console.error("scroll-manager: registerLayer method object parameter must have the 'key' property defined");
  }

  let layerObject = {
    key,
    isOpen
  };

  layers.push(layerObject);

  if (saveScrollEvent) {
    ee.on(saveScrollEvent, saveScrollPosition);
  }

  if (lockEvent) {
    ee.on(lockEvent, lockScrolling);
  }

  if (unlockEvent) {
    ee.on(unlockEvent, unlockScrolling);
  }

  if (resetScrollEvent) {
    ee.on(resetScrollEvent, resetScrollTop);
  }
}

/**
 * @method unregisterLayer
 * @description Unregisters a layer that was previously registered.
 * @param {Object} c - collection of properties
 * @param {String} c.key - a unique id to register and unregister layers
 */
export function unregisterLayer({ key }) {
  layers = layers.filter(layer => {
    if (layer.key === key) {
      if (layer.saveScrollEvent) {
        ee.off(layer.saveScrollEvent, saveScrollPosition);
      }
      if (layer.lockEvent) {
        ee.off(layer.lockEvent, lockScrolling);
      }
      if (layer.unlockEvent) {
        ee.off(layer.unlockEvent, unlockScrolling);
      }
      if (layer.resetScrollEvent) {
        ee.off(layer.resetScrollEvent, resetScrollTop);
      }
      return false;
    }
    return true;
  });
}

/**
 * @method saveScrollPosition
 * @description Called by "saveScrollEvent" event, will save position once, and will not be reset until the layer is closed
 */
export function saveScrollPosition() {
  if (!scrollPositionSaved) {
    scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
    scrollPositionSaved = true;
  }
}

/**
 * @method lockScrolling
 * @description Makes the body unscrollable
 */
export function lockScrolling() {
  saveScrollPosition();
  document.body.classList.add("u-no-scroll");
  ee.emit(`${eventNamespace}:locked`, [true]);
}

/**
 * @method unlockScrolling
 * @description When a layer has finished closing, check whether it's safe to allow the body to scroll again (all layers must be closed)
 */
export function unlockScrolling() {
  resetScrollTop();
}

/**
 * @method resetScrollTop
 * @description Removes no scroll class and reset scroll position
 */
export function resetScrollTop() {
  // every layer should have a method `isOpen` and the value for all of them must be `false`
  if (allLayersClosed()) {
    document.body.classList.remove("u-no-scroll");
    ee.emit(`${eventNamespace}:locked`, [false]);
    // reset to previous scroll top position
    window.scrollTo(0, scrollPosition);
    scrollPositionSaved = false;
  }
}

/**
 * @method containsClasses
 * @description Loops through the array of class names and check to see if the element currently has that class.
 * If any class exists on the element then method will return TRUE,
 * if none of the classes are found on the element then method returns FALSE
 * @param {HTMLElement} el - a DOM element
 * @param {Array} list - an array of class names
 * @returns {Boolean}
 */
export function containsClasses(el, list) {
  return list.some((className) => el.classList.contains(className));
}

/**
 * @method allLayersClosed
 * @description loops through all layers and returns true if `isOpen` methods on every layer returns false
 * @returns {Boolean}
 */
function allLayersClosed() {
  return layers.every((layer) => layer.isOpen && !layer.isOpen());
}
