/// <reference path="http://localhost/IndustrialDashboard/Scripts/IndustrialDashboard.js" />
define((loadCustomConfigs()).concat(["underscore"]), function () {
    // Provide a global location to place configuration settings and module
    // creation.
    var app_config = {
        name: "ihBoxSystem"
        // The root path to run the application.
        , root: "/IHBox"
        , foldersRoot: "/static" //if null then same as root.
        , idRoot: ""
        , routerDefaultURL: "!/lines-production"
        , ConnectionStrings: {
            app: "APP"
        }
        , customModules: [
            "*"
        ]
        , customConfigs: [
            "*"
        ] 
        //viewsCacheMaxLength: usually affects how many times you can press the "back" button and the DOM is not 
        //redrawn.
        , viewsCacheMaxLength: 4
        , DatabaseNames: {
            IH: "[IH_100020]"
            , System: "[IHBoxSystem]"
        }
        , returnURL: null
        , returnURLAppendNext: null
    };

    app_config.windowsAuthFoldersRoot = "/IHBox/IHBoxWindowsAuth";
    app_config.exportExcelUrl = location.origin + "/IHBox/IndustrialDashboard/DALService.svc/ExportToExcel";

    var app_config_arguments = arguments; 
    var custom_config_paths = (window && window["custom_configs_paths"]) ? custom_configs_paths : []; 
    var customConfigs = app_config.customConfigs;
    if (customConfigs && _.isArray(customConfigs)) {
        if ((_.indexOf(customConfigs, '*')) != -1) {
            customConfigs = _.without(customConfigs, '*');
            customConfigs = _.union(customConfigs, custom_config_paths);
        }
    }

    var customConfigsModules = _.map(customConfigs, function (t) {
        try{
            var iof = _.indexOf(custom_config_paths, t);
            if (iof != -1) {
                return (app_config_arguments[iof]["config"]) ? app_config_arguments[iof].config(app_config) : app_config_arguments[iof]; 
            } else { return {}; }
        } catch (error) {
            console.log("Error while loading custom module: [" + t + "] . Skipping module and continuing with loading process."); 
        }
    }); 

    deepExtend.apply(this, _.union(app_config, customConfigsModules)); 
    return app_config; 
});

function loadCustomConfigs(callback) {
    var t = [];
    try{
        var customConfigs = (window && window["custom_configs_paths"]) ? custom_configs_paths : [];
        for (var i = 0, len = customConfigs.length; i < len; i++) {
            t.push("custom-configs/" + customConfigs[i]);
        }
    } catch (Error) {
        console.error("Error while trying to read config paths."); 
    }
    return t;
}

function deepExtend(obj) {
    var parentRE = /#{\s*?_\s*?}/,
        source,

        isAssign = function (oProp, sProp) {
            return (_.isUndefined(oProp) || _.isNull(oProp) || _.isFunction(oProp) || _.isNull(sProp) || _.isDate(sProp));
        },

        procAssign = function (oProp, sProp, propName) {
            // Perform a straight assignment
            // Assign for object properties & return for array members
            return obj[propName] = _.clone(sProp);
        },

        hasRegex = function (oProp, sProp) {
            return (_.isString(sProp) && parentRE.test(sProp));
        },

        procRegex = function (oProp, sProp, propName) {
            // Perform a string.replace using parentRE if oProp is a string
            if (!_.isString(oProp)) {
                // We're being optimistic at the moment
                // throw new Error('Trying to combine a string with a non-string (' + propName + ')');
            }
            // Assign for object properties & return for array members
            return obj[propName] = sProp.replace(parentRE, oProp);
        },

        hasArray = function (oProp, sProp) {
            return (_.isArray(oProp) || _.isArray(sProp));
        },

        procArray = function (oProp, sProp, propName) {
            // extend oProp if both properties are arrays
            if (!_.isArray(oProp) || !_.isArray(sProp)) {
                throw new Error('Trying to combine an array with a non-array (' + propName + ')');
            }
            var tmp = deepExtend(obj[propName], sProp);
            // Assign for object properties & return for array members
            return obj[propName] = _.reject(tmp, _.isNull);
        },

        hasObject = function (oProp, sProp) {
            return (_.isObject(oProp) || _.isObject(sProp));
        },

        procObject = function (oProp, sProp, propName) {
            // extend oProp if both properties are objects
            if (!_.isObject(oProp) || !_.isObject(sProp)) {
                throw new Error('Trying to combine an object with a non-object (' + propName + ')');
            }
            // Assign for object properties & return for array members
            return obj[propName] = deepExtend(oProp, sProp);
        },

        procMain = function (propName) {
            var oProp = obj[propName],
                sProp = source[propName];

            // The order of the 'if' statements is critical

            // Cases in which we want to perform a straight assignment
            if (isAssign(oProp, sProp)) {
                procAssign(oProp, sProp, propName);
            }
                // sProp is a string that contains parentRE
            else if (hasRegex(oProp, sProp)) {
                procRegex(oProp, sProp, propName);
            }
                // At least one property is an array
            else if (hasArray(oProp, sProp)) {
                procArray(oProp, sProp, propName);
            }
                // At least one property is an object
            else if (hasObject(oProp, sProp)) {
                procObject(oProp, sProp, propName);
            }
                // Everything else
            else {
                // Let's be optimistic and perform a straight assignment
                procAssign(oProp, sProp, propName);
            }
        },

        procAll = function (src) {
            source = src;
            Object.keys(source).forEach(procMain);
        };

    _.each(Array.prototype.slice.call(arguments, 1), procAll);

    return obj;
};