$(document).ready(function() {

    /**
     * TODO: Refactor with intent toward pure functions. Mutation of state can lead to bugs and difficult debugging.
     */

    var toc = navData.toc;
    var flatToc = navData.flatToc.reverse();

    function collectNodes(tocMap) {
        var tocNodes = {};
        tocMap.map(function(node, index) {
            var sectionNode = $('#' + node.section);
            var tocSubsections = {};
            tocItem = {section: sectionNode};
            var subsectionNodes;
            if (node.subsections) {
                subsectionNodes = (collectNodes(node.subsections));
                tocItem.subsections = subsectionNodes;
            }
            tocNodes[node.section] = tocItem;
        });
        return tocNodes;
    }
    var tocItems = collectNodes(toc);

    function collectNodesFlat(tocMap, obj) {
        var collect = obj || {};
        tocMap.map(function(node, index) {
            var sectionNode = $('#' + node.section);
            tocItem = {section: sectionNode};
            if (node.subsections) {
                subsectionNodes = (collectNodesFlat(node.subsections, collect));
            }
            collect[node.section] = sectionNode;
        });
        return collect;
    }
    var tocFlat = collectNodesFlat(toc);

    var prevSectionToken;
    var prevSubsectionToken;
    var activeTokensObj = {};

    function scrollActions(scrollPosition) {
        var activeSection = checkNodePositions(toc, tocFlat, scrollPosition);
        var activeSubSection,
            prevL1Nav,
            currL1Nav,
            prevL2Nav,
            currL2Nav;

        // No active section - return existing activeTokensObj (may be empty)
        if (!activeSection) {
            return activeTokensObj;
        }

        /**
         * This block deals with L1Nav sections
         */

        // If no previous token, set previous to current active and show L1Nav
        if (!prevSectionToken) {
            prevSectionToken = activeSection.token;
            currL1Nav = getNavNode(activeSection.token);
            currL1Nav.show('fast');
        } 
        // If active is not the same as previous, hide previous L1Nav and show current L1Nav; set previous to current
        else if (activeSection.token !== prevSectionToken) {
            prevL1Nav = getNavNode(prevSectionToken);
            currL1Nav = getNavNode(activeSection.token);
            prevL1Nav.hide('fast');
            currL1Nav.show('fast');
            prevSectionToken = activeSection.token;
        }

        /**
         * This block deals with L2Nav subsections
         */

        // If there is a subsections array and it has a non-zero length, set active subsection
        if (activeSection.subsections && activeSection.subsections.length !== 0) {
            activeSubSection = checkNodePositions(activeSection.subsections, tocFlat, scrollPosition);
            if (activeSubSection) {
                if (!prevSubsectionToken) {
                    prevSubsectionToken = activeSubSection.token;
                    currL2Nav = getNavNode(activeSubSection.token);
                    currL2Nav.show('fast');
                } else if (activeSubSection.token !== prevSubsectionToken) {
                    prevL2Nav = getNavNode(prevSubsectionToken);
                    currL2Nav = getNavNode(activeSubSection.token);
                    prevL2Nav.hide('fast');
                    currL2Nav.show('fast');
                    prevSubsectionToken = activeSubSection.token;
                }
            } else {
                prevL2Nav = getNavNode(prevSubsectionToken);
                prevL2Nav.hide('fast');
                prevSubsectionToken = null;
            }
        }
        activeTokensObj.L1 = prevSectionToken;
        activeTokensObj.L2 = prevSubsectionToken;
        return activeTokensObj;
    }

    /**
     * Checks for active elements by scroll position
     */

    var prevElemToken;
    var activeElemToken;

    function checkActiveElement(items, scrollPosition) {
        var offset = 50;
        var offsetScroll = scrollPosition + offset;
        var visibleNode;
        for (var i = 0; i < items.length; i++) {
            var token = items[i];
            var node = getHeadingNode(token);
            if (offsetScroll >= node.offset().top) {
                activeElemToken = token;
            }
        }
        if (!prevElemToken) {
            getNavElemNode(activeElemToken).addClass('selected');
            prevElemToken = activeElemToken;
            return;
        }
        if (activeElemToken !== prevElemToken) {
            getNavElemNode(prevElemToken).removeClass('selected');
            getNavElemNode(activeElemToken).addClass('selected');
            prevElemToken = activeElemToken;
        }
        return activeElemToken;
    }

    function getHeadingNode(token) {
        return $('#' + token);
    }

    function getNavNode(token) {
        return $('#' + token + '-nav');
    }

    function getNavElemNode(token) {
        return $('#sidebar-wrapper > ul a[href="#' + token + '"]');
    }

    function checkNodePositions(nodes, flatNodeMap, scrollPosition) {
        var activeNode;
        for (var i = 0; i < nodes.length; i++) {
            var item = nodes[i];
            var node = flatNodeMap[item.section];
            var nodeTop = node.offset().top - 50;
            if (scrollPosition >= nodeTop) {
                activeNode = {token: item.section, node: node};

                if (item.subsections) {
                    activeNode.subsections = item.subsections;
                }
                break;
            }
        }
        return activeNode;
    }

    function scrollToNav(token) {
        setTimeout(function() {
            var scrollPosition = $(window).scrollTop();
            var activeSectionTokens = scrollActions(scrollPosition);
            var activeElemToken = checkActiveElement(flatToc, scrollPosition);
            var navNode = $('#sidebar-wrapper > ul a[href="#' + token + '"]');
            $('#sidebar-wrapper').scrollTo(navNode, {duration: 'fast', axis: 'y'});
        }, 200);
    }

    $(window).on('hashchange', function(event) {
        var scrollPosition = $(window).scrollTop();
        var activeSectionTokens = scrollActions(scrollPosition);
        var activeElemToken = checkActiveElement(flatToc, scrollPosition);
        var scrollToken = activeSectionTokens.L2 ? activeSectionTokens.L2 : activeSectionTokens.L1;
        scrollToNav(scrollToken);
        var token = location.hash.slice(1);
    });

    var scrollPosition = $(window).scrollTop();
    scrollActions(scrollPosition);
    checkActiveElement(flatToc, scrollPosition);
    // TODO: prevent scroll on sidebar from propagating to window
    $(window).on('scroll', function(event) {
        var scrollPosition = $(window).scrollTop();
        var activeSectionTokens = scrollActions(scrollPosition);
        var activeElemToken = checkActiveElement(flatToc, scrollPosition);
    });
});