var App = App || {};

App.Router = function(_, jQuery, promise, util, appState, presenterManager) {

    var root = '#/';
    var previousLocation = window.location.href;
    var routes = [];

    injectRoutes();

    function injectRoutes() {
        inject('', 'homePresenter');
        inject('#/', 'homePresenter');
        inject('#/404', 'httpErrorPresenter', {error: 404});
        inject('#/home', 'homePresenter');
        inject('#/login', 'loginPresenter');
        inject('#/logout', 'logoutPresenter');
        inject('#/register', 'registrationPresenter');
        inject('#/upload', 'postUploadPresenter');
        inject('#/password-reset(/:token)', 'userActivationPresenter', {operation: 'passwordReset'});
        inject('#/activate(/:token)', 'userActivationPresenter', {operation: 'activation'});
        inject('#/users(/:!query)', 'userListPresenter');
        inject('#/user/:userName(/:tab)', 'userPresenter');
        inject('#/posts(/:!query)', 'postListPresenter');
        inject('#/post/:postNameOrId(/:!query)', 'postPresenter');
        inject('#/comments(/:!query)', 'globalCommentListPresenter');
        inject('#/tags(/:!query)', 'tagListPresenter');
        inject('#/tag/:tagName', 'tagPresenter');
        inject('#/help(/:tab)', 'helpPresenter');
        inject('#/history(/:!query)', 'historyPresenter');
    }

    function navigate(url, useBrowserDispatcher) {
        if (('pushState' in history) && !useBrowserDispatcher) {
            history.pushState('', '', url);
            dispatch();
        } else {
            window.location.href = url;
        }
    }

    function navigateToMainPage() {
        navigate(root);
    }

    function navigateInplace(url, useBrowserDispatcher) {
        if ('replaceState' in history) {
            history.replaceState('', '', url);
            if (!useBrowserDispatcher) {
                dispatch();
            } else {
                location.reload();
            }
        } else {
            navigate(url, useBrowserDispatcher);
        }
    }

    function start() {
        if ('onhashchange' in window) {
            window.onhashchange = dispatch;
        } else {
            window.onpopstate = dispatch;
        }
        dispatch();
    }

    function inject(definition, presenterName, additionalParams) {
        routes.push(new Route(definition, function(params) {
            if (util.isExitConfirmationEnabled()) {
                if (window.location.href === previousLocation) {
                    return;
                } else {
                    if (window.confirm('Are you sure you want to leave this page? Data will be lost.')) {
                        util.disableExitConfirmation();
                    } else {
                        window.location.href = previousLocation;
                        return;
                    }
                }
            }

            params = _.extend({}, params, additionalParams, {previousLocation: previousLocation});

            //abort every operation that can be executed
            promise.abortAll();
            previousLocation = window.location.href;

            var presenter = App.DI.get(presenterName);
            presenter.name = presenterName;
            presenterManager.switchContentPresenter(presenter, params);
        }));
    }

    function dispatch() {
        var url = document.location.hash;
        for (var i = 0; i < routes.length; i ++) {
            var route = routes[i];
            if (route.match(url)) {
                route.callback(route.params);
                return true;
            }
        }
        navigateInplace('#/404', true);
        return false;
    }

    function parseComplexParamValue(value) {
        var result = {};
        var params = (value || '').split(/;/);
        for (var i = 0; i < params.length; i ++) {
            var param = params[i];
            if (!param) {
                continue;
            }
            var kv = param.split(/=/);
            result[kv[0]] = kv[1];
        }
        return result;
    }

    function Route(definition, callback) {
        var possibleRoutes = getPossibleRoutes(definition);

        function getPossibleRoutes(routeDefinition) {
            var parts = [];
            var re = new RegExp('\\(([^}]+?)\\)', 'g');
            while (true) {
                var text = re.exec(routeDefinition);
                if (!text) {
                    break;
                }
                parts.push(text[1]);
            }
            var possibleRoutes = [routeDefinition.split('(')[0]];
            for (var i = 0; i < parts.length; i ++) {
                possibleRoutes.push(possibleRoutes[possibleRoutes.length - 1] + parts[i]);
            }
            return possibleRoutes;
        }

        function match(url) {
            var params = {};
            for (var i = 0; i < possibleRoutes.length; i ++) {
                var possibleRoute = possibleRoutes[i];
                var compare = url;
                var possibleRouteParts = possibleRoute.split('/');
                var compareParts = compare.split('/');
                if (possibleRoute.search(':') > 0) {
                    for (var j = 0; j < possibleRouteParts.length; j ++) {
                        if ((j < compareParts.length) && (possibleRouteParts[j].charAt(0) === ':')) {
                            var key = possibleRouteParts[j].substring(1);
                            var value = compareParts[j];
                            if (key.charAt(0) === '!') {
                                key = key.substring(1);
                                value = parseComplexParamValue(value);
                            }
                            params[key] = value;
                            compareParts[j] = possibleRouteParts[j];
                            compare = compareParts.join('/');
                        }
                    }
                }
                if (possibleRoute === compare) {
                    this.params = params;
                    return true;
                }
            }
            return false;
        }

        this.match = match;
        this.callback = callback;
    }


    return {
        start: start,
        navigate: navigate,
        navigateInplace: navigateInplace,
        navigateToMainPage: navigateToMainPage,
    };
};

App.DI.registerSingleton('router', ['_', 'jQuery', 'promise', 'util', 'appState', 'presenterManager'], App.Router);
