text: import DatasetController from './core.datasetController';
  import Element from './core.element';
  import Scale from './core.scale';
  import TypedRegistry from './core.typedRegistry';
  import {each, callback as call, _capitalize} from '../helpers/helpers.core';
  
  /**
   * Please use the module's default export which provides a singleton instance
   * Note: class is exported for typedoc
   */
  export class Registry {
    constructor() {
      this.controllers = new TypedRegistry(DatasetController, 'controllers');
      this.elements = new TypedRegistry(Element, 'elements');
      this.plugins = new TypedRegistry(Object, 'plugins');
      this.scales = new TypedRegistry(Scale, 'scales');
      // Order is important, Scale has Element in prototype chain,
      // so Scales must be before Elements. Plugins are a fallback, so not listed here.
      this._typedRegistries = [this.controllers, this.scales, this.elements];
    }
  
    /**
  	 * @param  {...any} args
  	 */
    add(...args) {
      this._each('register', args);
    }
  
    remove(...args) {
      this._each('unregister', args);
    }
  
    /**
  	 * @param  {...typeof DatasetController} args
  	 */
    addControllers(...args) {
      this._each('register', args, this.controllers);
    }
  
    /**
  	 * @param  {...typeof Element} args
  	 */
    addElements(...args) {
      this._each('register', args, this.elements);
    }
  
    /**
  	 * @param  {...any} args
  	 */
    addPlugins(...args) {
      this._each('register', args, this.plugins);
    }
  
    /**
  	 * @param  {...typeof Scale} args
  	 */
    addScales(...args) {
      this._each('register', args, this.scales);
    }
  
    /**
  	 * @param {string} id
  	 * @returns {typeof DatasetController}
  	 */
    getController(id) {
      return this._get(id, this.controllers, 'controller');
    }
  
    /**
  	 * @param {string} id
  	 * @returns {typeof Element}
  	 */
    getElement(id) {
      return this._get(id, this.elements, 'element');
    }
  
    /**
  	 * @param {string} id
  	 * @returns {object}
  	 */
    getPlugin(id) {
      return this._get(id, this.plugins, 'plugin');
    }
  
    /**
  	 * @param {string} id
  	 * @returns {typeof Scale}
  	 */
    getScale(id) {
      return this._get(id, this.scales, 'scale');
    }
  
    /**
  	 * @param  {...typeof DatasetController} args
  	 */
    removeControllers(...args) {
      this._each('unregister', args, this.controllers);
    }
  
    /**
  	 * @param  {...typeof Element} args
  	 */
    removeElements(...args) {
      this._each('unregister', args, this.elements);
    }
  
    /**
  	 * @param  {...any} args
  	 */
    removePlugins(...args) {
      this._each('unregister', args, this.plugins);
    }
  
    /**
  	 * @param  {...typeof Scale} args
  	 */
    removeScales(...args) {
      this._each('unregister', args, this.scales);
    }
  
    /**
  	 * @private
  	 */
    _each(method, args, typedRegistry) {
      const me = this;
      [...args].forEach(arg => {
        const reg = typedRegistry || me._getRegistryForType(arg);
        if (typedRegistry || reg.isForType(arg) || (reg === me.plugins && arg.id)) {
          me._exec(method, reg, arg);
        } else {
          // Handle loopable args
          // Use case:
          //  import * as plugins from './plugins';
          //  Chart.register(plugins);
          each(arg, item => {
            // If there are mixed types in the loopable, make sure those are
            // registered in correct registry
            // Use case: (treemap exporting controller, elements etc)
            //  import * as treemap from 'chartjs-chart-treemap';
            //  Chart.register(treemap);
  
            const itemReg = typedRegistry || me._getRegistryForType(item);
            me._exec(method, itemReg, item);
          });
        }
      });
    }
  
    /**
  	 * @private
  	 */
    _exec(method, registry, component) {
      const camelMethod = _capitalize(method);
      call(component['before' + camelMethod], [], component);
      registry[method](component);
      call(component['after' + camelMethod], [], component);
    }
  
    /**
  	 * @private
  	 */
    _getRegistryForType(type) {
      for (let i = 0; i < this._typedRegistries.length; i++) {
        const reg = this._typedRegistries[i];
        if (reg.isForType(type)) {
          return reg;
        }
      }
      // plugins is the fallback registry
      return this.plugins;
    }
  
    /**
  	 * @private
  	 */
    _get(id, typedRegistry, type) {
      const item = typedRegistry.get(id);
      if (item === undefined) {
        throw new Error('"' + id + '" is not a registered ' + type + '.');
      }
      return item;
    }
  
  }
  
  // singleton instance
  export default new Registry();
  