"use strict";

exports.__esModule = true;
require("core-js/modules/es.error.cause.js");
require("core-js/modules/es.array.push.js");
var _object = require("../../helpers/object");
var _array = require("../../helpers/array");
var _registry = require("../registry");
var _registry2 = require("../../cellTypes/registry");
var _registry3 = require("../../editors/registry");
var _registry4 = require("../../renderers/registry");
var _registry5 = require("../../validators/registry");
var _eventManager = _interopRequireDefault(require("../../eventManager"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classPrivateMethodInitSpec(obj, privateSet) { _checkPrivateRedeclaration(obj, privateSet); privateSet.add(obj); }
function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); }
function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
function _classPrivateMethodGet(receiver, privateSet, fn) { if (!privateSet.has(receiver)) { throw new TypeError("attempted to get private field on non-instance"); } return fn; }
function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); }
function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); }
function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; }
const DEPS_TYPE_CHECKERS = new Map([['plugin', _registry.hasPlugin], ['cell-type', _registry2.hasCellType], ['editor', _registry3.hasEditor], ['renderer', _registry4.hasRenderer], ['validator', _registry5.hasValidator]]);
const PLUGIN_KEY = 'base';
exports.PLUGIN_KEY = PLUGIN_KEY;
const missingDepsMsgs = [];
let initializedPlugins = null;

/**
 * @util
 * @property {Core} hot Handsontable instance.
 */
var _hooks = /*#__PURE__*/new WeakMap();
var _isRelevantToSettings = /*#__PURE__*/new WeakSet();
class BasePlugin {
  static get PLUGIN_KEY() {
    return PLUGIN_KEY;
  }

  /**
   * The `SETTING_KEYS` getter defines the keys that, when present in the config object, trigger the plugin update
   * after the `updateSettings` calls.
   * - When it returns `true`, the plugin updates after all `updateSettings` calls, regardless of the contents of the
   * config object.
   * - When it returns `false`, the plugin never updates on `updateSettings` calls.
   *
   * @returns {string[] | boolean}
   */
  static get SETTING_KEYS() {
    return [this.PLUGIN_KEY];
  }

  /**
   * The instance of the {@link EventManager} class.
   *
   * @type {EventManager}
   */

  /**
   * @param {object} hotInstance Handsontable instance.
   */
  constructor(hotInstance) {
    /**
     * Check if any of the keys defined in `SETTING_KEYS` configuration of the plugin is present in the provided
     * config object, or if the `SETTING_KEYS` configuration states that the plugin is relevant to the config object
     * regardless of its contents.
     *
     * @private
     * @param {Handsontable.DefaultSettings} settings The config object passed to `updateSettings`.
     * @returns {boolean}
     */
    _classPrivateMethodInitSpec(this, _isRelevantToSettings);
    _defineProperty(this, "eventManager", new _eventManager.default(this));
    /**
     * @type {string}
     */
    _defineProperty(this, "pluginName", null);
    /**
     * @type {Function[]}
     */
    _defineProperty(this, "pluginsInitializedCallbacks", []);
    /**
     * @type {boolean}
     */
    _defineProperty(this, "isPluginsReady", false);
    /**
     * @type {boolean}
     */
    _defineProperty(this, "enabled", false);
    /**
     * @type {boolean}
     */
    _defineProperty(this, "initialized", false);
    /**
     * Collection of the reference to the plugins hooks.
     */
    _classPrivateFieldInitSpec(this, _hooks, {
      writable: true,
      value: {}
    });
    /**
     * Handsontable instance.
     *
     * @type {Core}
     */
    (0, _object.defineGetter)(this, 'hot', hotInstance, {
      writable: false
    });
    initializedPlugins = null;
    this.hot.addHook('afterPluginsInitialized', () => this.onAfterPluginsInitialized());
    this.hot.addHook('afterUpdateSettings', newSettings => this.onUpdateSettings(newSettings));
    this.hot.addHook('beforeInit', () => this.init());
  }
  init() {
    this.pluginName = this.hot.getPluginName(this);
    const pluginDeps = this.constructor.PLUGIN_DEPS;
    const deps = Array.isArray(pluginDeps) ? pluginDeps : [];
    if (deps.length > 0) {
      const missingDependencies = [];
      deps.forEach(dependency => {
        const [type, moduleName] = dependency.split(':');
        if (!DEPS_TYPE_CHECKERS.has(type)) {
          throw new Error(`Unknown plugin dependency type "${type}" was found.`);
        }
        if (!DEPS_TYPE_CHECKERS.get(type)(moduleName)) {
          missingDependencies.push(` - ${moduleName} (${type})`);
        }
      });
      if (missingDependencies.length > 0) {
        const errorMsg = [`The ${this.pluginName} plugin requires the following modules:\n`, `${missingDependencies.join('\n')}\n`].join('');
        missingDepsMsgs.push(errorMsg);
      }
    }
    if (!initializedPlugins) {
      initializedPlugins = (0, _registry.getPluginsNames)();
    }

    // Workaround for the UndoRedo plugin which, currently doesn't follow the plugin architecture.
    // Without this line the `callOnPluginsReady` callback won't be triggered after all plugin
    // initialization.
    if (initializedPlugins.indexOf('UndoRedo') >= 0) {
      initializedPlugins.splice(initializedPlugins.indexOf('UndoRedo'), 1);
    }
    if (initializedPlugins.indexOf(this.pluginName) >= 0) {
      initializedPlugins.splice(initializedPlugins.indexOf(this.pluginName), 1);
    }
    this.hot.addHookOnce('afterPluginsInitialized', () => {
      if (this.isEnabled && this.isEnabled()) {
        this.enablePlugin();
      }
    });
    const isAllPluginsAreInitialized = initializedPlugins.length === 0;
    if (isAllPluginsAreInitialized) {
      if (missingDepsMsgs.length > 0) {
        const errorMsg = [`${missingDepsMsgs.join('\n')}\n`, 'You have to import and register them manually.'].join('');
        throw new Error(errorMsg);
      }
      this.hot.runHooks('afterPluginsInitialized');
    }
    this.initialized = true;
  }

  /**
   * Enable plugin for this Handsontable instance.
   */
  enablePlugin() {
    this.enabled = true;
  }

  /**
   * Disable plugin for this Handsontable instance.
   */
  disablePlugin() {
    var _this$eventManager;
    (_this$eventManager = this.eventManager) === null || _this$eventManager === void 0 || _this$eventManager.clear();
    this.clearHooks();
    this.enabled = false;
  }

  /**
   * Add listener to plugin hooks system.
   *
   * @param {string} name The hook name.
   * @param {Function} callback The listener function to add.
   */
  addHook(name, callback) {
    _classPrivateFieldGet(this, _hooks)[name] = _classPrivateFieldGet(this, _hooks)[name] || [];
    const hooks = _classPrivateFieldGet(this, _hooks)[name];
    this.hot.addHook(name, callback);
    hooks.push(callback);
    _classPrivateFieldGet(this, _hooks)[name] = hooks;
  }

  /**
   * Remove all hooks listeners by hook name.
   *
   * @param {string} name The hook name.
   */
  removeHooks(name) {
    (0, _array.arrayEach)(_classPrivateFieldGet(this, _hooks)[name] || [], callback => {
      this.hot.removeHook(name, callback);
    });
  }

  /**
   * Clear all hooks.
   */
  clearHooks() {
    const hooks = _classPrivateFieldGet(this, _hooks);
    (0, _object.objectEach)(hooks, (callbacks, name) => this.removeHooks(name));
    hooks.length = 0;
  }

  /**
   * Register function which will be immediately called after all plugins initialized.
   *
   * @param {Function} callback The listener function to call.
   */
  callOnPluginsReady(callback) {
    if (this.isPluginsReady) {
      callback();
    } else {
      this.pluginsInitializedCallbacks.push(callback);
    }
  }
  /**
   * On after plugins initialized listener.
   *
   * @private
   */
  onAfterPluginsInitialized() {
    (0, _array.arrayEach)(this.pluginsInitializedCallbacks, callback => callback());
    this.pluginsInitializedCallbacks.length = 0;
    this.isPluginsReady = true;
  }

  /**
   * On update settings listener.
   *
   * @private
   * @param {object} newSettings New set of settings passed to the `updateSettings` method.
   */
  onUpdateSettings(newSettings) {
    const relevantToSettings = _classPrivateMethodGet(this, _isRelevantToSettings, _isRelevantToSettings2).call(this, newSettings);
    if (this.isEnabled) {
      if (this.enabled && !this.isEnabled()) {
        this.disablePlugin();
      }
      if (!this.enabled && this.isEnabled()) {
        this.enablePlugin();
      }
      if (this.enabled && this.isEnabled() && relevantToSettings) {
        this.updatePlugin(newSettings);
      }
    }
  }

  /**
   * Updates the plugin to use the latest options you have specified.
   *
   * @private
   */
  updatePlugin() {}

  /**
   * Destroy plugin.
   */
  destroy() {
    var _this$eventManager2;
    (_this$eventManager2 = this.eventManager) === null || _this$eventManager2 === void 0 || _this$eventManager2.destroy();
    this.clearHooks();
    (0, _object.objectEach)(this, (value, property) => {
      if (property !== 'hot') {
        this[property] = null;
      }
    });
    delete this.t;
    delete this.hot;
  }
}
exports.BasePlugin = BasePlugin;
function _isRelevantToSettings2(settings) {
  if (!settings) {
    return false;
  }
  const settingKeys = this.constructor.SETTING_KEYS;

  // If SETTING_KEYS is declared as `true` -> update the plugin regardless of the settings declared in
  // `updateSettings`.
  // If SETTING_KEYS is declared as `false` -> DON'T update the plugin regardless of the settings declared in
  // `updateSettings`.
  if (typeof settingKeys === 'boolean') {
    return settingKeys;
  }
  for (let i = 0; i < settingKeys.length; i++) {
    if (settings[settingKeys[i]] !== undefined) {
      return true;
    }
  }
  return false;
}