﻿function Lightbox(challengeId)
{

    var mainBoxSel = "div#mainBox";
    var leftMenuSel = "div#content_menu";
    var showHideRulesButton = "div.challengeSummary div.rulesButton div";
    
    var maxImagesCount = 10;
    
    var expanded = false;
    
    var tooltip =
        "The lightbox serves as a temporary space which you can " +
        "use to hold photos for later convenience. For instance, "+
        "you can use it for picking favourites during voting. " +
        "It is an experimental feature at the moment.";
        
    var pinnned;

    var $handle;
    var $rail;
    var $lightbox;
    var $images;
    var $shadow;
    
    var mainBoxTop;
    var mainBoxLeft;
    var mainBoxHeight;
    var leftMenuHeight;
    
    var mainBoxRadius = 10;
    var mainMenuOffset = 15;
    var lightboxWidth = 119;
    var shadowWidth = 10;
    var handleWidth = 22;

    $(document).ready(function () {
        if (!StorageUtils.storageExists) return;
        cacheSurroundingLayout();
        restoreExpandStatus();
        createHTML();
        restoreImages();
        hookOnScrollAndResize();
        hookOnToggleRules();
    });
    
    function cacheSurroundingLayout()
    {
        var mainBoxOffset = $(mainBoxSel).offset();
        mainBoxTop = mainBoxOffset.top;
        mainBoxLeft = mainBoxOffset.left;
        mainBoxHeight = $(mainBoxSel).height();
        leftMenuHeight = $(leftMenuSel).height();
    }
    
    function createHTML()
    {
    
        // create HTML (but hidden)
        
        var $header;
        var $content;
        var $removeAll;
       
        $rail = $("<div/>").
            addClass("lightboxRail").
            css("position", "fixed").
            css("visibility", "hidden").
            css("overflow", "hidden");

        $handle = $("<div/>").
            addClass("lightboxHandle").
            css("position", "absolute").
            css("overflow", "hidden").
            css("right", "0px").
            css("top", "0px").
            attr("title", tooltip).
            appendTo(document.body).
            click(toggle);
            
        $lightbox = $("<div/>").
            addClass("lightbox").
            css("position", "absolute").
            css("width", lightboxWidth + "px").
            css("left", "0px").
            css("top", "0px").
            droppable({ hoverClass: "lightboxDropHover", drop: dropEntry });
            
        $header = $("<div/>").
            addClass("header").
            attr("title", tooltip).
            click(toggle).
            append("LIGHTBOX");
            
        $content = $("<div/>").
            addClass("content");
            
        $images = $("<div/>").
            addClass("images").
            sortable({ items: "div.frameOccupied" });
            
        $removeAll = $("<div/>").
            addClass("removeAll").
            append($("<div/>").
                append("Remove all").
                click(removeAllEntries));

        for (var i = 0; i < maxImagesCount; i++)
        {
            $("<div/>").
                addClass("frame").
                append($("<div/>").addClass("image")).
                appendTo($images);
        }
        
        $lightbox.
            append($header).
            append($content.append($images).append($removeAll));

        $shadow = $("<div/>").
            addClass("lightboxShadow").
            addClass(isIE6() ? "lightboxShadowIE6Bg" : "lightboxShadowBg").
            css("position", "absolute").
            css("right", "0px").
            css("top", "0px").
            css("bottom", "0px");
            
        // attach everything to the document
           
        $rail.
            append($handle).
            append($lightbox).
            append($shadow).
            appendTo(document.body);

        // position the rail

        updateRailPosition();

        // init toggle state

        if (expanded)
        {
            $handle.hide();
            $lightbox.show();
            $shadow.show();
        }
        else
        {
            $handle.show();
            $lightbox.hide();
            $shadow.hide();
        }

        // This allows us to position the lightbox.

        adjustLightboxPosition(true);

        // And so finally, we can show it.
        
        $rail.css("visibility", "visible");

    }
    
    function isIE6()
    {
        // This should be a some global utility function
        // I guess, but ideally we should not need it.
        return window.navigator.userAgent.indexOf("MSIE 6") != -1;
    }
    
    function toggle()
    {

        expanded = !expanded;
        
        var mainBoxOffset = $(mainBoxSel).offset();
        var mainBoxTop = mainBoxOffset.top;
        var mainBoxLeft = mainBoxOffset.left;

        if (expanded)
        {
            $handle.hide();
            $shadow.show();
            $lightbox.
                css("left", lightboxWidth + "px").
                show().
                animate({left: "0px"}, "fast");
        }
        else
        {
            $handle.show();
            $lightbox.hide();
            $shadow.hide();
        }

        saveState();

    }
    
    function open()
    {
        if (!expanded) toggle();
    }
    
    function createCookieName()
    {
        return "challenge_lightbox_" + challengeId;
    }
    
    function hookOnScrollAndResize()
    {
        $(window).
            bind("scroll", function() { adjustLightboxPosition(false); }).
            bind("resize", function() { cacheSurroundingLayout(); updateRailPosition(); adjustLightboxPosition(true); });
    }
    
    function hookOnToggleRules()
    {
    
        // This is a little hack, or not very clean way. The problem is that we
        // need to know when the rules get expanded collapsed, or more generally,
        // we need to know when the position of the main box with entries (or
        // entry) changes its position, because we need to adjust the position
        // of the lightbox accordingly. At the moment, we the various scripts
        // on the page don’t communicate with each other and don't know about
        // each other, because so far there has been no reason for it. So a better
        // fix would be to implement some unifying "hub" which would drive all
        // scripts and interconnect them. But since we need it only for the
        // lightbox at the moment, and the lightbox is just "hacked in" loosely
        // anyway, we do it in the following way.
        
        $(showHideRulesButton).click(function()
        {
            setTimeout(function() { cacheSurroundingLayout(); adjustLightboxPosition(true); }, 10);
        });
        
    }

    function updateRailPosition()
    {

        var windowHeight = $(window).height();
        var railWidth = $lightbox.width();
        var railHeight = $lightbox.height();

        $rail.      
            css("left", (mainBoxLeft - lightboxWidth) + "px").
            css("top", Math.round(windowHeight / 2 - railHeight / 2) + "px").
            css("width", lightboxWidth + "px").
            css("height", railHeight + "px");

    }
    
    function adjustLightboxPosition(force)
    {
    
        // input layout sizes
        var windowHeight = $(window).height();
        var documentScrollTop = $(document).scrollTop();
        var railHeight = $rail.height();
        
        // max/min (in document coordinates)
        var railMinTop = Math.max(leftMenuHeight + mainMenuOffset, mainBoxTop + mainBoxRadius);
        var railMaxTop = Math.max(railMinTop, mainBoxTop + mainBoxHeight - mainBoxRadius - railHeight);
        
        // max/min extent (in window coordinates)
        var railMinTopInWindow = railMinTop - documentScrollTop;
        var railMaxTopInWindow = railMaxTop - documentScrollTop;

        // what would its position be in the middle of the window (in window coordinates)
        var currentTopInWindow = Math.round(windowHeight / 2 - railHeight / 2);
        
        // pin to min
        if (currentTopInWindow < railMinTopInWindow)
        {
            if (force || pinnned != "top")
            {
                pinnned = "top";
                $rail.
                    css("position", "absolute").
                    css("top", railMinTop + "px");
            }
        }

        // pin to max
        else if (railMaxTopInWindow < currentTopInWindow)
        {
            if (force || pinnned != "bottom")
            {
                pinnned = "bottom";
                $rail.
                    css("position", "absolute").
                    css("top", railMaxTop + "px");
            }
        }

        // pin to the middle of the window
        else
        {
            if (force || pinnned != "middle")
            {
                pinnned = "middle";
                $rail.
                    css("position", "fixed").
                    css("top", currentTopInWindow + "px");
            }
        }

    }
    
    function restoreExpandStatus()
    {
        expanded = (StorageUtils.get(createCookieName()) || "").indexOf("expanded") == 0;
    }
    
    function restoreImages()
    {
        var data = StorageUtils.get(createCookieName());
        if (data)
        {
            var entryIds = data.split(",");
            if (entryIds.length > 0)
            {
                for (var i = 1; i < entryIds.length; i++) {
                    var slashPos = entryIds[i].indexOf("\\");
                    var entryId = parseInt(entryIds[i].substr(0, slashPos), 10);
                    var url = entryIds[i].substring(slashPos + 1);
                    if (entryId != 0) addEntry(entryId, url, false);
                }
            }
        }
    }
    
    function saveState()
    {
        var status = [];
        status.push(expanded ? "expanded" : "collapsed");
        $lightbox.find("div.frameOccupied").each(function() { status.push($(this).data("entryId")+"\\" + $("img",this).attr("src")) });
        StorageUtils.set(createCookieName(), status.join(","));
    }
    
    function getNthFrame(n)
    {
        return $($images.children().get(n));
    }
    
    function getImagesCount()
    {
        return $lightbox.find("div.frameOccupied").length;
    }

    function addEntry(entryId, imageSrc, updateCookie)
    {
        var imagesCount = getImagesCount();
        var imageIndex = findImageIndex(entryId);
        if (imageIndex == -1) imageIndex = imagesCount;
        if (imageIndex >= maxImagesCount || imageIndex < imagesCount) return;
        
        var $frame = getNthFrame(imageIndex);
        
        $("<a/>").
            attr("href", createEntryPageUrl(entryId)).
            attr("border", "0").
            css("display", "block").
            css("width", "40px").
            css("height", "40px").
            append($("<img/>").
                attr("src", imageSrc).
                attr("width", "40").
                attr("height", "40").
                attr("border", "0")).
            appendTo($frame.find("div.image"));
                
        $("<div/>").
            addClass("remove").
            appendTo($frame).
            click(function() { removeEntry(entryId); });
                
        $frame.addClass("frameOccupied");
        
        $frame.data("entryId", entryId);
        
        if (updateCookie)
        {
            saveState();
        }

    }
    
    function dropEntry(event, ui)
    {
        addEntry($(ui.draggable).data("entryId"), $(ui.draggable).data("smallImgSrc"), true);
    }
    
    function removeFromFrames($frames)
    {
    
        // delete the inner HTML stuff
        $frames.find("div.remove").remove();
        $frames.find("div.image").empty();
        
        // remove the "occupied" style
        $frames.removeClass("frameOccupied");
    
    }
    
    function removeEntry(entryId)
    {
    
        var imageIndex = findImageIndex(entryId);
    
        // delete the inner HTML stuff
        var $frame = getNthFrame(imageIndex);
        removeFromFrames($frame);
        
        // shuffle entries
        $frame.remove().appendTo($images);

        // update cookie
        saveState();
        
    }
    
    function removeAllEntries()
    {
    
        // delete the inner HTML stuff
        removeFromFrames($lightbox.find("div.frameOccupied"));
    
        // update cookie
        saveState();

    }
    
    function preloadImage(imageSrc)
    {
        var img = new Image();
        img.src = imageSrc;
    }
    
    function findImageIndex(entryId)
    {
        var imageIndex = -1;
        $lightbox.find("div.frame").each(function(i)
        {
            if ($(this).hasClass("frameOccupied") &&
                parseInt($(this).data("entryId")) == entryId)
            {
                imageIndex = i;
                return;
            }
        });
        return imageIndex;
    }
    
    this.addEntryToLightbox = function(entryId, smallImgSrc, imageElement)
    {

        // is the image alread in the lightbox?
        var imagesCount = getImagesCount();
        var imageIndex = findImageIndex(entryId);
        if (imageIndex == -1) imageIndex = imagesCount;
        if (imageIndex >= maxImagesCount) return;
    
        // preload (if new)
        if (imageIndex == imagesCount)
        {
            preloadImage(smallImgSrc);
        }

        // layout and position of the image in the gallery
        var $img = $(imageElement);
        var imgOffset = $img.offset();
        var imgTop = imgOffset.top + 1;
        var imgLeft = imgOffset.left + 1;
        var imgWidth = parseInt($img.attr("width"));
        var imgHeight = parseInt($img.attr("height"));
        
        // create a fake dummy <div> for the animation
        var $animationElement = $("<div/>").
            css("position", "absolute").
            css("top", imgTop + "px").
            css("left", imgLeft + "px").
            css("width", imgWidth + "px"). 
            css("height", imgHeight + "px").
            css("border", "1px solid White").
            /* css("background-color", "#666"). */
            appendTo(document.body);
            
        // unhide for a moment to get the layout
        if (!expanded)
        {
            $lightbox.css("visibility", "hidden").show();
        }
        
        // layout
        var $image = getNthFrame(imageIndex).find("div.image");
        var imageOffset = $image.offset();
        var imageLeft = imageOffset.left;
        var imageTop = imageOffset.top;
        
        // and hide again
        if (!expanded)
        {
            $lightbox.hide().css("visibility", "visible");
        }

        // make sure that the lightbox is open
        open();
        
        // animation target
        var animationOptions = {
            left: imageLeft + "px",
            top: imageTop + "px",
            width: "40px",
            height: "40px" };

        // animate
        $animationElement.animate(
            animationOptions,
            "normal",
            "linear",
            function()
            {
                $animationElement.remove();
                addEntry(entryId, smallImgSrc, true);
            });

    }

}

