define([
    "./togglebutton.builder",
    "./toggleicon.builder",
    "jquery",
	"css!./sitemap.css"
],
function(
    ToggleButtonBuilder,
    ToggleIconbuilder,
    jq
){

    "use strict";

    /**
     * @constructor
     */

    return function( options, context, metadata ){

        var sitemapButton,
            button,
            sitemapButtonHotSpotElement,
            sitemapContainer,
            currentItemIndex = -1;

        function hexToRgb(hex) {
            var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
            return result ? {
                r: parseInt(result[1], 16),
                g: parseInt(result[2], 16),
                b: parseInt(result[3], 16)
            } : {
                r: 0,
                g: 0,
                b: 0
            };
        }

        function asRgbaValue( colorValue, transparencyValue ) {

            var color = hexToRgb( colorValue ),
                alpha = asAlpha( transparencyValue );

            return "rgba(" + color.r + ", " + color.g + ", " + color.b + ", " + alpha + ")";
        }

        function asAlpha( percentValue ) {
            return (100 - percentValue) / 100;
        }

        function createSitemap() {

            var imageOptions = options.image.button;

            sitemapButton = jq(document.createElement("div"))
                .attr({
                    "id": "sitemapButton" + options.instanceID,
                    "title" : options.caption
                })
                .css({
                    "width": (imageOptions.width / 2) + "px",
                    "height": (imageOptions.height / 3) + "px"
                })
                .addClass("sitemapButton");

            sitemapButtonHotSpotElement = jq(document.createElement("div"))
                .attr({
                    "id": "sitemapButtonHotSpotElement" + options.instanceID,
                    "title" : options.caption
                })
                .css({
                    "width": (imageOptions.width / 2) + "px",
                    "height": (imageOptions.height / 3) + "px"
                })
                .addClass("sitemapButtonHotSpotElement");

            button = new ToggleButtonBuilder({
                fileNameURL : imageOptions.fileNameURL,
                width : imageOptions.width / 2,
                height : imageOptions.height / 3,
                name : "sitemapButton",
                captionText : options.caption}, context, metadata).build();

            sitemapContainer = createSitemapContainer();

            sitemapButton.append(button.getElement());
            sitemapButton.append(sitemapButtonHotSpotElement);
            sitemapButton.append(sitemapContainer.getElement());
        }

        function createSitemapContainer() {

            var items = new Array(),
                isContainerOpen = false,
                position = getMenuPosition(),
                fillColor = options.fillColor,
                fillTransparency = options.fillTransparency,
                borderColor = options.borderColor,
                borderTransparency = options.borderTransparency,
                container = jq(document.createElement("div"))
                    .attr({
                        "id": "sitemapContainer" + options.instanceID
                    })
                    .css({
                        "width": options.sitemapWidth + "px",
                        "height": options.sitemapHeight + "px",
                        "border": options.borderWidth + "px solid " + asRgbaValue(borderColor, borderTransparency),
                        "background-color": asRgbaValue( fillColor, fillTransparency ),
                        "touch-action": "pan-y"
                    })
                    .css(position)
                    .addClass("sitemapContainer")
                    .on(options.pointerEvents.pointerDown, function(e){
                        cancelEvent(e);
                        lockNavigation();
                    })
                    .on(options.pointerEvents.pointerUp, function(e){
                        cancelEvent(e);
                        unlockNavigation();
                    })
                    .on( "dblclick", cancelEvent )
                    .on( "touchmove", function ( e ) {
                        e.stopPropagation();
                    } );

                context.page.interaction.provokesFeedback( container, false );
                context.page.interaction.provokesAutosolve( container, false );

            if(options.AuthoringMode && options.showSitemapInAuthoringMode){
                container
                    .css({
                        "opacity" : "0.15",
                        "background-image" : "url(\"" + context.getPath() + "invis.png\")",
                        "text-align" : "center",
                        "line-height" : options.sitemapHeight + "px",
                        "font-size" : "24px"
                    })
                    .text( "Sitemap" )
                    .show();
            }else{
                container.hide();
            }

            return {
                getElement : function(){
                    return container.get(0);
                },
                addItem : function(item){

                    items.push(item);
                    item.setIndex(items.length - 1);

                    // set parentIndex if available
                    for(var i = items.length - 1; i >= 0; i--){

                        //skip item
                        if(i == items.length - 1){
                            continue;
                        }

                        //skip pages
                        if(items[i].isPage()){
                            continue;
                        }

                        //section found and item is page
                        if(item.isPage()){
                            if(items[i].getParents().length == 0){
                                item.pushParents([i]);
                            }else{
                                item.pushParents(items[i].getParents());
                                item.pushParents([i]);
                            }
                            break;
                        }

                        //section found and item is section
                        if(items[i].getLevel() < item.getLevel()){
                            if(items[i].getParents().length == 0){
                                item.pushParents([i]);
                            }else{
                                item.pushParents(items[i].getParents());
                                item.pushParents([i]);
                            }
                            break;
                        }

                        //no parent possible anymore
                        if(items[i].getParents().length == 0){
                            break;
                        }
                    }

                    container.append(item.getElement());
                },
                getItems : function(){
                    return items;
                },
                hideContainer : function(){
                    isContainerOpen = false;
                    container.hide();
                },
                showContainer : function(){
                    isContainerOpen = true;
                    container.show();
                },
                isOpen : function(){
                    return isContainerOpen;
                },
                resetSitemapContainer : function(){
                    for(var i = 0; i < items.length; i++){
                        items[i].resetItem();
                    }
                    items.length = 0;
                    isContainerOpen = false;
                    container.empty();
                }
            }
        }

        function getMenuPosition(){

            var buttonWidth = options.image.button.width / 2,
                buttonHeight = options.image.button.height / 3,
                sitemapWidth = options.sitemapWidth,
                sitemapHeight = options.sitemapHeight,
                border = options.borderWidth,
                menuPositions = [
                    //bottom-left
                    {
                        "top": buttonHeight + "px",
                        "right": sitemapWidth + (border * 2) + "px"
                    },
                    //top-left
                    {
                        "bottom": sitemapHeight + (border * 2) + "px",
                        "right": sitemapWidth + (border * 2) + "px"
                    },
                    //top-centered
                    {
                        "bottom": sitemapHeight + (border * 2) + "px",
                        "right": (sitemapWidth + (border * 2) - buttonWidth) / 2 + "px"
                    },
                    //top-right
                    {
                        "bottom": sitemapHeight + (border * 2) + "px",
                        "left": buttonWidth + "px"
                    },
                    //right-centered
                    {
                        "bottom": (sitemapHeight + (border * 2) - buttonWidth) / 2 + "px",
                        "left": buttonWidth + "px"
                    },
                    //bottom-right
                    {
                        "top": buttonHeight + "px",
                        "left": buttonWidth + "px"
                    },
                    //bottom-centered
                    {
                        "top": buttonHeight + "px",
                        "right": (sitemapWidth + (border * 2) - buttonWidth) / 2 + "px"
                    },
                    //left-centered
                    {
                        "bottom": (sitemapHeight + (border * 2) - buttonWidth) / 2 + "px",
                        "right": sitemapWidth + (border * 2) + "px"
                    }
                ],
                posOption = options.position;

            if(posOption != 8){
                return menuPositions[ posOption ];
            }else{
                return {
                    "top": options.sitemapPositionY + "px",
                    "left": options.sitemapPositionX + "px"
                };
            }
        }

        function createSitemapContent(container) {
            try {
                var pathElements = metadata.stage.getMainPath().getPathElements();

                jq.each(pathElements, function(index, element){
                    var pathElement = element,
                        type = pathElement.getType(),
                        title = pathElement.getTitle(),
                        level = pathElement.getLevel(),
                        id,
                        action;

                        if(pathElement.getPageMetadata()){
                            id = pathElement.getPageMetadata().getId();
                        }else{
                            id = 1;
                        }

                        action = function(e){
                            if(context.getPlayerName() == "html5"){
                                cancelEvent(e);
                                unlockNavigation();
                                if(!isOnTarget(e)) {
                                    return;
                                }
                                context.stage.navigation.gotoPageById(id);
                            }else{
                                cancelEvent(e);
                                unlockNavigation();
                                if(!isOnTarget(e)) {
                                    return;
                                }
                                context.stage.navigation.gotoPage(pathElement.page);
                            }
                        };

                    // workaround for wysiwyg and bug with get title of current step
                    if(title == ""){
                        title = "Page";
                    }
                    container.addItem( createSitemapItem(type, title, id, action, level) );
                });
            } catch (error) {
                // to prevent script errors in html4 wysiwyg editor
            }
        }

        function createSitemapItem(type, caption, id, action, level) {

            var item,
                icon,
                index,
                marker,
                iconHotSpotElement,
                textColor,
                hoverColor,
                parentIndex = new Array(),
                elementId = id,
                separatorColor = options.separatorColor,
                separatorTransparency = options.separatorTransparency,
                sectionExpanded = false,
                isSection = false,
                isPage = false,
                itemPaddingLeft = 10 + parseInt(options.image.icons.width) + parseInt(options.paddingLeft) + ((level - 1) * (5 + parseInt(options.image.icons.width) + parseInt(options.paddingLeft))),
                itemPaddingRight = parseInt(options.paddingRight) + 10 + parseInt(options.image.marker.width) + 20,
                itemWidth = parseInt(options.sitemapWidth) - itemPaddingLeft - itemPaddingRight,
                iconHotSpotLeft = 5 + ((level-1) * (5 + parseInt(options.image.icons.width) + parseInt(options.paddingLeft))),
                iconLeft = 5 + ((level-1) * (5 + parseInt(options.image.icons.width) + parseInt(options.paddingLeft))),
                maxHeight;

            if(type == "page"){
                textColor = options.pageTextColor;
                hoverColor = options.pageHoverTextColor;
                isPage = true;
            }else{
                textColor = options.sectionTextColor;
                hoverColor = options.sectionHoverTextColor;
                isSection = true;
            }

            item = jq(document.createElement("div"))
                .attr( "id", id + "-item" + options.instanceID )
                .css({
                    "border-bottom": options.separatorWidth + "px solid " + asRgbaValue(separatorColor, separatorTransparency),
                    "color": textColor,
                    "font-family": options.fontFamily,
                    "padding-left": itemPaddingLeft + "px",
                    "padding-right": itemPaddingRight  + "px",
                    "padding-top": options.paddingTop + "px",
                    "padding-bottom": options.paddingBottom + "px",
                    "font-size": options.fontSize + "px",
                    "width" : itemWidth + "px",
                })
                .addClass("item sc_sitemap_text")
                .text(caption)
                .on(options.pointerEvents.pointerDown, function(e){
                    cancelEvent(e);
                    lockNavigation();
                })
                .on(options.pointerEvents.pointerUp, action)
                .on(options.pointerEvents.pointerMove, function(){
                    jq(this).css("color", hoverColor);
                })
                .on(options.pointerEvents.pointerLeave, function(){
                    jq(this).css("color", textColor);
                })
                .on("dblclick", cancelEvent );

                if(options.cutTitles){
                    item
                        .css({
                            "white-space" : "nowrap",
                            "overflow" : "hidden",
                            "text-overflow" : "ellipsis"
                        })
                }

            if (type == "section"){
                item.addClass("section");
            }

            maxHeight = getRenderedHeight(item) - options.paddingTop - options.paddingBottom;

            if(maxHeight < options.image.marker.height){
                maxHeight = options.image.marker.height;
            }
            if(maxHeight < options.image.icons.height / 2){
                maxHeight = options.image.icons.height / 2;
            }

            item.css({
                "height" : maxHeight + "px"
            })

            marker = jq(document.createElement("img"))
                .attr({
                    "id" : id + "-marker" + options.instanceID,
                    "src" : options.image.marker.fileNameURL
                })
                .css({
                    "width": options.image.marker.width + "px",
                    "height": options.image.marker.height + "px"
                })
                .addClass("marker")
                .hide();

            icon = new ToggleIconbuilder({
                fileNameURL : options.image.icons.fileNameURL,
                width : options.image.icons.width,
                height : options.image.icons.height / 2,
                name : "icon"}, context, metadata).build();

            icon.setLeftPosition(iconLeft);

            iconHotSpotElement = jq(document.createElement("div"))
                .attr({
                    "id": "iconHotSpotElement" + options.instanceID
                })
                .css({
                    "width": options.image.icons.width + "px",
                    "height": (options.image.icons.height / 2) + "px",
                    "left": iconHotSpotLeft + "px"
                })
                .on("mouseenter", function(){
                    item.css("color", hoverColor);
                })
                .on("mouseleave", function() {
                    item.css("color", textColor);
                })
                .on(options.pointerEvents.pointerUp, function(e){
                    cancelEvent(e);
                    unlockNavigation();
                    if(!isOnTarget(e)) {
                        return;
                    }
                    if(sectionExpanded){
                        sectionExpanded = false;
                        icon.toggleIcon();
                        refreshSitemap();
                    }else{
                        sectionExpanded = true;
                        icon.toggleIcon();
                        refreshSitemap();
                    }
                })
                .on(options.pointerEvents.pointerDown, function(e){
                    cancelEvent(e);
                    lockNavigation();
                })
                .on("dblclick", cancelEvent )
                .addClass("iconHotSpotElement")

            item.append(marker);

            if(type == "section"){
                item.append(icon.getElement());
                item.append(iconHotSpotElement);
            }

            context.page.interaction.provokesFeedback( iconHotSpotElement, false );
            context.page.interaction.provokesAutosolve( iconHotSpotElement, false );
            context.page.interaction.provokesFeedback( item, false );
            context.page.interaction.provokesAutosolve( item, false );

            return {
                getElement : function(){
                    return item;
                },
                getType : function(){
                    return type;
                },
                getId : function(){
                    return elementId;
                },
                getIndex : function(){
                    return index;
                },
                getLevel : function(){
                    return level;
                },
                getParents : function(){
                    return parentIndex;
                },
                isSection : function(){
                    return isSection;
                },
                isPage : function(){
                    return isPage;
                },
                isSectionExpanded : function(){
                    return sectionExpanded;
                },
                setIndex : function(i){
                    index = i;
                },
                pushParents : function(element){
                    parentIndex.push.apply(parentIndex, element);
                },
                showItem : function(){
                    item.show();
                },
                hideItem : function(){
                    item.hide();
                },
                setExpanded : function(){
                    if(!sectionExpanded){
                        icon.toggleIcon();
                        sectionExpanded = true;
                    }
                },
                setCollapsed : function(){
                    if(sectionExpanded){
                        icon.toggleIcon();
                        sectionExpanded = false;
                    }
                },
                setMarked : function(){
                    marker.show();
                },
                unsetMarked : function(){
                    marker.hide();
                },
                resetItem : function(){
                    type = null;
                    elementId = null;
                    index = null;
                    level = null;
                    parentIndex.length = 0;
                    sectionExpanded = false;
                    isSection = false;
                    isPage = false;
                }
            }
        }

        function getRenderedHeight(elem){
            var renderedHeight,
                clonedElem = elem.clone();

            clonedElem.appendTo('body');
            renderedHeight = clonedElem.innerHeight();
            clonedElem.remove();

            return renderedHeight;
        }

        function setMarker(){
            var items = sitemapContainer.getItems(),
                thisPageId = metadata.page.getId(),
                parents;

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

                if (items[i].getId() == thisPageId){
                    //skip section for first item in that section
                    if(items[i].isSection()){
                        continue;
                    }
                    currentItemIndex = i;

                    //pages included
                    if(options.includeSteps){
                        items[i].setMarked();
                        break;
                    }else{
                        //pages not included
                        parents = items[i].getParents();
                        if(parents.length != 0){
                            items[parents[parents.length - 1]].setMarked();
                            break;
                        }else{
                            //step without section
                            items[i].setMarked();
                            break;
                        }
                    }
                }
            }
        }

        function refreshSitemap(){
            var items = sitemapContainer.getItems(),
                parents,
                parentCollapsed;

            for(var i = items.length - 1; i >= 0; i--){

                // hide pages if not included, show only pages with no parents
                if(items[i].isPage() && !options.includeSteps && items[i].getParents().length != 0){
                    items[i].hideItem();
                    continue;
                }

                // no parent
                if(items[i].getParents().length == 0){
                    items[i].showItem();
                    continue;
                }

                parents = items[i].getParents();
                parentCollapsed = false;

                for(var j = parents.length - 1; j >= 0; j--){

                    if(!(items[parents[j]].isSectionExpanded())){
                        parentCollapsed = true;
                        break;
                    }
                }

                if(parentCollapsed){
                    items[i].hideItem();
                }else{
                    items[i].showItem();
                }
            }
        }

        function setVisibilityOnEnterPage(){
            var items = sitemapContainer.getItems();

            if(currentItemIndex > -1){
                var parents = items[currentItemIndex].getParents();
                for(var i = 0; i < parents.length; i++){
                    items[parents[i]].setExpanded();
                }
            }
        }

        function isOnTarget(e) {
            var elem = e.target,
                origEvent = e.originalEvent,
                changedTouch = origEvent.changedTouches && origEvent.changedTouches.length > 0 ? origEvent.changedTouches[0] : undefined;

            if (changedTouch) {
                elem = document.elementFromPoint(changedTouch.clientX, changedTouch.clientY);
            }

            return e.target === elem;
        }

        function lockNavigation() {
            if (context.stage.isInAuthoringMode() || (typeof(context.stage.trigger) === "undefined")) {
                return;
            }
            context.stage.trigger.lock( metadata.getId() );
        }

        function unlockNavigation() {
            if (context.stage.isInAuthoringMode() || (typeof(context.stage.trigger) === "undefined")) {
                return;
            }
            context.stage.trigger.unlock( metadata.getId() );
        }

        function cancelEvent( e ){
            e.preventDefault();
            e.stopPropagation();
        }

        return {
            build : function(){
                createSitemap();
                return {
                    getElement : function(){
                        return sitemapButton.get(0);
                    },
                    getHotSpotElement : function(){
                        return sitemapButtonHotSpotElement.get(0);
                    },
                    onMouseLeave : function(){
                        button.onMouseLeave();
                    },
                    onMouseOver : function(){
                        button.onMouseOver();
                    },
                    onMouseDown : function(e){
                        cancelEvent(e);
                        lockNavigation();
                        button.onMouseDown();
                    },
                    onMouseUp : function(e){
                        cancelEvent(e);
                        unlockNavigation();
                        button.onMouseLeave();

                        if(!isOnTarget(e)) {
                            return;
                        }

                        if(sitemapContainer.isOpen()){
                            sitemapContainer.hideContainer();
                        }else{
                            sitemapContainer.showContainer();
                        }

                        button.onMouseUp();
                    },
                    onEnterPage : function() {
                        createSitemapContent(sitemapContainer);
                        setMarker();
                        setVisibilityOnEnterPage();
                        refreshSitemap();
                    },
                    onExitPage : function() {
                        sitemapContainer.hideContainer();
                        button.onExitPage();
                        sitemapContainer.resetSitemapContainer();
                    }
                };
            }
        };
    }
});
