(function() {
  'use strict';

  window.utils = window.utils || {};

  var registeredSetupListeners = {};
  var activeInstances = {};

/**
 * setup function to initiate a util (utilName) on a scope (sope) with options (options).
 *
 * Utils need to be defined as property of `window.utils` and need to accept a scope and (optionally) options.
 * Example: `window.utils.autoSubmit = function(scope, options) { ... };`
 */

  window.utils.setup = function(utilName, scope, options) {
    if (!utilName || !scope) {
      return;
    }

    options = options || {};

    var utilInstance;

    // i18n
    if (window.I18N) {
      options.i18n = window.I18N;
    }

    if (activeInstances[utilName]) {
      var instanceWithSameScope = activeInstances[utilName]
        .filter(function(instance) { return !!instance; })
        .find(function(instance) {
          return instance.scope === scope;
        });
      var isAlreadySetup = !!instanceWithSameScope;

      if (isAlreadySetup) {
        console.warn('Trying to setup a JS utility that\'s already been set up', { utility: utilName, scope, options });
        if (!options.force) {
          return false;
        }
      }
    }

    function setup() {
      var listener = function(event) {
        if (event.detail.targetUtil !== utilName) {
          return false;
        }

        if (options.setupFunction) {
          utilInstance = options.setupFunction(scope, options);
        } else {
          var util = window.utils[utilName];
          if (!util) {
            throw new Error('"' + utilName + '" is not a known js util');
          }

          utilInstance = util(scope, options);
        }

        if (utilInstance) {
          if (activeInstances[utilName] && Array.isArray(activeInstances[utilName])) {
            activeInstances[utilName].push(utilInstance);
          } else {
            activeInstances[utilName] = [ utilInstance ];
          }
        }
      };

      if (registeredSetupListeners[utilName] && Array.isArray(registeredSetupListeners[utilName])) {
        window.utils.teardown(utilName);
      }

      if (!registeredSetupListeners[utilName] || Array.isArray(registeredSetupListeners[utilName])) {
        registeredSetupListeners[utilName] = [];
      }
      registeredSetupListeners[utilName].push(listener);

      document.addEventListener('setup', listener);

      document.dispatchEvent(new CustomEvent('setup', {
        detail: { targetUtil: utilName, module: 'none' },
        bubbles: true,
        cancelable: true,
      }));
    }

    setup();

    return utilInstance;
  };

  window.utils.teardown = function(utilName, destroy) {
    if (registeredSetupListeners[utilName]) {
      registeredSetupListeners[utilName]
        .filter(function(listener) { return !!listener })
        .forEach(function(listener) {
          document.removeEventListener('setup', listener);
        });
      delete registeredSetupListeners[utilName];
    }

    if (destroy === true && activeInstances[utilName]) {
      activeInstances[utilName]
        .filter(function(instance) { return !!instance })
        .forEach(function(instance) {
          instance.destroy();
        });
      delete activeInstances[utilName];
    }
  }
})();
