/// <reference path="../references.js" />

rwthapp.navigator = (function () {
    var currentPage = {
        target: {
            template: null,
            hash: '',
            parameters: {},
            renderedTemplate: ''
        },
        navTitle: {

            title: '',
            subTitle: ''
        },
        events: {}
    };
    var animate = true;
    var navigationEnabled = true;

    var tabletMainViewLink = null;
    var blockNavigationForHashChangeOnWindows = false;

    var loadHomeScreen = false;
    var gotoPage = function (template, parameters, addToHistory) {
        // TODO should be tested in all browsers
        parameters = parameters || {};
        var hash = '#' + template + '?' + $.param(parameters).replace(/\+/g, "%20"); // $.param replaces spaces with +
        if (addToHistory === false && history.replaceState) {
            // Change page without creating a new entry in browser history
            try {
                history.replaceState({}, "", hash);
                $(window).trigger('hashchange');
            } catch (e) {
                // Workaround for IE Mobile: throws SecurityException for certain hash values (probybly the serialized error objects)
                location.hash = hash;
            }
        } else {
            location.hash = hash;
        }
    };

    var replaceCurrentHashWithoutNavigation = function (hash, notAlwaysReplace) {
        /// <summary>Replaces current location. Does not work on certain browsers, for example on Android 4.0/4.1. No effect (and no page transition) on these browsers.</summary>

        if ((notAlwaysReplace && doesTemplatesEqualsForHash(hash, location.hash)) || !notAlwaysReplace) {

            if (rwthapp.platformInfo.isWindowsUniversal() || rwthapp.platformInfo.isWP8()) {
                
                blockNavigationForHashChangeOnWindows = true;
                location.hash = hash;

                // XXX sometimes, hashchange is not triggered, this works around blocking further user interaction
                setTimeout(function () {
                    blockNavigationForHashChangeOnWindows = false;
                }, 100);
            }
            else {

                if (history.replaceState) {

                    history.replaceState({}, "", hash);
                }
            }

            if (tabletMainViewLink && doesTemplatesEqualsForHash(hash, tabletMainViewLink)) {

                tabletMainViewLink = hash;
            }

            return;
        }

        if (tabletMainViewLink && doesTemplatesEqualsForHash(hash, tabletMainViewLink)) {

            tabletMainViewLink = hash;
        }
    };

    var getEventsForTemplate = function (templateString) {

        // at the moment events might are stored in structure like this rwthapp.search.employees.events, rwthapp.search.employees is not accessible with
        // string notation like rwthapp["search.employees"].events this has to be splitted like this rwthapp["search"]["employees"].events
        var eventObject = rwthapp.templates;
        var properties = templateString.split(".");

        for (var i in properties) {

            if (eventObject[properties[i]]) {

                eventObject = eventObject[properties[i]];
            }
        }

        if (eventObject !== rwthapp.templates) {

            return eventObject;
        }
        else {

            throw new rwthapp.error.Error("Page could not be loaded: " + currentPage.target.template, 2, rwthapp.localization.getLocalizedString('errors.templateEventsNotLoaded'), true);
        }
    };

    var exitCurrentPage = function () {

        if (currentPage.events && currentPage.events.exit && !compareHashWithTemplate(tabletMainViewLink, currentPage.target.template, currentPage.target.parameters)) {

            currentPage.events.exit();
        }
    };

    var changePage = function (hash) {

        rwthapp.templates.menu.initMainMenu();

        if (!navigationEnabled) {

            return;
        }

        // Exit current template
        exitCurrentPage();

        currentPage.target = parseHash(hash);

        rwthapp.util.setFavoriteState(currentPage.target.hash);

        $('.my-slide-nav').navdrawer('close');

        // XXX Ignore navigation actions
        if (currentPage.target.template === 'nav') {
            return;
        }

        return new Promise(function (resolve, reject) {

            currentPage.events = getEventsForTemplate(currentPage.target.template);

            // Check if the current page is still loaded (happens if loading of page is canceled with back button)
            var currentPageDiv = $('#content').filter(function () {

                return decodeURIComponent($(this).data("target")) === hash;
            });

            Promise.resolve().then(function () {

                if (currentPageDiv.length === 0) { // Current page is not loaded

                    // Show loading bar
                    rwthapp.util.showLoadingScreen();

                    return Promise.resolve(currentPage.events.load(currentPage.target.template, currentPage.target.parameters));
                }
                else { // Current page already loaded

                    // Hide loading bar
                    rwthapp.util.hideLoadingScreen();
                    return;
                }
            }).then(function (result) {

                // Only attempt rendering if previous promise chain link returned a value (so only if load event was called)
                if (result !== undefined) {

                    // Only perform rendering if the current page did not change while loading
                    if (hash === location.hash || loadHomeScreen) {

                        $('#content').removeData('target'); // No current page while rendering the page transition

                        setPageTitle(result.title, result.subTitle);

                        currentPage.target.renderedTemplate = result.template;

                        return render(result.template, result.parameters, result.tabletMode);
                    }
                }

            }).then(function (tabletModifiedHash) {

                if (tabletModifiedHash) {

                    hash = tabletModifiedHash;
                }

                // Only call event handler if the current page did not change while loading
                if (hash === location.hash || loadHomeScreen) {

                    // Store current page in div to avoid reload on back navigation
                    $('#content').data('target', encodeURIComponent(hash));

                    // Call .ready event
                    if (currentPage.events && currentPage.events.ready) {

                        currentPage.events.ready();
                    }
                }
            }).catch(function (err) {

                // Error can be ignored if user navigated to another page
                if (hash === location.hash || loadHomeScreen) {
                    // Handle error, load error page
                    rwthapp.error.handleError(err, hash);
                }
            }).then(function () {

                loadHomeScreen = false;
                resolve();
                return;
            });
        });
    };

    var setPageTitle = function (title, subtitle) {

        if (!title) {
            title = "RWTHApp";
        }

        if (subtitle) {

            $("#titleContainer").removeClass("title-noSubtitle");
        }
        else {

            $("#titleContainer").addClass("title-noSubtitle");
            subtitle = '';
        }

        $("#title-top").text(title);
        $("#title-sub").text(subtitle);

        currentPage.navTitle.subTitle = subtitle;
        currentPage.navTitle.title = title;
    };


    var parseHash = function (hash) {
        var templateToLoad = null;
        var parameters = {};

        var target = hash.replace('#', '');

        // Home if hash is empty
        if (target.trim() === '') {
            return {
                template: 'home',
                hash: '#home',
                parameters: {}
            };
        }

        // Parsing
        var splitTarget = splitAtFirstIndexOf(target, '?');

        templateToLoad = splitTarget[0].trim();

        if (splitTarget.length > 1) {

            var splitParameters = splitParametersForMainLink(splitTarget[1]);

            for (var i = 0; i < splitParameters.length; i++) {

                var splitParameter = splitAtFirstIndexOf(splitParameters[i], '=');

                if (splitParameter.length === 2) {

                    parameters[decodeURIComponent(splitParameter[0])] = decodeURIComponent(splitParameter[1]);
                }
            }
        }

        return {
            template: templateToLoad,
            hash: hash,
            parameters: parameters,
        };
    };

    var splitParametersForMainLink = function (mainLink) {

        var mainLinkParameters = mainLink.split('&'), indexToConclude;

        for (var i = 0; i < mainLinkParameters.length; i++) {

            if (indexToConclude) {

                mainLinkParameters[indexToConclude] += '&' + mainLinkParameters[i];
            }

            if (mainLinkParameters[i].indexOf('#') > -1) {

                indexToConclude = i;
            }
        }

        if (indexToConclude) {

            mainLinkParameters.splice(indexToConclude + 1, mainLinkParameters.length - indexToConclude + 1);
        }

        return mainLinkParameters;
    };

    var splitAtFirstIndexOf = function (splitString, splitChar) {

        var splitResult = [];

        if (splitString.indexOf(splitChar) > -1) {

            splitResult.push(splitString.substr(0, splitString.indexOf(splitChar)));
            splitResult.push(splitString.substr(splitString.indexOf(splitChar) + 1, splitString.length));
        }
        else {

            splitResult.push(splitString);
        }

        return splitResult;
    };

    var compareHashWithTemplate = function (hash, template, templateParams) {

        var target = hash ? parseHash(hash) : null;

        if (target) {

            if (target.template === template) {

                if (template === 'campus.search') {

                    if (target.parameters['type'] === templateParams['type']) {

                        return true;
                    }
                    else {

                        return false;
                    }
                }

                return true;
            }
        }

        
        return false;
    };

    var doesTemplatesEqualsForHash = function (hashA, hashB) {

        var targetA = parseHash(hashA);
        var targetB = parseHash(hashB);

        if (targetA.template === targetB.template) {

            if (targetA.parameters["type"] && targetB.parameters["type"]) {

                if (targetA.parameters["type"] === targetB.parameters["type"]) {

                    return true;
                }

                return false;
            }

            return true;
        }

        return false;
    };

    var render = function (templateName, data, tabletMode) {

        var tabletModifiedHash = null, tabletConfig = null;

        // Rendering is performed in promise object
        // new Promise is mandatory here because we need to wait until animaton has complete
        // therefore we need to make sure resolve is executed when complete methods of possible 
        // animations are done.
        return new Promise(function (resolve, reject) {

            if (animate) {

                // Cancel previous animations, if still active
                $('#content_new').stop(true, true);

                var content = $('#content');

                // TODO remove class
                var contentNewHTML = '<div id="content_new"/>';

                // Create hidden div for new content
                $(contentNewHTML).insertAfter('#content');

                var content_new = $('#content_new');
                content_new.hide();

                // Promise for animaton to look up if animation should be simply skipped
                Promise.resolve().then(function() {

                    // rendering for mobile or tablet for template with no tablet support
                    if (!rwthapp.platformInfo.isTablet() || !tabletMode) {

                        // Render template to content_new
                        rwthapp.templateRenderer.render(content_new, templateName, data);

                        if (rwthapp.platformInfo.isTablet() && tabletMainViewLink) {

                            var events = getEventsForTemplate(parseHash(tabletMainViewLink).template);

                            if (events.exit) {

                                events.exit();
                            }

                            tabletMainViewLink = null;
                        }

                        return false;
                    }
                    // rendering for tablet
                    else if (rwthapp.platformInfo.isTablet() && tabletMode) {

                        // this maybe performs an asynchronous server request
                        return performTabletRendering(data, tabletMode, templateName, content_new).then(function (tabletConfigProcessed) {

                            if (!tabletConfigProcessed) {

                                return false;
                            }
                            tabletModifiedHash = location.hash;
                            tabletConfig = tabletConfigProcessed;

                            // skipAnimation when rendering only single view in a tablet constelation
                            return tabletConfig.skipAnimation;
                        });
                    }
                }).then(function (skipAnimation) {

                    // Initialize MaterializeCSS after new content is added, before page transition animation
                    initializeMaterialize();

                    if (!skipAnimation) {

                        // TODO scroll to top, include animation?
                        $(window).scrollTop(0);

                        // Hide loading bar
                        rwthapp.util.hideLoadingScreen();

                        content_new.show();

                        content_new.css('opacity', 0);
                        content_new.css('min-height', "400px");
                        content_new.css('z-index', 100);
                        content.css('z-index', -100);

                        // Add click handler to open external links
                        $('.open-external').off('click.open-external'); // Avoid binding the event twice
                        $('.open-external').on('click.open-external', function () {

                            if ($(this).attr('href')) {
                                window.open($(this).attr('href'), '_system');
                                return false;
                            }
                        });


                        var leftForContentDiv = 0;

                        // TODO dependency to navdrawer...
                        if ($('.my-slide-nav').navdrawer('isAlwaysVisible')) {

                            leftForContentDiv = 300;
                        }

                        if (rwthapp.platformInfo.hasPageTransitionIssue()) {

                            // XXX Workaround for display bug on (some) Windows Phone 8.1 devices
                            content_new.css("left", '100%');
                            content_new.velocity({
                                left: leftForContentDiv, opacity: '1'
                            }, {
                                duration: 500,
                                complete: function () {
                                    content.remove();
                                    content_new.attr('id', 'content');
                                    content_new.find('#content_right_new').attr('id', 'content_right');
                                    content_new.find('#content_left_new').attr('id', 'content_left');
                                    content_new.css('left', '');

                                    // Resolve promise (animation complete, logic can continue)
                                    resolve();
                                },
                                queue: false
                            });
                        }
                        else {

                            // Standard animation
                            content_new.velocity({
                                opacity: '1'
                            }, {
                                duration: 500,
                                complete: function () {
                                    content.remove();
                                    content_new.attr('id', 'content');
                                    content_new.find('#content_right_new').attr('id', 'content_right');
                                    content_new.find('#content_left_new').attr('id', 'content_left');

                                    // Resolve promise (animation complete, logic can continue)
                                    resolve();
                                },
                                queue: false,
                                easing: 'easeInQuint'
                            });
                        }

                        content.velocity({
                            opacity: '0'
                        }, {
                            duration: 500,
                            queue: false,
                            easing: 'easeOutQuint'
                        });    
                    } // no animation for tabletMode
                    else {

                        rwthapp.util.hideLoadingScreen();
                        resolve();
                    }
                });
            }
            else {

                rwthapp.templateRenderer.render($('#content'), templateName, data);

                // TODO if this should be used, initialization needs to be added

                // Hide loading bar
                rwthapp.util.hideLoadingScreen();

                // Resolve promise (animation complete, logic can continue)
                resolve();
            }
        }).then(function () {
            
            // executed when animation is done
            if (tabletConfig && tabletConfig.secondaryPage) {

                if (tabletConfig.secondaryPage.events.ready) {

                    tabletConfig.secondaryPage.events.ready();
                }
            }

            // Fix rendering errors (black background at the bottom of the page) by triggering redraw
            $('body').hide().show(0);

            return tabletModifiedHash;
        });
    };

    var performTabletRendering = function (data, tabletMode, templateName, content_new) {

        if (!data) {

            data = {};
        }

        // make rendering decision here for render process render both or only left or right
        var link;

        // check if tabletmainview needs to be set on currentPage, therefore we need a tabletMainViewLink and possible links
        if (tabletMainViewLink && tabletMode.links && !currentPage.target.parameters.tabletmainview) {

            $.each(tabletMode.links, function (index, value) {

                if (doesTemplatesEqualsForHash(tabletMainViewLink, value)) {

                    link = value;

                    var hash = location.hash + "&tabletmainview=" + encodeURIComponent(tabletMainViewLink);

                    tabletMode.selectedLink = link;

                    $.extend(true, currentPage.target, parseHash(hash));

                    replaceCurrentHashWithoutNavigation(hash, true);

                    return false;
                }
            });
        }

        // tablet view change complete, a new tablet view is needed, old needs to be exited
        if (!link || tabletMode.noMainView) {

            if (tabletMainViewLink && tabletMainViewLink !== currentPage.target.parameters.tabletmainview) {

                var events = getEventsForTemplate(parseHash(tabletMainViewLink).template);

                if (events.exit) {

                    events.exit();
                }

                tabletMainViewLink = null;
            }

            // following a favorite e.g.
            if (currentPage.target.parameters.tabletmainview) {

                tabletMode.selectedLink = decodeURIComponent(currentPage.target.parameters.tabletmainview);
            }
            else if (tabletMode.noMainView) {

                tabletMode.selectedLink = tabletMode.links[0];
            }
            else if(!tabletMode.mainView) {

                rwthapp.templateRenderer.render(content_new, templateName, data);

                return Promise.resolve();
            }
        }

        // when templates tabletMode supports a mainView like searches e.g. then
        // we assume that currentPage represents the template which will not change
        // on interactions to itself. The interaction happens on mainView
        if (tabletMode.mainView) {

            tabletMainViewLink = location.hash;
        }

        return getTabletConfig(tabletMode, templateName, data).then(function (tabletConfig) {

            var skipAnimation = false;

            if (tabletMode.larger) {

                tabletConfig[tabletMode.position === 'left' ? 'right' : 'left'].larger = true;
            }

            if (!tabletConfig.renderSingle) {

                rwthapp.templateRenderer.render(content_new, 'tablet', tabletConfig);
            }
            else {

                rwthapp.templateRenderer.render($('#content_' + tabletConfig.renderSingle), tabletConfig[tabletConfig.renderSingle].template, tabletConfig[tabletConfig.renderSingle].result);
                $('#content_' + tabletConfig.renderSingle).data("tpl" + tabletConfig.renderSingle, tabletConfig[tabletConfig.renderSingle].template);
                rwthapp.util.changeOrientationOnTablet();
                skipAnimation = true;
            }

            tabletConfig.skipAnimation = skipAnimation;

            return tabletConfig;
        }).catch(function (error) {

            rwthapp.logging.logger.debug("error processing tablet rendering");
        });
    };

    var getTabletConfig = function (tabletMode, templateName, data) {

        // tabletMode.position always specifies where data needs to be loaded, currentPage is allready loaded by changePage flow
        var otherPosition = tabletMode.position === 'right' ? 'left' : 'right',
            positionToLoadData = tabletMode.position,
            tabletConfig = {
                isiOS: rwthapp.platformInfo.isIOS(),
                isWindows: rwthapp.platformInfo.isWindowsUniversal() || rwthapp.platformInfo.isWP8(),
                renderSingle: null,
                left: {
                    template: null,
                    result: null,
                    visible: true
                },
                right: {
                    template: null,
                    result: null,
                    visible: true
                },
                large: tabletMode.large ? tabletMode.large : 's8',
                small: tabletMode.small ? tabletMode.small : 's4',
                secondaryPage: {
                    target: null,
                    events: {}
                }
            }

        return Promise.resolve().then(function () {

            tabletConfig[otherPosition].template = templateName;
            tabletConfig[otherPosition].result = data;

            // when not tabletMode.mainView and orientation is portrait
            if (!rwthapp.util.isLandscape()) {

                tabletConfig[positionToLoadData].visible = false;
            }

            // a selection is given when... 
            // tabletmainview parameter was set on currentPage, 
            // no main view was given for current template or 
            // currentPages tabletMode parameter supports links where one matches to current tabletMainViewLink
            if (tabletMode.selectedLink) {

                tabletConfig.secondaryPage.target = parseHash(tabletMode.selectedLink);
                tabletConfig.secondaryPage.events = getEventsForTemplate(tabletConfig.secondaryPage.target.template);

                // when tabletMainViewLink is undefined because we might acutally are following a favorite or 
                // no main view is provided because of loosely coupled relationship between the views
                // we need to load the non link providing template full qualified
                if (!tabletMainViewLink) {

                    // following a favorite or back navigation
                    if (currentPage.target.parameters.tabletmainview) {

                        tabletMainViewLink = decodeURIComponent(currentPage.target.parameters.tabletmainview);
                    }
                    else if (tabletMode.noMainView) {

                        tabletMainViewLink = location.hash;
                    }

                    return Promise.resolve(tabletConfig.secondaryPage.events.load(tabletConfig.secondaryPage.target.template, tabletConfig.secondaryPage.target.parameters)).then(function (loadedData) {

                        tabletConfig[positionToLoadData].template = loadedData.template;
                        tabletConfig[positionToLoadData].result = loadedData.parameters;

                        if (!tabletConfig[otherPosition]) {

                            tabletConfig.renderSingle = true;
                        }

                        return tabletConfig;
                    }).catch(function (error) {

                        var errorObject = rwthapp.error.handleAndDeliverError(error, tabletConfig.secondaryPage.target.template);

                        tabletConfig[positionToLoadData] = {

                            template: errorObject.template,
                            isError: true,
                            result: {

                                message: errorObject.message,
                                targetPage: errorObject.targetPage,
                                error: errorObject.error,
                                targetTemplate: parseHash(errorObject.targetPage).template
                            }
                        };

                        if (!tabletConfig[otherPosition]) {

                            tabletConfig.renderSingle = true;
                        }

                        return tabletConfig;
                    });
                }
                else {

                    tabletConfig.renderSingle = otherPosition;
                }
            }

            // tabletMode.mainView === true && !tabletMode.selectedLink && !(tabletMode.noMainView || !tabletMainViewLink)
            tabletConfig.secondaryPage = null;

            return tabletConfig;

        });
    };

    var refreshPage = function () {

        if (tabletMainViewLink) {

            var events = getEventsForTemplate(parseHash(tabletMainViewLink).template);

            if (events.exit) {

                events.exit();
            }

            tabletMainViewLink = null;
        }

        replaceHashForRefresh();

        rwthapp.options.disableInappcaching();

        return Promise.resolve(changePage(location.hash).then(function () {

            rwthapp.options.enableInappcaching();
        }));
    };

    var replaceHashForRefresh = function () {

        var hash = location.hash;

        var target = parseHash(hash);

        // no parameters, add timestamp at beginning
        if (Object.keys(target.parameters).length === 0) {

            replaceCurrentHashWithoutNavigation(hash + "?_=" + Date.now());
        }
        // parameters but no timestamp add timestamp at the end
        else if (!target.parameters['_']) {

            replaceCurrentHashWithoutNavigation(hash + "&_=" + Date.now());
        }
        // parameters and timestamp, replace timestamp parameter
        else {

            var link = hash.substr(0, hash.indexOf('_=') + 2);
            var sub = hash.substr(hash.indexOf('_=') + 2, hash.length - hash.indexOf('_='));

            sub = sub.indexOf('&') === -1 ? '' : sub.substr(sub.indexOf('&'), sub.length - sub.indexOf('&'));
            link += Date.now() + sub;

            replaceCurrentHashWithoutNavigation(link);
        }
    };

    var onPause = function () {
        if (currentPage.events && currentPage.events.pause) {
            currentPage.events.pause();
        }
    };

    var onResume = function () {
        if (currentPage.events && currentPage.events.resume) {
            currentPage.events.resume();
        }
    };

    var goBack = function () {

        $("#overlayQuiz").hide();
        window.history.back();
    };

    // Initialise
    $(window).on('hashchange', function () {

        if (blockNavigationForHashChangeOnWindows) {
            
            blockNavigationForHashChangeOnWindows = false;
            return;
        }

        var hash = location.hash;

        // page load triggering from deviceReady
        if (hash === '' && rwthapp.favorites.getRWTHHomeFavorite()) {

            loadHomeScreen = true;

            if (rwthapp.favorites.getRWTHHomeFavorite()) {

                hash = rwthapp.favorites.getRWTHHomeFavorite().route;

                // Add parameter to detect this as homescreen to allow closing the app by pressing the backbutton
                hash += hash.indexOf('?') > 0 ? '&' : '?';
                hash += '_homescreen=true'; // Does not need to be encoded, recheck when changing

                replaceCurrentHashWithoutNavigation(hash);
            }
        }

        rwthapp.logging.logger.info("Page change: " + hash);

        changePage(location.hash);
    });

    var disableNavigation = function () {
        navigationEnabled = false;
        $('.my-slide-nav').navdrawer('disable');
        // TODO
        $("#rwthapp-nav-toggle").hide();
        $("#rwthapp-back-button").hide();
    };

    var enableNavigation = function () {
        navigationEnabled = true;
        $('.my-slide-nav').navdrawer('enable');
        $("#rwthapp-nav-toggle").show();
        // TODO
        if (rwthapp.platformInfo.needsBackButton()) {
            $('#rwthapp-back-button').show();
        }
    };

    var initializeMaterialize = function () {
        $('.slider').slider({
            full_width: true
        });
        $('.datepicker').pickadate();
        $('.select:not(.platform-windows .select)').material_select(); // TODO?
        $('select').material_select();
        $('.collapsible').collapsible();
        Materialize.updateTextFields();
    };

    var resetBlockNavigationForHashChangeOnWindows = function () {

        blockNavigationForHashChangeOnWindows = false;
    };

    return {
        changePage: changePage,
        currentPage: currentPage,
        onPause: onPause,
        onResume: onResume,
        goBack: goBack,
        gotoPage: gotoPage,
        replaceCurrentHashWithoutNavigation: replaceCurrentHashWithoutNavigation,
        disableNavigation: disableNavigation,
        enableNavigation: enableNavigation,
        refreshPage: refreshPage,
        setPageTitle: setPageTitle,
        parsehHash: parseHash,
        resetBlockNavigationForHashChangeOnWindows: resetBlockNavigationForHashChangeOnWindows
    };
})();