/*
 * Requires YAHOO user interface library (YUI, say 2.4.1)
 *    yahoo-dom-event-2.4.1.js
 *    dragdrop-min-2.4.1.js
 *    slider-min-2.4.1.js
 * also needs jquery
 */

var Dom = YAHOO.util.Dom;
var Event = YAHOO.util.Event;


var zoomer;

var tickSize = 20;

var mainImageId = "main-image";
var mainImageContainerId = "main-image-zoom";
var imageStripContainerId = "image-strip";

var sliderBackgroundId = "slide_bg";

var sliderThumbBarId = "slide_thumb";

var contraintModes = {
    STANDARD:'STANDARD',
    LOOSE:'LOOSE'
}
var contraintMode = contraintModes.STANDARD;

var labelLogoId = "label-logo";

var regionHighlightId = "region-highlight";
var regionHighlightHeight = "20px";
var regionHighlightWidth = "30px";
var regionHighlightBorder = "1px #f00 solid";
var overlay;

var zoomInId = "zoomIn";
var zoomOutId = "zoomOut";
var zoomInImage = "plus";
var zoomOutImage = "minus";


// The percentage of the image (expressed as a decimal) that must remain on screen in any direction
var onscreenThreshold = {
    left   : 0.35,
    right  : 0.35,
    top    : 0.35,
    bottom : 0.35
};

// the zoomer can move up 200 pixels
var upLimit = 200;

// and down 0 pixels
var downLimit = 0;

// Reference to YUI Drag and Drop object
var mainImageIdDragDrop;

/*
   Create the YUI inverted slider to scale the image
*/
function setupZoomer() {

    Dom.get(sliderBackgroundId).style.display = "block";

    // Create the Slider instance
    zoomer = YAHOO.widget.Slider.getVertSlider(
        sliderBackgroundId, sliderThumbBarId,
        upLimit, downLimit, tickSize);

    // Add a little functionality to the instance
    YAHOO.lang.augmentObject(zoomer, {

        // A custom value range for the zoomer
        minValue : 20,
        maxValue : 100,
        defaultValue : -20,
        container : Dom.get(mainImageContainerId),
        image : Dom.get(mainImageId),
        mainImageDragDrop:null,

        // The default main image size for the slider
        getDefaultValue : function () {
            return this.defaultValue;
        },

        // A method to retrieve the calculated value, per the value range
        getCalculatedValue : function () {

            /* Invert the offset value so "real" values increase as the
            zoomer moves up
          */
            var offset = -1 * this.getValue();

            // Convert the offset to a value in our configured range
            var conversionFactor =
            (this.maxValue - this.minValue) /
            (this.thumb.topConstraint + this.thumb.bottomConstraint);

            return Math.round(offset * conversionFactor) + this.minValue;
        },

        //
        getImageWidth : function () {
            return Math.round(this.originalWidth * ( this.getCalculatedValue() / 100) );
        },

        //
        getImageHeight : function () {
            return Math.round(this.originalHeight * ( this.getCalculatedValue() / 100) );
        }

    });

    /*
      Updates the screen and moves the image as it changes
   */
    zoomer.subscribe('change', function (offsetFromStart) {

        // Remember the old dimensions
        var oldWidth = Math.round(this.image.width);
        var oldHeight = Math.round(this.image.height);

        var mainImageIdX = Dom.getX(this.image);
        var mainImageIdY = Dom.getY(this.image);

        // Resize the image based on the new slider value
        var w = Math.round(this.originalWidth * ( this.getCalculatedValue() / 100));
        this.image.width = w;

        var h = Math.round(this.originalHeight * ( this.getCalculatedValue() / 100));
        this.image.height = h;

        // Dimension changes from old to new.
        var widthChange = w - oldWidth;
        var heightChange = h - oldHeight;

        // How much has the image to the left of the focal point changed?
        var focalCoords = this.getCentrePointCoords(mainImageContainerId);

        var newXVal = mainImageIdX + ( oldWidth - w )/2;

        var newYVal = mainImageIdY + ( oldHeight - h )/2;

        // If offscreen, reposition
        var containerRegion = Dom.getRegion(this.container);
        Dom.setX( this.image, newXVal );
        Dom.setY( this.image, newYVal );

        var imageRegion = Dom.getRegion(this.image);

        // If the image is not wholly inside the container check bounds
        if(containerRegion.contains(imageRegion)){
            // Image is within bounds, reset the contraint model to prevent moving offscreen
            contraintMode = contraintModes.STANDARD;

        } else {

            // Image has broken bounds, reset the contraint model to allow for overlap
            contraintMode = contraintModes.LOOSE;

            // Check container top/bottom
            if(containerRegion.top > imageRegion.bottom || containerRegion.top > ( imageRegion.bottom + onscreenThreshold.top * this.getImageHeight()) ) {
                // Fix top side - BARMY
                newYVal = containerRegion.top - ( ( 1 - onscreenThreshold.top) * this.getImageHeight());
            } else if(containerRegion.bottom < newYVal || containerRegion.bottom < ( newYVal + onscreenThreshold.bottom * this.getImageHeight())) {
                // Fix bottom side
                newYVal = containerRegion.bottom - ( onscreenThreshold.bottom * this.getImageHeight());

            }

            // Check container sides
            if(containerRegion.right < newXVal || containerRegion.right < ( newXVal + onscreenThreshold.right * this.getImageWidth()) ) {
                // Fix right side (offscreen to the right)
                newXVal = containerRegion.right - ( onscreenThreshold.left * this.getImageWidth());
            } else if(containerRegion.left > imageRegion.right || containerRegion.left > (imageRegion.right + onscreenThreshold.left * this.getImageWidth()) ) {
                // Fix left side (offscreen to the left) - BARMY
                newXVal = containerRegion.left - ( ( 1 - onscreenThreshold.left) * this.getImageWidth());
            }
        }

        Dom.setX( this.image, newXVal );
        Dom.setY( this.image, newYVal );

        mainImageIdDragDrop.resetConstraints();
        constrainImage(mainImageIdDragDrop);
        //mainImageIdDragDrop.resetConstraints();

        Dom.get(sliderThumbBarId).title =
        this.getCalculatedValue() + "% of full size";
    });


    /*
      Reset main image to default value and positioning
   */
    zoomer.reset = function(resetValue) {

        // This is just a fudge. Bug exists where a double reset will put the image back to defaults, a single would be right size but out of position
        if(resetValue) {
            zoomer.setValue(this.getDefaultValue());
        }

        window.setTimeout(
            function() {
                Dom.setX(zoomer.image, ( (zoomer.getCentrePointCoords(mainImageContainerId)).x - (zoomer.getImageWidth() / 2)) )
            }
            , 50);

        window.setTimeout(
            function() {
                Dom.setY(zoomer.image, ( 100 + zoomer.getCentrePointCoords(mainImageContainerId).y - (zoomer.getImageHeight() / 2)) )
            }
            , 50);

        contraintMode = contraintModes.STANDARD;

        constrainImage(mainImageIdDragDrop);
    };

    /*
      Get the centre point of a (rectangular) container as an object with properties x,y
   */
    zoomer.getCentrePointCoords = function(container) {

        if(Dom.get(container) == null) {
            return;
        }

        var coords = new Object();
        var width = jQuery( '#main-image-zoom' ).innerWidth();
        var height = jQuery( '#main-image-zoom' ).innerHeight();

        coords.x = width / 2;
        coords.y = height / 2;

        return coords;
    };

    /*
      Externally accessible zoom in function that manually fires a new event change to the slider
   */
    zoomer.zoomIn = function(point_x, point_y) {
        var newVal = this.getValue() - tickSize;
        this.setValue(newVal);
        zoomer.fireEvent("change", newVal);
    };

    /*
      Externally accessible zoom out function that manually fires a new event change to the slider
   */
    zoomer.zoomOut = function(point_x, point_y) {
        var newVal = this.getValue() + tickSize;
        this.setValue(newVal);
        zoomer.fireEvent("change", newVal);
    };

    zoomer.setDragDrop = function(dd) {
        this.mainImageDragDrop = dd;
    };

    zoomer.getDragDrop = function() {
        return this.mainImageDragDrop;
    };

}


/*
 *  Constrain the image movement to within the the defined thresholds of the containing element.
 */
function constrainImage(mainImageIdDragDrop) {

    //Get the top, right, bottom and left positions
    var region = Dom.getRegion(zoomer.container);
    var topBorderWidth = parseInt(Dom.getStyle(zoomer.container, "border-top-width"));
    var rightBorderWidth = parseInt(Dom.getStyle(zoomer.container, "border-right-width"));
    var bottomBorderWidth = parseInt(Dom.getStyle(zoomer.container, "border-bottom-width"));
    var leftBorderWidth = parseInt(Dom.getStyle(zoomer.container, "border-left-width"));

    //Get the xy position of it
    var xy = Dom.getXY(zoomer.image);

    //Get the width and height
    var width = parseInt(Dom.getStyle(zoomer.image, 'width'), 10);
    var height = parseInt(Dom.getStyle(zoomer.image, 'height'), 10);

    // Constraints refelc the number of pixels the image can move before being constrained
    var topConstraint, rightConstraint, bottomConstraint, leftConstraint;

    if(contraintMode == contraintModes.LOOSE) {
        /* Offsets relate to the distance from the region edges ot the image edges.
         A negative distance means the image edge is bounded within the container region
       */
        var topOffset = region.top  - xy[1];
        var bottomOffset = xy[1] + height - region.bottom;
        var leftOffset = region.left - xy[0];
        var rightOffset = xy[0] + width - region.right;

        if(topOffset < 0) {
            topOffset = 0;
        }
        if(rightOffset < 0) {
            rightOffset = 0;
        }
        if(bottomOffset < 0) {
            bottomOffset = 0;
        }
        if(leftOffset < 0) {
            leftOffset = 0;
        }

        if(topOffset > 0 && bottomOffset > 0) {
            topConstraint = bottomOffset;
            bottomConstraint = topOffset;
        } else if(topOffset > 0) {
            topConstraint = 0;
            bottomConstraint = topOffset - bottomOffset;
        } else if(bottomOffset > 0) {
            topConstraint = bottomOffset - topOffset;
            bottomConstraint = 0;
        } else {
            topConstraint = xy[1] - region.top - topBorderWidth;
            bottomConstraint = region.bottom - xy[1] - height - bottomBorderWidth;
        }

        if(leftOffset > 0 && rightOffset > 0) {
            leftConstraint = rightOffset;
            rightConstraint = leftOffset;
        } else if(leftOffset > 0) {
            leftConstraint = 0;
            rightConstraint = leftOffset - rightOffset;
        } else if(rightOffset > 0) {
            leftConstraint = rightOffset - leftOffset;
            rightConstraint = 0;
        }else
        {
            leftConstraint = xy[0] - region.left - leftBorderWidth;
            rightConstraint = region.right - xy[0] - width - rightBorderWidth;
        }
            


    } else if(contraintMode == contraintModes.STANDARD) {
        //Set left to x minus left
        leftConstraint = xy[0] - region.left - leftBorderWidth;

        //Set right to right minus x minus width
        rightConstraint = region.right - xy[0] - width - rightBorderWidth;

        //Set top to y minus top
        topConstraint = xy[1] - region.top - topBorderWidth;

        //Set bottom to bottom minus y minus height
        bottomConstraint = region.bottom - xy[1] - height - bottomBorderWidth;

    } else {
        alert("Unknown contraint mode used ["+contraintMode+"]");
        return;
    }


    //Set the constraints based on the above calculations
    if(zoomer.getDragDrop()) {
        zoomer.getDragDrop().setXConstraint(leftConstraint, rightConstraint);
        zoomer.getDragDrop().setYConstraint(topConstraint, bottomConstraint);
    }

}

/*
   Swap main image with selected thumbnail and reset  thumbnail links
*/
function swapMainImage(currObj, newSrc) {

    var mainImage = document.getElementById(mainImageId);
    var currentClass = "current";

    if(mainImage.src) {

        var newImage = new Image();
        newImage.src = newSrc;

        // Swap over the images
        mainImage.src = newImage.src;

        if(Dom.getFirstChild(currObj).nodeName == "IMG") {
            var imgNode = Dom.getFirstChild(currObj);
            var aNode = currObj;
            var liNode = currObj.parentNode;

            // Check for previously selected img preview to repopulate the onclick event handler
            if(liNode.parentNode && liNode.parentNode.hasChildNodes()) {
                var childNodes = liNode.parentNode.childNodes;

                var oldCurrentNode;

                // For each child (an LI) remove any current class
                for(var i=0; i<childNodes.length; i++) {
                    // Check for element nodes only
                    if(childNodes[i].nodeType == 1) {
                        if(Dom.hasClass(childNodes[i], currentClass)) {

                            oldCurrentNode = childNodes[i];

                            var oldImgNode
                            var oldANode
                            if(Dom.getFirstChild(oldCurrentNode).nodeName == "IMG") {
                                oldImgNode = Dom.getFirstChild(oldCurrentNode);

                                var newLinkNode = document.createElement("A");

                                newLinkNode.href = "javascript:swapMainImage(this, oldImgNode.src.substring(oldImgNode.src.lastIndexOf('/')+1)); return false;";

                                Event.addListener(newLinkNode, "click", function() {
                                    swapMainImage(this, oldImgNode.src.replace('stk_img_thb', 'stk_img_large')); return false;
                                });
                                oldImgNode.parentNode.appendChild(newLinkNode);
                                newLinkNode.appendChild(oldImgNode);
                            }
                        }
                    }
                }

                // Now add it back onto the current li node
                Dom.addClass(liNode, currentClass);

                if(oldCurrentNode) {
                    Dom.removeClass(oldCurrentNode, currentClass);
                }

                /*
               3. Reset Parent node
               Remove link and move img
            */
                currObj.parentNode.replaceChild(imgNode, currObj);

            }
        }
    }
}

/*
   Initialise the massive main image
*/
function initImage(imageElem) {

    if(!imageElem) {
        imageElem = Dom.get(mainImageId);
    }

    // Given the offset, this is our "zero"
    zoomer.originalWidth = imageElem.width;
    zoomer.originalHeight =  imageElem.height;
    zoomer.setValue(zoomer.getDefaultValue());

    // Create drag drop element
    mainImageIdDragDrop = new YAHOO.util.DD(mainImageId);
    zoomer.setDragDrop(mainImageIdDragDrop);
    Dom.setStyle(Dom.get("main-image"), "border", "1px solid #fff");

    // Track movements
    mainImageIdDragDrop.onDrag = function(evt) {
        Dom.setStyle(Dom.get("main-image"), "border", "1px dashed #ccc");

    };

    mainImageIdDragDrop.endDrag = function(evt) {
        mainImageIdDragDrop.resetConstraints();
        constrainImage(this);
        Dom.setStyle(Dom.get("main-image"), "border", "1px solid #fff");
    };

}

/*
   Add the image reset button to reset to defaults
*/
function addResetLink() {

    var resetContainer = Dom.get("reset")
    resetContainer.style.display = "block";

    var aLink = document.createElement('a');
    var linkText = document.createTextNode("Reset");
    aLink.href = "javascript:zoomer.reset(true)";
    aLink.className = "button";
    aLink.appendChild(linkText);

    resetContainer.appendChild(aLink);
}

/*
   Add the zoom in/out link images around the slider and attach the function handlers
*/
function addZoomLinks() {

    var links = new Array(zoomInId, zoomOutId);

    for (var i in links) {
        var container = Dom.get(links[i]);

        if(container != null) {
            container.style.display = "block";

            Dom.setStyle(links[i], 'backgroundImage', 'none');

            var linkNode = document.createElement("A");
            var linkImg = document.createElement("IMG");
            var imgName = links[i] == zoomInId ? zoomInImage : zoomOutImage;
            linkImg.src = "/site_images/yui/" + imgName + ".gif";

            if(links[i] == zoomInId) {
                linkNode.onclick = function() {
                    zoomer.zoomIn()
                };
                linkImg.setAttribute("alt", "zoom in");
                linkImg.setAttribute("title", "zoom in");
            } else {
                linkNode.onclick = function() {
                    zoomer.zoomOut()
                };
                linkImg.setAttribute("alt", "zoom out");
                linkImg.setAttribute("title", "zoom out");
            }

            linkNode.appendChild(linkImg);

            container.appendChild(linkNode);

        }


    }
}


/*
   Initialisation convenience wrapper - call all initialisers
*/
function init() {

    jQuery('#image-strip').css('position', 'static');

    // Override styles for JS enabled version
    Dom.setStyle(mainImageId, 'cursor:hover', 'pointer');
    Dom.setStyle(mainImageContainerId, 'overflow', 'hidden');

    // Create the YUI slider
    setupZoomer();

    // Reset image button
    addResetLink();

    // Zoom in and out buttons
    addZoomLinks();

    // Initialise the main image
    initImage();

    // Reset to defaults (location only)
    zoomer.reset();

    /* Initialization code. */
    var mainImage = Dom.get(mainImageId);
    if(zoomer.container != null && zoomer.image != null) {
        if (zoomer.container.addEventListener) {
            zoomer.container.addEventListener('DOMMouseScroll', wheel, false);
            zoomer.container.onmousewheel = wheel;
        } else if (zoomer.container.attachEvent){
            zoomer.container.attachEvent('onmousewheel', wheel);
        }
    }

    resize();
    zoomer.fireEvent("change", zoomer.getValue());
}

/*
   Enable mouse scroll wheel to zoom in and out
*/
function handle(delta, evt) {
    if (delta < 0) {
        zoomer.zoomOut();
    } else {
        zoomer.zoomIn(Event.getPageX(evt), Event.getPageY(evt));
    }
}

/*
   Callback to do something when the mouse wheel event fires and is scrolled over the target area
*/
function wheel(evt){
    var delta = 0;
    if (!evt) {
        evt = window.event;
    }

    if (evt.wheelDelta) {
        delta = evt.wheelDelta/120;
    /* Perhaps needed for older version of opera?
      if (window.opera) {
         delta = -delta;
      }
      */
    } else if (evt.detail) {
        delta = -evt.detail/3;
    }
    if (delta) {
        handle(delta, evt);
    }

    if (evt.preventDefault) {
        evt.preventDefault();
    }

    evt.returnValue = false;
}

/* Window load */
YAHOO.util.Event.addListener(window, "load", init);

/* Window resize */
YAHOO.util.Event.addListener(window, "resize", resize);



function resize(e) {
    var currentHeight = Dom.getViewportHeight();
    var currentWidth = Dom.getViewportWidth();
    var headerHeight = jQuery('#label-logo').outerHeight(true);
    var footerHeight = jQuery('#image-strip').outerHeight(true);
    var titleHeight = jQuery('h1').outerHeight(true);
    var paddingHeight = jQuery('#main-image-zoom').outerHeight( true ) - jQuery('#main-image-zoom').innerHeight();

    Dom.setStyle(mainImageContainerId, 'height' , ( currentHeight - headerHeight - footerHeight - paddingHeight - titleHeight) + "px");

//    var pageWidth = jQuery('#main-image-zoom').outerWidth();
    var imageStripWidth = jQuery('#image-strip').outerWidth(false);
    jQuery('#image-strip').css('left', (currentWidth - imageStripWidth)/2 + "px" );

    zoomer.reset(false);
}
