import "core-js/modules/es.array.iterator";
import "core-js/modules/es.object.to-string";
import "core-js/modules/es.string.iterator";
import "core-js/modules/es.weak-map";
import "core-js/modules/es.weak-set";
import "core-js/modules/web.dom-collections.iterator";

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

import EventManager from './../../eventManager';
import localHooks from './../../mixins/localHooks';
import { mixin } from './../../helpers/object';
import { isMobileBrowser } from './../../helpers/browser';
import { selectElementIfAllowed } from './../../helpers/dom/element';
/**
 * @class FocusableWrapper
 *
 * @plugin CopyPaste
 */

var FocusableWrapper =
/*#__PURE__*/
function () {
  function FocusableWrapper(container) {
    _classCallCheck(this, FocusableWrapper);

    this.rootDocument = container.defaultView ? container : container.ownerDocument;
    /**
     * The main/operational focusable element.
     *
     * @type {HTMLElement}
     */

    this.mainElement = null;
    /**
     * Instance of EventManager.
     *
     * @type {EventManager}
     */

    this.eventManager = new EventManager(this);
    /**
     * An object for tracking information about event listeners attached to the focusable element.
     *
     * @type {WeakSet}
     */

    this.listenersCount = new WeakSet();
    /**
     * Parent for an focusable element.
     *
     * @type {HTMLElement}
     */

    this.container = container;
  }
  /**
   * Switch to the secondary focusable element. Used when no any main focusable element is provided.
   */


  _createClass(FocusableWrapper, [{
    key: "useSecondaryElement",
    value: function useSecondaryElement() {
      var el = createOrGetSecondaryElement(this.container);

      if (!this.listenersCount.has(el)) {
        this.listenersCount.add(el);
        forwardEventsToLocalHooks(this.eventManager, el, this);
      }

      this.mainElement = el;
    }
    /**
     * Switch to the main focusable element.
     *
     * @param {HTMLElement} element
     */

  }, {
    key: "setFocusableElement",
    value: function setFocusableElement(element) {
      if (!this.listenersCount.has(element)) {
        this.listenersCount.add(element);
        forwardEventsToLocalHooks(this.eventManager, element, this);
      }

      this.mainElement = element;
    }
    /**
     * Get currently set focusable element.
     *
     * @returns {HTMLElement}
     */

  }, {
    key: "getFocusableElement",
    value: function getFocusableElement() {
      return this.mainElement;
    }
    /**
     * Set focus to the focusable element.
     */

  }, {
    key: "focus",
    value: function focus() {
      // Add an empty space to texarea. It is necessary for safari to enable "copy" command from menu bar.
      this.mainElement.value = ' ';

      if (!isMobileBrowser()) {
        selectElementIfAllowed(this.mainElement);
      }
    }
  }]);

  return FocusableWrapper;
}();

mixin(FocusableWrapper, localHooks);
var refCounter = new WeakMap();
/**
 * Create and return the FocusableWrapper instance.
 *
 * @param {HTMLElement} container
 * @returns {FocusableWrapper}
 */

function createElement(container) {
  var focusableWrapper = new FocusableWrapper(container);
  var counter = refCounter.get(container);
  counter = isNaN(counter) ? 0 : counter;
  refCounter.set(container, counter + 1);
  return focusableWrapper;
}
/**
 * Deactivate the FocusableWrapper instance.
 *
 * @param {FocusableWrapper} wrapper
 */


function deactivateElement(wrapper) {
  wrapper.eventManager.clear();
}

var runLocalHooks = function runLocalHooks(eventName, subject) {
  return function (event) {
    return subject.runLocalHooks(eventName, event);
  };
};
/**
 * Register copy/cut/paste events and forward their actions to the subject local hooks system.
 *
 * @param {HTMLElement} element
 * @param {FocusableWrapper} subject
 */


function forwardEventsToLocalHooks(eventManager, element, subject) {
  eventManager.addEventListener(element, 'copy', runLocalHooks('copy', subject));
  eventManager.addEventListener(element, 'cut', runLocalHooks('cut', subject));
  eventManager.addEventListener(element, 'paste', runLocalHooks('paste', subject));
}

var secondaryElements = new WeakMap();
/**
 * Create and attach newly created focusable element to the DOM.
 *
 * @param {HTMLElement} container
 * @returns {HTMLElement}
 */

function createOrGetSecondaryElement(container) {
  var secondaryElement = secondaryElements.get(container);

  if (secondaryElement) {
    if (!secondaryElement.parentElement) {
      container.appendChild(secondaryElement);
    }

    return secondaryElement;
  }

  var doc = container.defaultView ? container : container.ownerDocument;
  var element = doc.createElement('textarea');
  secondaryElements.set(container, element);
  element.className = 'HandsontableCopyPaste';
  element.tabIndex = -1;
  element.autocomplete = 'off';
  element.wrap = 'hard';
  element.value = ' ';
  container.appendChild(element);
  return element;
}
/**
 * Destroy the FocusableWrapper instance.
 *
 * @param {FocusableWrapper} wrapper
 */


function destroyElement(wrapper) {
  if (!(wrapper instanceof FocusableWrapper)) {
    return;
  }

  var counter = refCounter.get(wrapper.container);
  counter = isNaN(counter) ? 0 : counter;

  if (counter > 0) {
    counter -= 1;
  }

  deactivateElement(wrapper);

  if (counter <= 0) {
    counter = 0; // Detach secondary element from the DOM.

    var secondaryElement = secondaryElements.get(wrapper.container);

    if (secondaryElement && secondaryElement.parentNode) {
      secondaryElement.parentNode.removeChild(secondaryElement);
      secondaryElements.delete(wrapper.container);
    }

    wrapper.mainElement = null;
  }

  refCounter.set(wrapper.container, counter);
}

export { createElement, deactivateElement, destroyElement };