define([

'jquery',
'require',

'./results',

'./selection/single',
'./selection/multiple',
'./selection/placeholder',
'./selection/allowClear',
'./selection/search',
'./selection/eventRelay',

'./utils',
'./translation',
'./diacritics',

'./data/select',
'./data/array',
'./data/ajax',
'./data/tags',
'./data/tokenizer',
'./data/minimumInputLength',
'./data/maximumInputLength',
'./data/maximumSelectionLength',

'./dropdown',
'./dropdown/search',
'./dropdown/hidePlaceholder',
'./dropdown/infiniteScroll',
'./dropdown/attachBody',
'./dropdown/minimumResultsForSearch',
'./dropdown/selectOnClose',
'./dropdown/closeOnSelect',

'./i18n/en'

], function ($, require,

           ResultsList,

           SingleSelection, MultipleSelection, Placeholder, AllowClear,
           SelectionSearch, EventRelay,

           Utils, Translation, DIACRITICS,

           SelectData, ArrayData, AjaxData, Tags, Tokenizer,
           MinimumInputLength, MaximumInputLength, MaximumSelectionLength,

           Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
           AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,

           EnglishTranslation) {
function Defaults () {
  this.reset();
}

Defaults.prototype.apply = function (options) {
  options = $.extend(true, {}, this.defaults, options);

  if (options.dataAdapter == null) {
    if (options.ajax != null) {
      options.dataAdapter = AjaxData;
    } else if (options.data != null) {
      options.dataAdapter = ArrayData;
    } else {
      options.dataAdapter = SelectData;
    }

    if (options.minimumInputLength > 0) {
      options.dataAdapter = Utils.Decorate(
        options.dataAdapter,
        MinimumInputLength
      );
    }

    if (options.maximumInputLength > 0) {
      options.dataAdapter = Utils.Decorate(
        options.dataAdapter,
        MaximumInputLength
      );
    }

    if (options.maximumSelectionLength > 0) {
      options.dataAdapter = Utils.Decorate(
        options.dataAdapter,
        MaximumSelectionLength
      );
    }

    if (options.tags) {
      options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
    }

    if (options.tokenSeparators != null || options.tokenizer != null) {
      options.dataAdapter = Utils.Decorate(
        options.dataAdapter,
        Tokenizer
      );
    }

    if (options.query != null) {
      var Query = require(options.amdBase + 'compat/query');

      options.dataAdapter = Utils.Decorate(
        options.dataAdapter,
        Query
      );
    }

    if (options.initSelection != null) {
      var InitSelection = require(options.amdBase + 'compat/initSelection');

      options.dataAdapter = Utils.Decorate(
        options.dataAdapter,
        InitSelection
      );
    }
  }

  if (options.resultsAdapter == null) {
    options.resultsAdapter = ResultsList;

    if (options.ajax != null) {
      options.resultsAdapter = Utils.Decorate(
        options.resultsAdapter,
        InfiniteScroll
      );
    }

    if (options.placeholder != null) {
      options.resultsAdapter = Utils.Decorate(
        options.resultsAdapter,
        HidePlaceholder
      );
    }

    if (options.selectOnClose) {
      options.resultsAdapter = Utils.Decorate(
        options.resultsAdapter,
        SelectOnClose
      );
    }
  }

  if (options.dropdownAdapter == null) {
    if (options.multiple) {
      options.dropdownAdapter = Dropdown;
    } else {
      var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);

      options.dropdownAdapter = SearchableDropdown;
    }

    if (options.minimumResultsForSearch !== 0) {
      options.dropdownAdapter = Utils.Decorate(
        options.dropdownAdapter,
        MinimumResultsForSearch
      );
    }

    if (options.closeOnSelect) {
      options.dropdownAdapter = Utils.Decorate(
        options.dropdownAdapter,
        CloseOnSelect
      );
    }

    if (
      options.dropdownCssClass != null ||
      options.dropdownCss != null ||
      options.adaptDropdownCssClass != null
    ) {
      var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');

      options.dropdownAdapter = Utils.Decorate(
        options.dropdownAdapter,
        DropdownCSS
      );
    }

    options.dropdownAdapter = Utils.Decorate(
      options.dropdownAdapter,
      AttachBody
    );
  }

  if (options.selectionAdapter == null) {
    if (options.multiple) {
      options.selectionAdapter = MultipleSelection;
    } else {
      options.selectionAdapter = SingleSelection;
    }

    // Add the placeholder mixin if a placeholder was specified
    if (options.placeholder != null) {
      options.selectionAdapter = Utils.Decorate(
        options.selectionAdapter,
        Placeholder
      );
    }

    if (options.allowClear) {
      options.selectionAdapter = Utils.Decorate(
        options.selectionAdapter,
        AllowClear
      );
    }

    if (options.multiple) {
      options.selectionAdapter = Utils.Decorate(
        options.selectionAdapter,
        SelectionSearch
      );
    }

    if (
      options.containerCssClass != null ||
      options.containerCss != null ||
      options.adaptContainerCssClass != null
    ) {
      var ContainerCSS = require(options.amdBase + 'compat/containerCss');

      options.selectionAdapter = Utils.Decorate(
        options.selectionAdapter,
        ContainerCSS
      );
    }

    options.selectionAdapter = Utils.Decorate(
      options.selectionAdapter,
      EventRelay
    );
  }

  if (typeof options.language === 'string') {
    // Check if the language is specified with a region
    if (options.language.indexOf('-') > 0) {
      // Extract the region information if it is included
      var languageParts = options.language.split('-');
      var baseLanguage = languageParts[0];

      options.language = [options.language, baseLanguage];
    } else {
      options.language = [options.language];
    }
  }

  if ($.isArray(options.language)) {
    var languages = new Translation();
    options.language.push('en');

    var languageNames = options.language;

    for (var l = 0; l < languageNames.length; l++) {
      var name = languageNames[l];
      var language = {};

      try {
        // Try to load it with the original name
        language = Translation.loadPath(name);
      } catch (e) {
        try {
          // If we couldn't load it, check if it wasn't the full path
          name = this.defaults.amdLanguageBase + name;
          language = Translation.loadPath(name);
        } catch (ex) {
          // The translation could not be loaded at all. Sometimes this is
          // because of a configuration problem, other times this can be
          // because of how Select2 helps load all possible translation files.
          if (options.debug && window.console && console.warn) {
            console.warn(
              'Select2: The language file for "' + name + '" could not be ' +
              'automatically loaded. A fallback will be used instead.'
            );
          }

          continue;
        }
      }

      languages.extend(language);
    }

    options.translations = languages;
  } else {
    var baseTranslation = Translation.loadPath(
      this.defaults.amdLanguageBase + 'en'
    );
    var customTranslation = new Translation(options.language);

    customTranslation.extend(baseTranslation);

    options.translations = customTranslation;
  }

  return options;
};

Defaults.prototype.reset = function () {
  function stripDiacritics (text) {
    // Used 'uni range + named function' from http://jsperf.com/diacritics/18
    function match(a) {
      return DIACRITICS[a] || a;
    }

    return text.replace(/[^\u0000-\u007E]/g, match);
  }

  function matcher (params, data) {
    // Always return the object if there is nothing to compare
    if ($.trim(params.term) === '') {
      return data;
    }

    // Do a recursive check for options with children
    if (data.children && data.children.length > 0) {
      // Clone the data object if there are children
      // This is required as we modify the object to remove any non-matches
      var match = $.extend(true, {}, data);

      // Check each child of the option
      for (var c = data.children.length - 1; c >= 0; c--) {
        var child = data.children[c];

        var matches = matcher(params, child);

        // If there wasn't a match, remove the object in the array
        if (matches == null) {
          match.children.splice(c, 1);
        }
      }

      // If any children matched, return the new object
      if (match.children.length > 0) {
        return match;
      }

      // If there were no matching children, check just the plain object
      return matcher(params, match);
    }

    var original = stripDiacritics(data.text).toUpperCase();
    var term = stripDiacritics(params.term).toUpperCase();

    // Check if the text contains the term
    if (original.indexOf(term) > -1) {
      return data;
    }

    // If it doesn't contain the term, don't return anything
    return null;
  }

  this.defaults = {
    amdBase: './',
    amdLanguageBase: './i18n/',
    closeOnSelect: true,
    debug: false,
    dropdownAutoWidth: false,
    escapeMarkup: Utils.escapeMarkup,
    language: EnglishTranslation,
    matcher: matcher,
    minimumInputLength: 0,
    maximumInputLength: 0,
    maximumSelectionLength: 0,
    minimumResultsForSearch: 0,
    selectOnClose: false,
    sorter: function (data) {
      return data;
    },
    templateResult: function (result) {
      return result.text;
    },
    templateSelection: function (selection) {
      return selection.text;
    },
    theme: 'default',
    width: 'resolve'
  };
};

Defaults.prototype.set = function (key, value) {
  var camelKey = $.camelCase(key);

  var data = {};
  data[camelKey] = value;

  var convertedData = Utils._convertData(data);

  $.extend(this.defaults, convertedData);
};

var defaults = new Defaults();

return defaults;

});