define([

    //some configuration variables are stored here.
    "app", 

    //notFound
    "modules/notFound", 
    "screens/help-center/help-center", 
    //modules

], function (app, NotFound, HelpCenter) {

    // Defining the application router, you can attach sub routers here.
    var Router = Backbone.Router.extend({
        routes: {
            //all default routes are declared on the main.js file            
            //other routes come from db (FrontEnd.GetRoutes proc and FrontEnd.Routes table)
        },
        routesPolicies: {
            "": '*'
        },

        //@override
        //This overrides the routeToRegExp function to allow the router to be case-insensitive.
        //Uses backbone router prototype so it doesn't break in future versions.
        _routeToRegExp: function (route) {
            route = Backbone.Router.prototype._routeToRegExp.call(this, route);
            return new RegExp(route.source, 'i');
        }, 

        before: function (routeCaught, b, c) {

            this.storeParameters(routeCaught, b);

            if (localStorage && localStorage.getItem(app.name + "_localstorage") != app.models.user.get("logged").toString()) app.models.user.fetch();

            var validRoute = false;
            var routeFound = false; 
            for (var route in this.routesPolicies) {
                var policies = "";
                if (route == routeCaught) {
                    routeFound = true; 
                    var policies = this.routesPolicies[route];
                    for (var param = 0; param < b.length; param++) {
                        var value = b[param];
                        policies = policies[value];
                    }
                    var userRoles = app.models.user.get("roles");
                    if (policies != "*") {
                        if (userRoles) {
                            for (var roleInx = 0; roleInx < userRoles.length; roleInx++) {
                                var role = userRoles[roleInx];
                                if (policies == role) {
                                    validRoute = true;
                                    break;
                                }
                            }
                        }
                    } else {
                        validRoute = true; 
                    }
                }
            }
            if (!routeFound) validRoute = true;
            if (!validRoute) {
                var that = this; 
                setTimeout(
                    function (that) {
                        return function () { that.navigate("", true); };
                    }(that), 100);
                return false;
            } else {
                this.previousRoute = this.currentRoute;
                this.currentRoute = Backbone.history.fragment;
                return true; 
            }
        },

        logout: function () {
            app.models.user.logout();
            this.currentView.close();
            this.currentView = null; 
            this.navigate("", true); 
        },

        initialize: function (options) {
            (options.routeIDs) ? this.routeIDs = options.routeIDs : null;
            //this.listenTo(this, "route", this.storeParameters);
            this.listenTo(this, "route", _.debounce(this.instanceView_generic, 200));
        },

        defaultRoute: function () {
            var defaultURL = app.models.user.get("defaultURL");
            if (defaultURL == "") defaultURL = app.routerDefaultURL;
            
            _.defer(this.navigate, defaultURL, true);
        }, 

        storeParameters: function (route, viewParams) {
            try{
                var moduleName = app.router.routes[route]; 
                var paramNames = (app.router.routeIDs[moduleName]) ? app.router.routeIDs[moduleName].Route.match(/(\(\?)?:[^/)(]+/g) : [];
                var paramsDictionary = {};

                for (var p = 0; p < paramNames.length; p++) {
                    if (viewParams[p]) paramsDictionary[paramNames[p].replace(":", "")] = viewParams[p];
                }

                this.currentParameters = paramsDictionary;
            } catch (Error) { }
        }, 

        currentView: null,

        cachedViews: [],

        previousRoute: "", 

        currentRoute: "",

        currentModule: "",

        currentParameters: "",

        routingtimestamp: null, 

        switchView: function (view, viewParams) {
            if (this.currentView && this.currentView.id != ((typeof view === 'string') ? view : view.id)) {

                //checking if the view has a beforeClose method defined or a boolean variable
                //if (this.currentView.beforeClose != null && this.currentView.beforeClose != undefined
                //    && (_.isFunction(this.currentView.beforeClose) || _.isBoolean(this.currentView.beforeClose))) {

                //    var askBeforeClose = (_.isFunction(this.currentView.beforeClose)) ? this.currentView.beforeClose.call(this) : this.currentView.beforeClose;

                //    if (askBeforeClose === false && preventAskBeforeClose !== true) {
                //        this.showAskBeforeCloseModal(
                //            this.currentView.askBeforeCloseOpt,
                //            module,
                //            viewID,
                //            viewParams
                //        );

                //        return;
                //    }
                //}

                if (this.currentView.isCacheable) {
                    var currentViewCacheIndex = this.findViewInCache(this.currentView.id, false);
                    //this.cachedViews[this.currentView.id] = this.currentView;

                    if (currentViewCacheIndex != -1) {
                        this.cachedViews[currentViewCacheIndex] = this.currentView;
                    } else {
                        //if views 
                        if (((this.cachedViews.length + 1) > app.viewsCacheMaxLength) && app.viewsCacheMaxLength != -1) {
                            this.flushViewsCache(); 
                        }

                        this.cachedViews.push(this.currentView);
                    }
                    this.currentView.hide(); 
                } else {
                    //close method should be declared on all views
                    if (this.currentView.close)
                        this.currentView.close();
                }
            } else if (this.currentView && this.currentView.id == ((typeof view === 'string') ? view : view.id)) {
                //if same view that being shown, return.

                if (this.currentView.refresh)
                    this.currentView.refresh(viewParams);

                return;
            }

            // Move the view element into the DOM (replacing the old content)
            //this.el.html(view.el);

            // Render view after it is in the DOM (styles are applied)
            if (typeof view === 'string') {
                //means view already exists and a view-id is being received as parameter
                var indexOfView = this.findViewInCache(view);

                //if view is cached, getting view from cache
                view = this.cachedViews[indexOfView];

                //preRender probably used to set which pickers the view is going to use.
                if (view.preRender)
                    view.preRender(viewParams); 

                //this should be discussed
                //going back into history means refreshing the view? if not, then its faster, if so, then this refresh
                //method is called when the view is changed (and by doing so, when the going back in the browser history).
                if (view.reRender)
                    view.reRender(viewParams);

                view.show();
            } else if (typeof view === 'object') {
                //preRender probably used to set which pickers the view is going to use.
                if (view.preRender)
                    view.preRender(viewParams);

                //means is a view (object)
                view.render($("#content"), viewParams);
                
            }

            this.currentView = view;

            //setting title
            document.title = "IHBox " + ((this.currentView.title) ? " - " + this.currentView.title : "");
            $("#screen_title").text((this.currentView.title) ? this.currentView.title : ""); 

        },
        //showAskBeforeCloseModal: function (viewAskOpt, module, viewID, viewParams) {
        //    var that = this,
        //        askOpt = {
        //            events: {},
        //            type: 'CONTINUE-CANCEL',
        //            title: 'Closing screen',
        //            message: 'Current screen will be closed. Do you want to continue?',
        //        },
        //        askModalOpt = {
        //            id: 'ask-before-close',
        //            allowCancel: true,
        //            focusOk: true,
        //        },
        //        askModal;

        //    _.extend(askOpt, viewAskOpt);

        //    askModalOpt.buttons_type = askOpt.type;
        //    askModalOpt.message = askOpt.message;
        //    askModalOpt.title = askOpt.title;

        //    askModal = new Modal.Views.Main(askModalOpt);

        //    askModal.bind("cancel", function () {
        //        if (askOpt.events.cancel)
        //            askOpt.events.cancel();

        //        that.navigate(that.previousRoute, { silent: true });

        //        that.currentRoute = that.previousRoute;
        //    });
        //    askModal.bind("continue", function () {
        //        if (askOpt.events.continue)
        //            askOpt.events.continue();

        //        askModal.hide();

        //        that.switchView(module, viewID, viewParams, true);
        //    });
        //    askModal.bind("no", function () {
        //        if (askOpt.events.no)
        //            askOpt.events.no();

        //        askModal.hide();

        //        that.switchView(module, viewID, viewParams, true);
        //    });
        //    askModal.bind("yes", function () {
        //        if (askOpt.events.yes)
        //            askOpt.events.yes();

        //        askModal.hide();

        //        that.switchView(module, viewID, viewParams, true);
        //    });

        //    askModal.show();
        //},
        flushViewsCache: function () {
            var cacheToFlush = (app.viewsCacheMaxLength / 2).toFixed(0); 
            for (var x = 0; x < cacheToFlush; x++) {
                this.cachedViews[0].close(); 
                this.cachedViews.shift();
            }
        }, 
        findViewInCache: function (view_id, checkCurrent) {
            //default for checkCurrent is true
            //if returns -2 means current view
            if (checkCurrent != false) if (this.currentView && view_id == this.currentView.id) return -2;

            for (var v = 0; v < this.cachedViews.length; v++) {
                if (this.cachedViews[v].id == view_id) {
                    return v; 
                }
            }
            return -1; 
        },

        //this method should be moved to the menu view.
        setActiveEntry: function(url){
            // Unmark all entries
            $('li').removeClass('active');

            // Mark active entry
            $("li a[href='" + url + "']").parents('li').addClass('active');
        },

        notFound: function () {
            var viewID = "not-found"; 

            if (this.findViewInCache(viewID) != -1) {
                this.switchView(viewID); 
            } else {
                var notFoundView = new NotFound.Views.Main({id: viewID});
                this.switchView(notFoundView);
            }
        },

        helpCenter: function(a, b, c){
            var viewID = "help-center";
            var viewParams = {
                action: a
                , item: b
                , subitem: c
            };

            if (this.findViewInCache(viewID) != -1) {
                this.switchView(viewID, viewParams);
            } else {
                var helpCenterView = new HelpCenter.Views.Main({ id: viewID });
                this.switchView(helpCenterView, viewParams);
            }
        }, 

        signup: function () {
            //alert(app.i18n.translate("This web app is currently in beta, you can't create an account yet.").fetch());
            this.navigate(""); 
        }, 

        instanceView_generic: function () {
            var that = this;
            if (!that.routeIDs[arguments[0]]) return;

            var loaded = false; 
            setTimeout(function () {
                if (!loaded)
                    NProgress.start();
            }, 200);
            //app.views.appController.screenLoading(true);

            var moduleName = arguments[0];
            var viewParams = arguments[1]; 
            that.currentModule = moduleName;

            var paramNames = (app.router.routeIDs[moduleName]) ? app.router.routeIDs[moduleName].Route.match(/(\(\?)?:[^/)(]+/g) : [];
            var paramsDictionary = this.currentParameters; 

            //for (var p = 0; p < paramNames.length; p++) {
            //    if (viewParams[p]) paramsDictionary[paramNames[p].replace(":", "")] = viewParams[p]; 
            //}

            viewParams = this.currentParameters; 
            var routingtimestamp_temp = this.routingtimestamp = new Date().getTime();
            require([((that.routeIDs[moduleName].ModulePath) ? that.routeIDs[moduleName].ModulePath : "modules/") + moduleName], function (module) {

                if (routingtimestamp_temp != that.routingtimestamp)
                    return;

                var viewID = null;

                if (that.routeIDs[moduleName].HasChildren == false) {
                    viewID = (module.generateID)
                        ? module.generateID(paramsDictionary) :
                        that.routeIDs[moduleName].Name;
                } else {
                    for (var i in that.routeIDs[moduleName]) {
                        if (that.currentRoute.indexOf(i) != -1) {
                            var viewID = that.routeIDs[moduleName][i].Name;
                            break;
                        }
                    }
                }


                if (viewID) {

                    var viewInCache = that.findViewInCache(viewID);
                    if (viewInCache != -1) {
                        loaded = true;
                        NProgress.done();
                        //app.views.appController.screenLoading(false);

                        that.switchView(viewID, viewParams);
                        return;
                    }

                    var module_view = new module.Views.Main({
                        id: viewID, viewParams: viewParams, onappend: function () {
                            loaded = true;
                            NProgress.done();
                        }
                    });
                    that.switchView(module_view, viewParams);

                    //loaded = true; 
                    //NProgress.done();
                    //app.views.appController.screenLoading(false);

                }

            });

        }

        , encodeParameter: function(name, value){
            try{
                var ret = value; 
                switch (name) {
                    case "date":
                        if (value.getTime){
                            ret = Core.DateTime.ToString(value, "yyyy-mm-dd HH:MM:ss").replace(/\:/g, "$")
                        } else if (typeof value == "string") ret = value.replace(/\:/g, "$"); 
                        break; 
                }

                return ret; 
            }catch(Error){}
        }
        , resolveURL: function (route, params, appendRoot, isCustomRoute) {
            //returning blank if route does not exist on the routeIDs array.
            var isCustomRoute = (_.isBoolean(isCustomRoute)) ? isCustomRoute : false; 

            if (!this.routeIDs[route] && !isCustomRoute) return "";

            appendRoot = (appendRoot == false) ? false : true;
            route, params;
            var href = "";

            var regExpAllNormalParams = /(\(\?)?\/:\w+/g;
            var regExpAllOptionalParams = /\((.*?)\)/g;

            href = (!isCustomRoute) ? this.routeIDs[route].Route : route;
            for (var p in params) {
                var regExpNormal = new RegExp("(\\(\\?)?(:" + p + ")([\\/\\(].+)?$");
                var regExpOptional = new RegExp("\\((/:" + p + ")\\)");
                var regExpNamedParams = new RegExp("\\((/([^\\/]+):"+p+")\\)");

                var value = this.encodeParameter(p, params[p]); 

                var nameOptParam = href.match(regExpNamedParams); 
                if (nameOptParam != null) {
                    href = href.replace(regExpNamedParams, "/" + ((nameOptParam[2]) ? nameOptParam[2] : "") + escape(value));
                }
                else if (href.match(regExpOptional)) {
                    href = href.replace(regExpOptional, "/" + escape(value));
                }
                else if (href.match(regExpNormal)) {
                    href = href.replace(regExpNormal, escape(value));
                }
            }
            href = href.replace(regExpAllOptionalParams, "");
            href = href.replace(regExpAllNormalParams, "");
            href = (appendRoot) ? app.root + href : href; 
            return href; 
        }

    });

    return Router;

}); 