import isArray from "../utils/is-array.js";
import isUndefined from "../utils/is-undefined.js";
import { deprecateSimple } from "../utils/deprecate.js";
import { mergeConfigs } from "./set.js";
import { Locale } from "./constructor.js";
import keys from "../utils/keys.js";
import { baseConfig } from "./base-config.js"; // internal storage for locale config files

var locales = {},
    localeFamilies = {},
    globalLocale;

function commonPrefix(arr1, arr2) {
  var i,
      minl = Math.min(arr1.length, arr2.length);

  for (i = 0; i < minl; i += 1) {
    if (arr1[i] !== arr2[i]) {
      return i;
    }
  }

  return minl;
}

function normalizeLocale(key) {
  return key ? key.toLowerCase().replace('_', '-') : key;
} // pick the locale from the array
// try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each
// substring from most specific to least, but move to the next array item if it's a more specific variant than the current root


function chooseLocale(names) {
  var i = 0,
      j,
      next,
      locale,
      split;

  while (i < names.length) {
    split = normalizeLocale(names[i]).split('-');
    j = split.length;
    next = normalizeLocale(names[i + 1]);
    next = next ? next.split('-') : null;

    while (j > 0) {
      locale = loadLocale(split.slice(0, j).join('-'));

      if (locale) {
        return locale;
      }

      if (next && next.length >= j && commonPrefix(split, next) >= j - 1) {
        //the next array item is better than a shallower substring of this one
        break;
      }

      j--;
    }

    i++;
  }

  return globalLocale;
}

function isLocaleNameSane(name) {
  // Prevent names that look like filesystem paths, i.e contain '/' or '\'
  return name.match('^[^/\\\\]*$') != null;
}

function loadLocale(name) {
  var oldLocale = null,
      aliasedRequire; // TODO: Find a better way to register and load all the locales in Node

  if (locales[name] === undefined && typeof module !== 'undefined' && module && module.exports && isLocaleNameSane(name)) {
    try {
      oldLocale = globalLocale._abbr;
      aliasedRequire = require;
      aliasedRequire('./locale/' + name);
      getSetGlobalLocale(oldLocale);
    } catch (e) {
      // mark as not found to avoid repeating expensive file require call causing high CPU
      // when trying to find en-US, en_US, en-us for every format call
      locales[name] = null; // null means not found
    }
  }

  return locales[name];
} // This function will load locale and then set the global locale.  If
// no arguments are passed in, it will simply return the current global
// locale key.


export function getSetGlobalLocale(key, values) {
  var data;

  if (key) {
    if (isUndefined(values)) {
      data = getLocale(key);
    } else {
      data = defineLocale(key, values);
    }

    if (data) {
      // moment.duration._locale = moment._locale = data;
      globalLocale = data;
    } else {
      if (typeof console !== 'undefined' && console.warn) {
        //warn user if arguments are passed but the locale could not be set
        console.warn('Locale ' + key + ' not found. Did you forget to load it?');
      }
    }
  }

  return globalLocale._abbr;
}
export function defineLocale(name, config) {
  if (config !== null) {
    var locale,
        parentConfig = baseConfig;
    config.abbr = name;

    if (locales[name] != null) {
      deprecateSimple('defineLocaleOverride', 'use moment.updateLocale(localeName, config) to change ' + 'an existing locale. moment.defineLocale(localeName, ' + 'config) should only be used for creating a new locale ' + 'See http://momentjs.com/guides/#/warnings/define-locale/ for more info.');
      parentConfig = locales[name]._config;
    } else if (config.parentLocale != null) {
      if (locales[config.parentLocale] != null) {
        parentConfig = locales[config.parentLocale]._config;
      } else {
        locale = loadLocale(config.parentLocale);

        if (locale != null) {
          parentConfig = locale._config;
        } else {
          if (!localeFamilies[config.parentLocale]) {
            localeFamilies[config.parentLocale] = [];
          }

          localeFamilies[config.parentLocale].push({
            name: name,
            config: config
          });
          return null;
        }
      }
    }

    locales[name] = new Locale(mergeConfigs(parentConfig, config));

    if (localeFamilies[name]) {
      localeFamilies[name].forEach(function (x) {
        defineLocale(x.name, x.config);
      });
    } // backwards compat for now: also set the locale
    // make sure we set the locale AFTER all child locales have been
    // created, so we won't end up with the child locale set.


    getSetGlobalLocale(name);
    return locales[name];
  } else {
    // useful for testing
    delete locales[name];
    return null;
  }
}
export function updateLocale(name, config) {
  if (config != null) {
    var locale,
        tmpLocale,
        parentConfig = baseConfig;

    if (locales[name] != null && locales[name].parentLocale != null) {
      // Update existing child locale in-place to avoid memory-leaks
      locales[name].set(mergeConfigs(locales[name]._config, config));
    } else {
      // MERGE
      tmpLocale = loadLocale(name);

      if (tmpLocale != null) {
        parentConfig = tmpLocale._config;
      }

      config = mergeConfigs(parentConfig, config);

      if (tmpLocale == null) {
        // updateLocale is called for creating a new locale
        // Set abbr so it will have a name (getters return
        // undefined otherwise).
        config.abbr = name;
      }

      locale = new Locale(config);
      locale.parentLocale = locales[name];
      locales[name] = locale;
    } // backwards compat for now: also set the locale


    getSetGlobalLocale(name);
  } else {
    // pass null for config to unupdate, useful for tests
    if (locales[name] != null) {
      if (locales[name].parentLocale != null) {
        locales[name] = locales[name].parentLocale;

        if (name === getSetGlobalLocale()) {
          getSetGlobalLocale(name);
        }
      } else if (locales[name] != null) {
        delete locales[name];
      }
    }
  }

  return locales[name];
} // returns locale data

export function getLocale(key) {
  var locale;

  if (key && key._locale && key._locale._abbr) {
    key = key._locale._abbr;
  }

  if (!key) {
    return globalLocale;
  }

  if (!isArray(key)) {
    //short-circuit everything else
    locale = loadLocale(key);

    if (locale) {
      return locale;
    }

    key = [key];
  }

  return chooseLocale(key);
}
export function listLocales() {
  return keys(locales);
}