define(['connectionManager', 'userSettings', 'events'], function (connectionManager, userSettings, events) {
    'use strict';

    var allTranslations = {};
    var currentCulture;

    function getCurrentLocale() {

        return currentCulture;
    }

    function getDefaultLanguage() {

        var culture = document.documentElement.getAttribute('data-culture');

        if (culture) {
            return culture;
        }

        if (navigator.language) {
            return navigator.language;
        }
        if (navigator.userLanguage) {
            return navigator.userLanguage;
        }
        if (navigator.languages && navigator.languages.length) {
            return navigator.languages[0];
        }

        return 'en-us';
    }

    function updateCurrentCulture() {

        var culture;
        try {
            culture = userSettings.get('language');
        } catch (err) {

        }
        culture = culture || getDefaultLanguage();

        currentCulture = normalizeLocaleName(culture);

        ensureTranslations(currentCulture);
    }

    function ensureTranslations(culture) {

        for (var i in allTranslations) {
            ensureTranslation(allTranslations[i], culture);
        }
    }

    function ensureTranslation(translationInfo, culture) {

        if (translationInfo.dictionaries[culture]) {
            return Promise.resolve();
        }

        return loadTranslation(translationInfo.translations, culture).then(function (dictionary) {

            translationInfo.dictionaries[culture] = dictionary;
        });
    }

    function normalizeLocaleName(culture) {

        culture = culture.replace('_', '-');

        // If it's de-DE, convert to just de
        var parts = culture.split('-');
        if (parts.length === 2) {
            if (parts[0].toLowerCase() === parts[1].toLowerCase()) {
                culture = parts[0].toLowerCase();
            }
        }

        var lower = culture.toLowerCase();

        if (lower === 'ca-es') {
            return 'ca';
        }

        // normalize Swedish
        if (lower === 'sv-se') {
            return 'sv';
        }

        return lower;
    }

    function getDictionary(module) {

        if (!module) {
            module = defaultModule();
        }

        var translations = allTranslations[module];

        if (!translations) {
            return {};
        }

        return translations.dictionaries[getCurrentLocale()];
    }

    function register(options) {

        allTranslations[options.name] = {
            translations: options.strings || options.translations,
            dictionaries: {}
        };
    }

    function loadStrings(options) {

        var locale = getCurrentLocale();

        if (typeof options === 'string') {
            return ensureTranslation(allTranslations[options], locale);
        } else {
            register(options);
            return ensureTranslation(allTranslations[options.name], locale);
        }
    }

    var cacheParam = new Date().getTime();
    function loadTranslation(translations, lang) {

        lang = normalizeLocaleName(lang);

        var filtered = translations.filter(function (t) {
            return normalizeLocaleName(t.lang) === lang;
        });

        if (!filtered.length) {
            filtered = translations.filter(function (t) {
                return normalizeLocaleName(t.lang) === 'en-us';
            });
        }

        return new Promise(function (resolve, reject) {

            if (!filtered.length) {
                resolve();
                return;
            }

            var url = filtered[0].path;

            url += url.indexOf('?') === -1 ? '?' : '&';
            url += 'v=' + cacheParam;

            var xhr = new XMLHttpRequest();
            xhr.open('GET', url, true);

            xhr.onload = function (e) {
                if (this.status < 400) {
                    resolve(JSON.parse(this.response));
                } else {
                    resolve({});
                }
            };

            xhr.onerror = function () {
                resolve({});
            };
            xhr.send();
        });
    }

    function translateKey(key) {

        var parts = key.split('#');
        var module;

        if (parts.length > 1) {
            module = parts[0];
            key = parts[1];
        }

        return translateKeyFromModule(key, module);
    }

    function translateKeyFromModule(key, module) {

        var dictionary = getDictionary(module);

        if (!dictionary) {
            return key;
        }

        return dictionary[key] || key;
    }

    function replaceAll(str, find, replace) {

        return str.split(find).join(replace);
    }

    function translate(key) {

        var val = translateKey(key);

        for (var i = 1; i < arguments.length; i++) {

            val = replaceAll(val, '{' + (i - 1) + '}', arguments[i]);

        }

        return val;
    }

    function translateHtml(html, module) {

        if (!module) {
            module = defaultModule();
        }

        if (!module) {
            throw new Error('module cannot be null or empty');
        }

        var startIndex = html.indexOf('${');

        if (startIndex === -1) {
            return html;
        }

        startIndex += 2;
        var endIndex = html.indexOf('}', startIndex);

        if (endIndex === -1) {
            return html;
        }

        var key = html.substring(startIndex, endIndex);
        var val = translateKeyFromModule(key, module);

        html = html.replace('${' + key + '}', val);
        return translateHtml(html, module);
    }

    var _defaultModule;
    function defaultModule(val) {

        if (val) {

            _defaultModule = val;
        }

        return _defaultModule;
    }

    updateCurrentCulture();

    events.on(connectionManager, 'localusersignedin', updateCurrentCulture);
    events.on(userSettings, 'change', function (e, name) {
        if (name === 'language') {
            updateCurrentCulture();
        }
    });

    return {
        getString: translate,
        translate: translate,
        translateDocument: translateHtml,
        translateHtml: translateHtml,
        loadStrings: loadStrings,
        defaultModule: defaultModule,
        getCurrentLocale: getCurrentLocale,
        register: register
    };
});