const DEBUG_MODE = /localhost/.test(window.location.href) && 0;

export class UtilRegistry {

  _registeredUtils = [];
  _activeUtilInstances = [];
  _appInstance;

  /**
   * function registerUtil
   *
   * utils need to have at least these properties:
   *   name: string | utils name, e.g. 'example'
   *   selector: string | utils selector, e.g. '[uw-example]'
   *   setup: Function | utils setup function, see below
   *
   * setup function must return instance object with at least these properties:
   *   name: string | utils name
   *   element: HTMLElement | element the util is applied to
   *   destroy: Function | function to destroy the util and remove any listeners
   *
   * @param util Object Utility that should be added to the registry
   */
  register(util) {
    if (DEBUG_MODE > 2) {
      console.log('registering util "' + util.name + '"');
      console.log({ util });
    }
    this._registeredUtils.push(util);
  }

  deregister(name, destroy) {
    const utilIndex = this._findUtilIndex(name);

    if (utilIndex >= 0) {
      if (destroy === true) {
        this._destroyUtilInstances(name);
      }

      this._registeredUtils.splice(utilIndex, 1);
    }
  }

  setApp(appInstance) {
    this._appInstance = appInstance;
  }

  setupAll(scope) {
    if (DEBUG_MODE > 1) {
      console.info('registered js utilities:');
      console.table(this._registeredUtils);
    }

    this._registeredUtils.forEach((util) => this.setup(util, scope));
  }

  setup(util, scope = document.body) {
    if (DEBUG_MODE > 2) {
      console.log('setting up util', { util });
    }

    let instances = [];

    if (util) {
      const elements = this._findUtilElements(util, scope);

      elements.forEach((element) => {
        let utilInstance = null;

        try {
          utilInstance = new util(element, this._appInstance);
        } catch(err) {
          if (DEBUG_MODE > 0) {
            console.warn('Error while trying to initialize a utility!', { util , element, err });
          }
        }

        if (utilInstance) {
          if (DEBUG_MODE > 2) {
            console.info('Got utility instance for utility "' + util.name + '"', { utilInstance });
          }

          instances.push(utilInstance);
        }
      });
    }

    this._activeUtilInstances.push(...instances);
    return instances;
  }

  find(name) {
    return this._registeredUtils.find((util) => util.name === name);
  }

  _findUtilElements(util, scope) {
    if (scope && scope.matches(util.selector)) {
      return [scope];
    }
    return Array.from(scope.querySelectorAll(util.selector));
  }

  _findUtilIndex(name) {
    return this._registeredUtils.findIndex((util) => util.name === name);
  }

  _destroyUtilInstances(name) {
    this._activeUtilInstances
      .map((util, index) => ({
        util: util,
        index: index,
      }))
      .filter((activeUtil) => activeUtil.util.name === name)
      .forEach((activeUtil) => {
        // destroy util instance
        activeUtil.util.destroy();
        delete this._activeUtilInstances[activeUtil.index];
      });

    // get rid of now empty array slots
    this._activeUtilInstances = this._activeUtilInstances.filter((util) => !!util);
  }
}
