//! Copyright (c) Microsoft Corporation. All rights reserved.

if (!window.Microsoft) { window.Microsoft = { __namespace:true }; }
if (!window.Microsoft.Live) { window.Microsoft.Live = { __namespace:true }; }
if (!window.Microsoft.Live.Core) { window.Microsoft.Live.Core = { __namespace:true }; }

Microsoft.Live.Core.Loader = function() {
    this._detectBrowsers();
    this._addDocumentReadyListener();
    this._resolveMarket();
    this._resolveDirection();
}

Microsoft.Live.Core.Loader.prototype = {
    _loadInitiated: false,
    _paused: false,
    _settings: {},
    _loadRequests: [],
    _readyRequests: [],
    _domRequests: [],
    _documentReady: false,
    _browser: {},
    _resourceMarket: null,
    _direction: "ltr",
    _isLocalizedStyleMarket: false,

    // Markets must be sorted.
    _supportedMarkets: [
        "ar", "ar-sa-psloc", "bg", "ca", "cs", "da", "de", "de-de-psloc", "el", "en", "es", "et", "eu", "fi",
        "fr", "gu", "he", "hi", "hr", "hu", "id", "it", "ja", "ja-jp-psloc", "kn", "ko", "lt", "lv", "ml",
        "mr", "ms", "nb-no", "nl", "pl", "pt-br", "pt-pt", "ro", "ru", "sk", "sl", "sr", "sr-cyrl-cs", "sv",
        "ta", "te", "th", "tr", "uk", "vi", "zh-cn", "zh-hk", "zh-tw"],

    // Markets which require stylesheet localization.
    _localizedStyleMarkets: ["ja", "ko", "zh-hk", "zh-tw"],

    _resources: {
        "scriptsharp_compat": {
            filename: "{configuration}/sscompat.js",
            type: "script",
            browsers: ["opera", "webkit", "firefox"],
            objectType: "ScriptLoader",
            dependencies: []
        },
        "scriptsharp": {
            filename: "{configuration}/sscorlib.js",
            type: "script",
            objectType: "ScriptLoader",
            dependencies: ["scriptsharp_compat"]
        },
        "mesh": {
            filename: "{configuration}/Microsoft.Live.Mesh.Framework.js",
            type: "script",
            dependencies: ["scriptsharp"]
        },
        "messenger_channel": {
            filename: "{configuration}/channel.js",
            type: "script",
            objectType: "postMessage",
            dependencies: []
        },
        "messenger_core": {
            filename: "{configuration}/Microsoft.Live.Messenger.Model.js",
            type: "script",
            dependencies: ["scriptsharp", "messenger_channel"]
        },
        "messenger_ui_primitives": {
            filename: "{market}/{configuration}/Microsoft.Live.Messenger.UI.Primitives.js",
            type: "script",
            dependencies: ["mesh"]
        },
        "messenger_ui_controls": {
            filename: "{market}/{configuration}/Microsoft.Live.Messenger.UI.Controls.js",
            type: "script",
            dependencies: ["mesh", "messenger_ui_primitives", "messenger_core"]
        },
        "messenger_ui": {
            filename: "{configuration}/Microsoft.Live.Messenger.UI.Tags.js",
            type: "script",
            dependencies: ["messenger_ui_controls"]
        },
        "messenger_ui_styles_core": {
            type: "stylesheet",
            dependencies: ["messenger_ui_styles_core_ie6",
                           "messenger_ui_styles_core_ie7",
                           "messenger_ui_styles_core_ie8",
                           "messenger_ui_styles_core_other"]
        },
        "messenger_ui_styles_core_ie6": {
            filename: "resources/styles/{direction}/core.ie6.{market}.css",
            type: "stylesheet",
            browsers: ["ie6"],
            dependencies: []
        },
        "messenger_ui_styles_core_ie7": {
            filename: "resources/styles/{direction}/core.ie7.{market}.css",
            type: "stylesheet",
            browsers: ["ie7"],
            dependencies: []
        },
        "messenger_ui_styles_core_ie8": {
            filename: "resources/styles/{direction}/core.ie8.{market}.css",
            type: "stylesheet",
            browsers: ["ie8"],
            dependencies: []
        },
        "messenger_ui_styles_core_other": {
            filename: "resources/styles/{direction}/core.other.{market}.css",
            type: "stylesheet",
            browsers: ["!ie"],
            dependencies: []
        }
    },

    get_settings: function() {
        return this._settings;
    },

    get_documentReady: function() {
        return this._documentReady;
    },

    initialize: function(settings) {
        if (!settings) {
            return;
        }
        if (this._loadInitiated) {
            throw new Error("Cannot invoke initialize() after load() has been invoked");
        }
        if (settings.configuration) {
            this._settings.configuration = settings.configuration.toLowerCase();
        }
        if (settings.version) {
            this._settings.version = settings.version.toLowerCase();
        }
        if (settings.resourcePath) {
            this._settings.resourcePath = settings.resourcePath.toLowerCase();

            if (!settings.styleResourcePath) {
                this._settings.styleResourcePath = null;
            }
        }
        if (settings.styleResourcePath) {
            this._settings.styleResourcePath = settings.styleResourcePath.toLowerCase();
        }
        if (settings.applicationPath) {
            this._settings.applicationPath = settings.applicationPath.toLowerCase();
        }
        if (settings.serviceHostName) {
            this._settings.serviceHostName = settings.serviceHostName.toLowerCase();
        }
        if (settings.consentHostName) {
            this._settings.consentHostName = settings.consentHostName.toLowerCase();
        }
        if (settings.market) {
            this._resolveMarket(settings);
        }

        window.__messengerPath =
            this._settings.applicationPath +
            this._settings.version + "/";

        window.__messengerScriptPath =
            this._settings.resourcePath +
            this._settings.version + "/";

        if (this._settings.configuration) {
            window.__messengerScriptPath += this._settings.configuration + "/";
        }

        if (this._settings.serviceHostName) {
            window.__messengerSignInServer = this._settings.serviceHostName;
        }

        if (this._settings.consentHostName) {
            window.__messengerConsentServer = this._settings.consentHostName;
        }
    },

    addScript: function(scriptName, url, dependencies) {
        this._addFeature(scriptName, "script", url, dependencies);
    },

    load: function(features, callback) {
        if (!features || features.length <= 0) {
            throw new Error("No features provided");
        }

        var resources = [];

        for (var i = 0; i < features.length; i++) {
            if (features[i].indexOf('_') >= 0) {
                throw new Error("Feature '%1' does not exist".replace("%1", features[i]));
            }

            var resource = this._normalizeName(features[i]);

            if (!this._resources[resource]) {
                throw new Error("Feature '%1' does not exist".replace("%1", resource));
            }

            resources.push(resource);
        }

        this._loadInitiated = true;

        this._loadRequests.push({ "resources": resources, "callback": callback });

        this._process();
    },

    onReady: function(callback) {
        if (!callback) {
            throw new Error("No callback provided");
        }

        if (this._loadRequests.length == 0) {
            this._invokeCallback(callback);
            return;
        }

        this._readyRequests.push({ "callback": callback });
    },

    onDocumentReady: function(callback) {
        if (!callback) {
            throw new Error("No callback provided");
        }

        if (this._documentReady) {
            this._invokeCallback(callback);
            return;
        }

        this._domRequests.push({ "callback": callback });
    },

    pause: function() {
        this._paused = true;
    },

    resume: function() {
        this._paused = false;

        this._process();
    },

    _addFeature: function(featureName, featureType, url, dependencies) {
        if (!featureName || featureName.length == 0) {
            throw new Error("featureName must not be empty");
        }

        if (featureName.indexOf('_') >= 0) {
            throw new Error("featureName cannot contain '_'");
        }

        if (!url || url.length == 0) {
            throw new Error("fileName must not be empty");
        }

        featureName = this._normalizeName(featureName);

        if (this._resources[featureName]) {
            throw new Error("Feature already exists");
        }

        var normalized = [];

        if (dependencies) {
            for (var i = 0; i < dependencies.length; i++) {
                normalized.push(this._normalizeName(dependencies[i]));
            }
        }

        this._resources[featureName] = {
            "url": url,
            "type": featureType,
            "dependencies": normalized
        };
    },

    _normalizeName: function(name) {
        return name.toLowerCase().replace(/\./g, "_");
    },

    _process: function() {
        if (this._paused) {
            return;
        }

        var callbacks = [];

        for (var i = 0; i < this._loadRequests.length; i++) {
            var loadRequest = this._loadRequests[i];

            var pendingCount = 0;

            for (var j = 0; j < loadRequest.resources.length; j++) {
                pendingCount += this._loadResources(this._resources[loadRequest.resources[j]]);
            }

            if ((pendingCount == 0) && this._documentReady) {
                // Even if we've loaded everything, we need to wait for the document to load.
                if (loadRequest.callback) {
                    callbacks.push(loadRequest.callback);
                }

                // Since we're done, remove this request. 
                this._loadRequests.splice(i--, 1);
            }
        }

        if (this._loadRequests.length == 0) {
            while (this._readyRequests.length > 0) {
                var readyRequest = this._readyRequests.shift();
                callbacks.push(readyRequest.callback);
            }
        }

        while (callbacks.length > 0) {
            var callback = callbacks.shift();
            this._invokeCallback(callback);
        }
    },

    _loadResources: function(resource) {
        var pendingCount = 0;

        switch (resource.readyState) {
            case "loaded":
                pendingCount = 0;
                break;

            case "loading":
                pendingCount = 1;
                break;

            default:
                for (var i = 0; i < resource.dependencies.length; i++) {
                    pendingCount += this._loadResources(
                        this._resources[resource.dependencies[i]]);
                }

                if (pendingCount == 0) {
                    this._loadResource(resource);
                    pendingCount++;
                }
                break;
        }

        return pendingCount;
    },

    _loadResource: function(resource) {
        resource.readyState = "loading";

        if (!resource.filename && !resource.url) {
            this._onResourceLoaded(resource);
            return;
        }

        if (!this._browserRequires(resource)) {
            this._onResourceLoaded(resource);
            return;
        }

        switch (resource.type) {
            case "script":
                this._loadScript(resource);
                break;
            case "stylesheet":
                this._loadStyleSheet(resource);
                break;
        }
    },

    _browserRequires: function(resource) {
        if (!resource.browsers) {
            return true;
        }

        for (var i = 0; i < resource.browsers.length; i++) {
            var b = resource.browsers[i];
            if (b.substr(0, 1) == "!") {
                if (!this._browser[b.substr(1)]) {
                    return true;
                }
            }
            else if (this._browser[b]) {
                return true;
            }
        }

        return false;
    },

    _loadScript: function(resource) {
        if (resource.objectType) {
            // Check to see if a type defined within the resource is loaded already.
            if (window[resource.objectType] !== undefined) {
                this._onResourceLoaded(resource);
                return;
            }
        }

        var loader = this;

        var element = document.createElement("SCRIPT");
        if (this._browser.ie) {
            element.attachEvent(
                "onreadystatechange",
                function() { loader._onScriptLoad(window.event, resource); });
        }
        else {
            element.readyState = "complete";
            element.addEventListener(
                "load",
                function(e) { loader._onScriptLoad(e, resource); },
                false);
        }

        element.type = "text/javascript";
        element.src = this._getUrl(resource);

        document.getElementsByTagName("HEAD")[0].appendChild(element);
    },

    _loadStyleSheet: function(resource) {
        var element = document.createElement("LINK");
        element.type = "text/css";
        element.rel = "stylesheet";
        element.media = "screen";
        element.href = this._getUrl(resource);

        document.getElementsByTagName("HEAD")[0].appendChild(element);

        this._onResourceLoaded(resource);
    },

    _getUrl: function(resource) {
        if (resource.url) {
            return resource.url;
        }

        var url = "";
        var settings = this._settings;

        if ((resource.type == "stylesheet") && settings.styleResourcePath) {
            url += settings.styleResourcePath;
        }
        else {
            url += settings.resourcePath;
        }

        url += settings.version;
        url += "/";
        url += resource.filename;

        url = url.replace(/{configuration}/g, settings.configuration);
        url = url.replace(/{direction}/g, this._direction);

        if ((resource.type == "stylesheet") && !this._isLocalizedStyleMarket) {
            url = url.replace(/{market}/g, "other");
        }
        else {
            url = url.replace(/{market}/g, this._resourceMarket);
        }

        return url;
    },

    _onScriptLoad: function(e, resource) {
        if (resource.readyState == "loaded") {
            return;
        }
        var element = e.srcElement || e.currentTarget;
        if (!element.readyState) {
            element = e.currentTarget;
        }

        if ((element.readyState != "complete") &&
            (element.readyState != "loaded")) {
            return;
        }

        this._onResourceLoaded(resource);
    },

    _onResourceLoaded: function(resource) {
        resource.readyState = "loaded";
        this._process();
    },

    _normalizeMarket: function(market) {
        market = market || "";

        market = market.toLowerCase();

        if (this._getResourceMarket(market)) {
            return market;
        }

        return null;
    },

    _getResourceMarket: function(market) {
        if (this._supportsMarket(market)) {
            return market;
        }

        var idx = market.lastIndexOf("-");
        if (idx < 0) {
            return null;
        }

        market = market.substr(0, idx);

        if (this._supportsMarket(market)) {
            return market;
        }

        return null;
    },

    _supportsMarket: function(market) {
        var low = 0;
        var high = this._supportedMarkets.length - 1;

        while (low <= high) {
            var i = Math.floor((low + high) / 2);

            var curr = this._supportedMarkets[i];

            if (curr < market) {
                low = i + 1;
                continue;
            }
            if (curr > market) {
                high = i - 1;
                continue;
            }

            return true;
        }

        return false;
    },

    _resolveDirection: function() {
        var direction = "ltr";

        var elements = document.getElementsByTagName("html");
        if (elements && elements.length > 0) {
            direction = elements[0].getAttribute("dir") || "";
        }

        direction = direction.toLowerCase();

        switch (direction) {
            case "ltr":
            case "rtl":
                this._direction = direction;
                break;
        }
    },

    _resolveMarket: function(settings) {
        var market = "";

        // 1. Check new settings
        if (settings) {
            if (this._tryUpdateMarket(settings.market)) {
                return;
            }
        }

        // 2. Check old settings
        if (this._settings.market) {
            return;
        }

        var elements = document.getElementsByTagName("html");
        var element = (elements && elements.length > 0) ? elements[0] : null;

        // 3. Check html xml:lang attribute
        if (element) {
            market = element.getAttribute("xml:lang") || "";
        }

        if (this._tryUpdateMarket(market)) {
            return;
        }

        // 4. Check html lang attribute
        if (element) {
            market = element.getAttribute("lang") || "";
        }

        if (this._tryUpdateMarket(market)) {
            return;
        }

        // 5. Use the default (English)
        this._tryUpdateMarket("en-us");
    },

    _tryUpdateMarket: function(market) {
        market = this._normalizeMarket(market);
        if (market) {
            this._settings.market = market;
            this._resourceMarket = this._getResourceMarket(market);
            this._isLocalizedStyleMarket = false;

            for (var i = 0; i < this._localizedStyleMarkets.length; i++) {
                if (this._resourceMarket == this._localizedStyleMarkets[i]) {
                    this._isLocalizedStyleMarket = true;
                    break;
                }
            }

            return true;
        }

        return false;
    },

    _detectBrowsers: function() {
        var ua = navigator.userAgent.toLowerCase();

        this._browser = {
            "firefox": /firefox/.test(ua),
            "firefox1.5": /firefox\/1\.5/.test(ua),
            "firefox2": /firefox\/2/.test(ua),
            "firefox3": /firefox\/3/.test(ua),
            "ie": /msie/.test(ua) && !/opera/.test(ua),
            "ie6": /msie 6/.test(ua) && !/opera/.test(ua),
            "ie7": /msie 7/.test(ua) && !/opera/.test(ua),
            "ie8": /trident\/4/.test(ua) && !/msie 7/.test(ua) && !/opera/.test(ua),
            "ie8compat": /msie 7/.test(ua) && /trident\/4/.test(ua) && !/opera/.test(ua),
            "opera": /opera/.test(ua),
            "webkit": /webkit/.test(ua)
        };
    },

    _addDocumentReadyListener: function() {
        var loader = this;
        if (document.addEventListener) {
            document.addEventListener("DOMContentLoaded", function() { loader._onDocumentReady(); }, false);
            document.addEventListener("load", function() { loader._onDocumentReady(); }, false);
        }
        else if (window.attachEvent) {
            window.attachEvent("onload", function() { loader._onDocumentReady(); });
        }

        if (this._browser.ie) {
            document.attachEvent("onreadystatechange", function() {
                if (document.readyState === "complete") {
                    document.detachEvent("onreadystatechange", arguments.callee);
                    loader._onDocumentReady();
                }
            });
        }
    },

    _invokeCallback: function(callback) {
        window.setTimeout(callback, 1);
    },

    _onDocumentReady: function() {
        if (this._documentReady == false) {
            this._documentReady = true;

            var callbacks = [];

            while (this._domRequests.length > 0) {
                var domRequest = this._domRequests.shift();
                callbacks.push(domRequest.callback);
            }

            while (callbacks.length > 0) {
                var callback = callbacks.shift();
                this._invokeCallback(callback);
            }

            this._process();
        }
    }
}

Microsoft.Live.Core.Loader = new Microsoft.Live.Core.Loader();

Microsoft.Live.Core.Loader.initialize({
    "version": "3.10.4601.0000",
    "configuration": "retail",
    "resourcePath": "http://www.wlmessenger.net/api/",
    "styleResourcePath": "http://css.wlmessenger.net/api/",
    "applicationPath": "http://settings.messenger.live.com/api/",
    "consentHostName": "consent.messenger.services.live.com"
});
