/// <reference path="http://localhost/IndustrialDashboard/Scripts/IndustrialDashboard.js" />
define([

    "backbone",
    "jed", 
    "app-config", 

    "bootstrap-amd",
    "js/backbone.routefilter",
    "js/backbone.caseinsensitiverouter",
    "js/jquery-ui-1.10.3", 
    "js/backbone.epoxy", 

    ], function (Backbone, Jed, app_config) {

    // Provide a global location to place configuration settings and module
    // creation.
    var app = {
        name: "boilerplate"
        // The root path to run the application.
        , root: "/"
        , foldersRoot: "/" //if null then same as root.
        , routerDefaultURL: "!/trending"
        , ConnectionStrings: {
            app: "APP"
        },
        views:{}, 
        models: {},
        custom_modules: {}, 
        customModules: ['*'],
        i18n: {
            global: null
        }, //i18n: using jed.js for internationalization client side (using gettext)
        //i18n: will contain translation jed objects for every view.

        //viewsCacheMaxLength: usually affects how many times you can press the "back" button and the DOM is not 
        //redrawn.
        viewsCacheMaxLength: 4,
        language: null,

        translate: null, 

        view_states: {
            loading: "LOADING",
            hidden: "HIDDEN",
            shown: "SHOWN",
            closed: "CLOSED", 
        },
        log: {
            common: null,
            debug: null,
            error: null, 
        }, 
    };

    if (app_config) {
       app = _.extend(app, app_config); 
    }

    //default value for foldersRoot
    app.foldersRoot = (app.foldersRoot != null) ? app.foldersRoot : app.root;
    
    /*    
    The app.CallProcedure method executes a procedure from the DB and handles errors and warnings, shows the user
    a pop up.

    To display a warning the procedure should use an errorNumber between 50000 and 51000 (not included)
    RAISERROR(errorNumber,11, 1)
    The warning will show a pop up with the message that the error contains and will prompt the user to continue
    if the user agrees then will call the same procedure again, with the exactly same parameters, adding a @ErrorIgnored = errorNumber param

    To display an error the procedure should use an errorNumber greater(or equal) 51000
    RAISERROR(errorNumber, 11, 1)
    The error will show a pop up with the error message with only one OK button.

    app.CallProcedure usage:
    * procedure: string with the procedure name as it is named on the db
    * queryParameters: an object Core.Database.QueryParameters
    * requestParams: 

    requestParams = {
        onSuccess: function(data){ ... } (the call procedure was executed succesfully, maybe a warning was shown previusly to the user and the user clicked on continue)
        , onFailure: function(data){ ... } (the call procedure failed for some unexpected reason, inspect data object for more information)
        , onCancel: function(){ ... } (the call procedure returned a warning, and the user pressed cancel to that warning, onSuccess will not be called.)
        , onError: function() { ... } (the call procedure returned an error (sql exception) that had code >= 51000 (considered an error), the error was shown to the user)
        , onFinally: function() { ... } (works just as the finally option in try...catch block, executes itself in EVERY case no matter what happened. probably will get one of the previous calls and this one. (eg. when you want to hide a loading icon))
    }

    * connectionString: string with the connectionstringnames as it is named on the web.config

    app.CallProcedure(procedure, queryParameters, requestParams, connectionString)
    */

    app.CallProcedure = function (procedure, queryParameters, requestParams, connectionString) {
        try {
            require(["modules/modal"], function (Modal) {
                Core.Json.CallProcedure(procedure, queryParameters, _.defaults({
                    onSuccess: function (a, b, c) {
                        try {
                            if (Core.Object.Exist(a, "ErrorNumber")) {
                                var err = a.ErrorNumber;

                                if (err >= 50000 && err < 51000) {
                                    //if between [50000; 51000) then it's a warning

                                    function _trans_confirmed() {
                                        try {
                                            queryParameters.Add("ErrorIgnored", "INT", err); 
                                            app.CallProcedure(procedure, queryParameters, requestParams, connectionString); 
                                        } catch (Error) { }
                                    }

                                    var modalWarning = new Modal.Views.Main({
                                        title: app.i18n.global.translate("modal_warning_title").fetch()
                                        , buttons_type: "CONTINUE-CANCEL"
                                        , message: a.Message
                                    });

                                    if (Core.Object.Exist(requestParams, 'onCancel')) {
                                        modalWarning.on("cancel", requestParams.onCancel); 
                                    }

                                    modalWarning.show(_trans_confirmed);

                                    if (Core.Object.Exist(requestParams, 'onFinally')) {
                                        requestParams.onFinally(a, b, c);
                                    }

                                } else {
                                    //else it's an error.

                                    var modalError = new Modal.Views.Main({
                                        title: app.i18n.global.translate("modal_error_title").fetch()
                                        , buttons_type: "OK"
                                        , message: a.Message
                                    });
                                    modalError.show();

                                    if (Core.Object.Exist(requestParams, 'onError')) {
                                        requestParams.onError(a, b, c); 
                                    }

                                    if (Core.Object.Exist(requestParams, 'onFinally')) {
                                        requestParams.onFinally(a, b, c);
                                    }

                                }
                            } else {
                                if (Core.Object.Exist(requestParams, "onSuccess")) {
                                    requestParams.onSuccess(a, b, c);
                                }

                                if (Core.Object.Exist(requestParams, 'onFinally')) {
                                    requestParams.onFinally(a, b, c);
                                }
                            }
                        } catch (Error) { }
                    },
                    onFailure: function (a, b, c) {
                        try {
                            if (Core.Object.Exist(requestParams, "onFailure")) {
                                requestParams.onFailure(a, b, c);
                            }

                            if (Core.Object.Exist(requestParams, 'onFinally')) {
                                requestParams.onFinally(a, b, c);
                            }
                        } catch (Error) { }
                    }
                }, requestParams), connectionString);
            }); 

        } catch (Error) { }
    }
    
    app.log.common = function (type, message, loggerName) {
        try {
            var method = ""; 
            type = (_.isString(type)) ? type.toUpperCase() : "DEBUG";
            loggerName = (_.isString(loggerName)) ? loggerName : "WebsiteLog";

            var pre_msg = ("[{{accountNumber}}] - [{{username}}] - ")
                            .replace("{{accountNumber}}", app.models.user.get("accountNumber"))
                            .replace("{{username}}", app.models.user.get("username"));

            message = pre_msg + message; 

            switch (type) {
                case "DEBUG":
                    method = "Debug"; 
                    break;
                case "ERROR":
                    method = "Error"; 
                    break; 
            }

            $.ajax({
                url: app.foldersRoot + "/Log.svc/" + method,
                //dataType: "json",
                contentType: "application/json",
                method: "POST", 
                data: JSON.stringify({ msg: message, loggerName: loggerName }), 
                success: function (data) {
                    return data; 
                },
                error: function (data) {
                    return data; 
                }, 
                async: true,
            });
        } catch (Error) { }
    }; 

    //call log using app.log.debug(this, msg) from modules (screens)
    app.log.debug = function (ref, msg) {
        loggerName = (_.isObject(ref) && ref.id) ? ref.id : null;
        msg = (_.isString(msg)) ? msg : (_.isString(ref)) ? ref : null;
        app.log.common("DEBUG", msg, loggerName);
    }
    //call log using app.log.error(this, msg) from modules (screens)
    app.log.error = function (ref, msg) {
        loggerName = (_.isObject(ref) && ref.id) ? ref.id : null;
        msg = (_.isString(msg)) ? msg : (_.isString(ref)) ? ref : null;
        app.log.common("ERROR", msg, loggerName);
    }

    app.translate = function (ref, key, fetchOptions) {
        try {
            var jed_object;

            if ($.isArray(ref) == false) {
                jed_object = (ref.i18n && ref.i18n.global)
                                    ? ref.i18n.global
                                    : (ref.options.i18n)
                                            ? ref.options.i18n[ref.template]
                                            : ref.i18n[ref.template];
            }
            else {
                var arrayRef = ref;

                for (var i = 0, len = arrayRef.length; i < len; i++) {
                    var ref = arrayRef[i];

                    jed_object = (ref.i18n && ref.i18n.global)
                                        ? ref.i18n.global
                                        : (ref.options) && (ref.options.i18n) && (ref.options.i18n[ref.template])
                                                ? ref.options.i18n[ref.template]
                                                : (ref.i18n) && (ref.i18n[ref.template])
                                                    ? ref.i18n[ref.template]
                                                    : ref.i18n;

                    if ((jed_object.options.locale_data[jed_object.options.domain][key]) || (jed_object.defaults.locale_data[jed_object.defaults.domain][key]))
                        return jed_object.translate(key).fetch(_.extend([], fetchOptions));
                }
            }

            return jed_object.translate(key).fetch(_.extend([], fetchOptions));
        } catch (error) {
            console.error(error);
            return "";
        }
    };

    //creating a default jed object, it's gonna be passed to every view that lacks of translation files.
    app.i18n.default_jed = new Jed({
        // You can choose to set the domain at instantiation time
        // If you don't, then "messages" will be used by default
        /*"domain": "messages",*/
        "missing_key_callback": function (key) {
            console.error(key)
        },
        "locale_data": {
            'messages': {
                '': {
                    'domain': 'messages'
                    , 'lang': 'en'
                    , 'plural_forms': 'nplurals=2; plural=(n != 1);'
                }
                , 'modal_warning_title': [null, "Warning"]
                , 'modal_error_title': [null, "Error"]
            }
        }
    });

    app.i18n.global = new Jed({
        // You can choose to set the domain at instantiation time
        // If you don't, then "messages" will be used by default
        "domain": "messages",
        // This callback is called when a key is missing
        "missing_key_callback": function (key) {
            // Do something with the missing key
            // e.g. send key to web service or
            console.error(key)
        },

        // This is the translation data, which is often generated by
        // a po2json converter. You would ideally have one per locale
        // and only pull in the locale_data that you need.
        "locale_data": (window && window["jed_locale_data"]) ? jed_locale_data : { 'messages': { '': { 'domain': 'messages', 'lang': 'en', 'plural_forms': '' } } }
    });

    app.getI18NJed = function (ins, templateName, onSuccess, hasI18N, customPath) {
        //scoped this
        var that = this;

        //default values
        hasI18N = typeof hasI18N !== 'undefined' ? hasI18N : true;
        customPath = typeof customPath !== 'undefined' ? customPath : null;

        var templateID = (customPath) ? customPath + templateName : templateName;

        if ((!this.i18n[templateID]) && hasI18N) {
            var urlForI18N = app.foldersRoot + '/app/i18n/resx2json.aspx?path=' + templateName + ((customPath) ? "&customPath=" + customPath : "");
            //Core.Json.Request(urlForI18N, {
            //    onSuccess: function (data) {
            //        if (data) {
            //            var i18nJED = new Jed({
            //                // You can choose to set the domain at instantiation time
            //                // If you don't, then "messages" will be used by default
            //                "domain": "messages",
            //                // This callback is called when a key is missing
            //                "missing_key_callback": function (key) {
            //                    // Do something with the missing key
            //                    // e.g. send key to web service or
            //                    console.error(key)
            //                },

            //                // This is the translation data, which is often generated by
            //                // a po2json converter. You would ideally have one per locale
            //                // and only pull in the locale_data that you need.
            //                "locale_data": data
            //            });
            //            //storing jed object for cache
            //            that.i18n[templateID] = i18nJED;

            //            onSuccess(that.i18n[templateID]);
            //        } else {
            //            if (onFailure) onFailure();
            //        }
            //    },
            //    CrossDomain: true,
            //    Async: false
            //});
            $.ajax({
                url: urlForI18N,
                dataType: "json",
                success: function (data) {
                    if (data) {
                        var i18nJED = new Jed({
                            // You can choose to set the domain at instantiation time
                            // If you don't, then "messages" will be used by default
                            "domain": "messages",
                            // This callback is called when a key is missing
                            "missing_key_callback": function (key) {
                                // Do something with the missing key
                                // e.g. send key to web service or
                                console.error(key)
                            },

                            // This is the translation data, which is often generated by
                            // a po2json converter. You would ideally have one per locale
                            // and only pull in the locale_data that you need.
                            "locale_data": data
                        });
                        //storing jed object for cache
                        that.i18n[templateID] = i18nJED;

                        onSuccess(that.i18n[templateID]);
                    } else {
                        //if (onFailure) onFailure();
                    }
                },
                async: false,
            });
        } else if (this.i18n[templateID]) {
            onSuccess(this.i18n[templateID]);
        } else if (!hasI18N) {
            onSuccess(this.i18n.default_jed);
        }
    }; 

    app.serverResponseToConsole = function (resp, checkData) {
        try
        {
            if (resp == null) {
                console.warn("No response from server.");
            }
            else if (resp.Success == false) {
                console.warn(
                    (resp.Message)
                        ? resp.Message
                        : "Internal server error."
                );
            }
            else if ((checkData === true) && (resp.Data == null)) {
                console.warn("Server response is not valid.");
            }
            else {
                console.debug("Success.");
            }
        }
        catch (Error) { console.error(Error); }
    };

    app.jsonArrayToXml = function (data, cols) {
        function _toString(val) {
            return (val == null) ? "" : val.toString();
        }

        cols = (_.isArray(cols)) ? cols : [];
        cols = _.invoke(cols, "toUpperCase");

        data = (_.isArray(data)) ? data : [data];

        var xml = null;
        try {
            xml = '<XMLRoot>';
            for (var r = 0; r < data.length; r++) {
                rowObj = data[r];
                var row = '<Row ';
                for (var c in rowObj) {
                    if (rowObj[c] != null) {
                        if ((cols.length == 0) || (cols.length > 0 && _.indexOf(cols, c.toString().toUpperCase()) != -1)) {
                            row += ((Trim(c).replace(' ', '')).replace('[', '')).replace(']', '') + '="' + _toString(rowObj[c]) + '" ';
                        }
                    }
                }
                row += '/>';
                row = row.replace(/#/g, '');
                xml += row;
            }
            xml += '</XMLRoot>';
        } catch (Error) {
        }
        return xml;
    };

    app.getRoutes = function (group, fixRoutes, async, callback) {
        var that = this; 
        group = _.isString(group) ? group : null;
        async = (_.isBoolean(async)) ? async : false; 

        var QP = Core.Database.QueryParameters();
        QP.Add("Group", "VARCHAR", group);

        var route_IDs = {}; 
        Core.Json.CallProcedure("FrontEnd.GetRoutes", QP, {
            onSuccess: function (data) {
                if (Core.Object.Eval(data, 'Table')) {
                    var data = data.Table;
                    for (var x = 0; x < data.length; x++) {
                        var route = data[x].Route;

                        //Check if the route has an optional slash at the end. If not, add it to prevent issues if the routed
                        //route has it or not.
                        if (fixRoutes) {
                            var match = route.match(new RegExp("^.+[^\\(](\\(?)(\\/)(\\)?)$"));
                            if (match == null) {
                                route += "(/)";
                            }
                            else if ((match[1] == "") && (match[3] == "")) {
                                //Means that the route ends with a slash but not optional. make it optional.
                                route = route.replace(new RegExp("^(.+)\\/$"), "$1(/)");
                            }
                        }

                        if (!route_IDs[data[x].ModuleName]) {
                            route_IDs[data[x].ModuleName] = {
                                Name: data[x].ModuleName,
                                Route: route,
                                HasChildren: false,
                                ModulePath: data[x].ModulePath,
                            };
                        }
                    }
                }

                if (async && callback && _.isFunction(callback)) {
                    callback.call(that, that, route_IDs);
                }
            }
            , Async: async
        }, app.ConnectionStrings.app);

        if (!async) return route_IDs; 
    }; 

    return app; 

});

//avoid "trim" errors in old browsers (<=ie8)
(function () {
    if (typeof String.prototype.trim !== 'function') {
        String.prototype.trim = function () {
            return this.replace(/^\s+|\s+$/g, '');
        }
    }
}()); 

// Avoid `console` errors in browsers that lack a console.
// HTML5 Boilerplate
(function () {
    var method;
    var noop = function () { };
    var methods = [
        'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
        'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
        'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
        'timeStamp', 'trace', 'warn'
    ];
    var length = methods.length;
    var console = (window.console = window.console || {});

    while (length--) {
        method = methods[length];

        // Only stub undefined methods.
        if (!console[method]) {
            console[method] = noop;
        }
    }
}());