/**
 * PLUGIN CREATED BY BRIAN OSBORNE
 * PLEASE DO NOT USE ON A COMMERICIAL WEBSITE WITHOUT PERMISSION FROM THE OWNER
 *
 * HTTP://WWW.BKOSOLUTIONS.COM
 */
(function($) {

    $.fn.carousel = function (options, orientation) {

        // override the default options with user defined options
        options = $.extend({}, $.fn.carousel.defaults, options || {});

        // Poisitioning must be set to horizontal or vertical
        if (orientation != "horizontal" && orientation != "vertical") {
            return false;
        }
        
        return $(this).each(function () {

            var data = {
                itemsContainer:         $(this),
                totalItems:             $(this).children().length,
                containerWidth:         $(this).width(),
                containerHeight:        $(this).height(),
                items:                  [],
                itemDistances:          [],
                waveDistances:          [],
                itemOpacities:          [],
                animationQueue:         0,
                currentlyMoving:        false,
                itemsAnimating:         0,
                currentSpeed:           options.speed
            }


            // What to do before anything else is done
            beforeLoaded();
            // Preload the images
            preload(function () {
                // once they are preloaded, call the setup method
                setup();
                if (options.useSlider == true)
                    setupSlider();
            });

            // Gets the feature container based on the number
            function getItem(itemNum) {
                return data.itemsContainer.children().eq(itemNum - 1);
            }

            // get previous feature number - accounts for wrap around
            function getPreviousNum(num) {
                var newNum = (num == 1) ? null : num--;
                return newNum;
            }

            // get next feature number - accounts for wrap around
            function getNextNum(num) {
                var newNum = (num == data.totalItems) ? null : num++;
                return newNum;
            }

            function beforeLoaded() {
                data.itemsContainer.find("img").hide();
            }

            function preload(callback) {
                // NOTE THIS DOESNT WORK IF AN IMAGE IS BROKEN!
                var $imageElements = data.itemsContainer.find("img");
                var images = [];
                $imageElements.each(function () {
                    if ($.inArray($(this).attr('src'),images) == -1) {
                        images.push($(this).attr('src'));
                    }
                });
                var loadedImages = 0;
                var totalImages = images.length;
                $imageElements.each(function () {
                    if ($.inArray($(this).attr('src'),images) > -1) {
                        $(this).load(function () {
                            loadedImages++;
                            if (loadedImages == totalImages) {
                                callback();
                            }
                        });
                    }
                });
            }

            function setup() {

                data.items = data.itemsContainer.children('img');
                for (var i = 0; i < data.items.length; i++) {
                    data.items[i] = $(data.items[i]);
                }

                // calculate distance between items
                data.itemDistances[0] = options.startingItemSeparation;
                data.waveDistances[0] = options.startingWaveSeparation;
                data.itemOpacities[0] = 1 * .75;
                for (var i = 1; i < options.flankingItems+1; i++) {
                    data.itemDistances[i] = data.itemDistances[i-1] * options.itemSeparationFactor;
                    data.waveDistances[i] = data.waveDistances[i-1] * options.waveSeparationFactor;
                    data.itemOpacities[i] = data.itemOpacities[i-1] * options.opacityDecreaseFactor;
                }
                
                var $centerItem = data.itemsContainer.children('img').eq(options.startingItem - 1);
                var $previousItems = $centerItem.prevAll();
                var $nextItems = $centerItem.nextAll();

                data.itemsContainer
                    .css('position','relative')
                    .find('img')
                        .each(function (i) {
                            // center images in container
                            var newLeft,newTop;
                            if (orientation == "horizontal") {
                                newLeft = (data.containerWidth / 2) - ($(this).width() / 2);
                                newTop = options.centerOffset;
                            } else {
                                newLeft = options.centerOffset;
                                newTop = (data.containerHeight / 2) - ($(this).height() / 2);
                            }
                            $(this)
                                .css({
                                    left: newLeft,
                                    top: newTop,
                                    position: 'absolute',
                                    'z-index': options.flankingItems+2,
                                    'opacity': 1
                                })
                                .data({
                                    currentPosition:    0,
                                    width:              $(this).width(),
                                    owidth:             $(this).width(),
                                    height:             $(this).height(),
                                    oheight:            $(this).height(),
                                    top:                newTop,
                                    left:               newLeft,
                                    opacity:            1,
                                    index:              i
                                })
                                .show();
                        });

                data.animationQueue = 1;

                var counter;
                counter = 1;
                $previousItems.each(function () {
                    for (i = 0; i < counter; i++) {
                        moveItem($(this),false);
                    }
                    counter = counter + 1;
                });

                counter = 1;
                $nextItems.each(function () {
                    for (i = 0; i < counter; i++) {
                        moveItem($(this),true);
                    }
                    counter = counter + 1;
                });
            }

            function setupSlider() {
                $("#slider").slider({
                    min:        1,
                    max:        data.items.length,
                    step:       1,
                    value:      options.startingItem,
                    animate:    true,
                    stop:      function () {
                        stopAnimations();
                        data.currentlyMoving = false;
                        var oldValue = $('#slider').slider('value');
                        var positionOfItem = data.items[oldValue-1].data().currentPosition;
                        
                        if (positionOfItem < 0) {
                            rotateCarousel(true, Math.abs(positionOfItem));
                        } else if (positionOfItem > 0) {
                            rotateCarousel(false, positionOfItem);
                        }
                        
                    }
                });

            }

            function performCalculations($item, newPosition) {

                // Distance to the center
                var oldPosition = $item.data().currentPosition;
                var newDistanceFromCenter = Math.abs(newPosition);

                /** CALCULATE THE NEW WIDTH AND HEIGHT OF THE ITEM **/
                    var oldWidth = $item.data().width, newWidth = $item.data().owidth;
                    var oldHeight = $item.data().height, newHeight = $item.data().oheight;
                    for (var i = 0; i < newDistanceFromCenter; i++) {
                        newWidth = newWidth * options.itemDecreaseFactor;
                        newHeight = newHeight * options.itemDecreaseFactor;
                    }
                    var widthDifference = Math.abs(oldWidth - newWidth);
                    var heightDifference = Math.abs(oldHeight - newHeight);

                /** CALCULATE THE NEW WAVE SEPARATION OF THE ITEM **/
                    var waveSeparation = 0, centeringNumber
                    // number to center item on horizon (vertical or horizontal)
                    if (orientation == "horizontal")
                        centeringNumber = heightDifference / 2;
                    else
                        centeringNumber = widthDifference / 2;
                    // Item growing
                    if ((newPosition > -1 && (newPosition < oldPosition)) || (newPosition < 1 && (newPosition > oldPosition))) {
                        // center item along the horizon
                        waveSeparation -= centeringNumber;
                        // now add the wave
                        waveSeparation += data.waveDistances[Math.abs(newPosition)];
                    // Item shrinking
                    } else if ((newPosition > -1 && (newPosition > oldPosition)) || (newPosition < 1 && (newPosition < oldPosition))) {
                        // center item along the horizon
                        waveSeparation += centeringNumber;
                        // now subtract the wave
                        waveSeparation -= data.waveDistances[Math.abs(newPosition) - 1];
                }

                /** CALCULATE THE NEW ITEM SEPARATION OF THE ITEM **/
                    var itemSeparation = 0;
                    // if moving towards the center, the separation value will be different
                    // than if it were moving away from the center
                    if (Math.abs(newPosition) < Math.abs(oldPosition)) {
                        itemSeparation = data.itemDistances[Math.abs(newPosition)];
                    // if not moving towards center, just give it normal positioning
                    } else {
                        itemSeparation = data.itemDistances[Math.abs(newPosition)-1];
                    }
                    // Need to account for additional size separation only if the item is
                    // on the right side or moving to the center from the right side
                    if (newPosition > 0 || (newPosition == 0 && oldPosition == 1)) {
                        if (orientation == "horizontal")
                            itemSeparation += widthDifference;
                        else
                            itemSeparation += heightDifference;
                    }
                    // We want to separation to be negative if the image is going towards the left
                    if (newPosition < oldPosition) {
                        itemSeparation = itemSeparation * -1;
                    }

                /** CALCULATE NEW OPACITY OF THE ITEM **/
                    var newOpacity;
                    if (newPosition == 0) {
                        newOpacity = 1;
                    } else {
                        newOpacity = data.itemOpacities[Math.abs(newPosition)-1];
                    }

                // Figure out the new top and left values based on the orientation
                var newTop = $item.data().top;
                var newLeft = $item.data().left;
                if (orientation == "horizontal") {
                    newTop = $item.data().top + waveSeparation;
                    newLeft = $item.data().left + itemSeparation;
                } else {
                    newTop = $item.data().top + itemSeparation;
                    newLeft = $item.data().left + waveSeparation;
                }

                var newDepth = options.flankingItems + 2 - newDistanceFromCenter;
                // Set calculations
                $item.data('width',newWidth);
                $item.data('height',newHeight);
                $item.data('top',newTop);
                $item.data('left',newLeft);
                $item.data('currentPosition',newPosition);
                $item.data('depth',newDepth);
                $item.data('opacity',newOpacity);
            }

            function rotationComplete($item, newPosition, direction) {
                // take care of CUSTOM STUFF
                if (newPosition == 0) {
                    var pageToLoad = $item.attr('id');
                    $("#content").children().hide().end().find("#"+pageToLoad).show();
                }

                // hide items that moved to the outside edge
                if (Math.abs(newPosition) == options.flankingItems + 1) {
                    $item.hide();
                }
                
                // decrement items animating
                data.itemsAnimating--;
                // finish shifting entire carousel?
                if (data.itemsAnimating == 0) {
                    // decrement the animation queue
                    data.animationQueue -= 1;
                    // carousel no longer moving
                    data.currentlyMoving = false;
                    // animate the carousel again?
                    if (data.animationQueue > 0) {
                        rotateCarousel(direction, 0);
                    } else {
                        // reset speed
                        data.currentSpeed = options.speed;

                        for (var i = 0; i < data.items.length; i++) {
                            if ($item.data().currentPosition == 0) {
                                var itemIndex = $item.data().index;
                                //$('#slider').slider('option','value',itemIndex+1);
                            }
                        }
                    }
                }
            }

            function moveItem($item, direction) {

                // Get old and new positions
                var oldPosition = $item.data('currentPosition'), newPosition;
                if (direction == false) {
                    newPosition = oldPosition - 1;
                } else {
                    newPosition = oldPosition + 1;
                }
                
                // Only want to physically move the item if it is within the boundaries
                // or in the first position just outside either boundary
                if (Math.abs(newPosition) <= options.flankingItems + 1) {
                    // increment number of items animating
                    data.itemsAnimating++;
                    // change data for the item
                    performCalculations($item, newPosition);
                    // Change depth of item
                    $item.css('z-index',$item.data().depth);
                    // Animate Item
                    $item
                        //.show()
                        .animate({
                            left: $item.data().left,
                            width: $item.data().width,
                            height: $item.data().height,
                            top: $item.data().top,
                            opacity: $item.data().opacity
                        },data.currentSpeed,"linear", function () {
                            rotationComplete($item, newPosition, direction);
                        });

                // If the item is moving further outside of the boundary, don't move it,
                // just increment it's position
                } else if (Math.abs(newPosition) > options.flankingItems) {
                    $item.data('currentPosition',newPosition);
                }

            }

            function stopAnimations() {
                for (var i = 0; i < data.items.length; i++) {
                    data.items[i].stop();
                }
            }

            function rotateCarousel(direction, rotations) {

                // only rotate if not moving already
                if (data.currentlyMoving == false) {

                    // Don't rotate the carousel past the last boundary on the
                    // left or right side. This creates "walls" that prevent the
                    // user from rotating it endlessly
                    var rotateOkay = true;
                    if (direction == true) {
                        if (data.items[0].data().currentPosition == 0) {
                            rotateOkay = false;
                        }
                    } else {
                        if (data.items[data.items.length-1].data().currentPosition == 0) {
                            rotateOkay = false;
                        }
                    }

                    if (rotateOkay) {
                        // Carousel is now moving
                        data.currentlyMoving = true;
                        data.itemsAnimating = 0;

                        // Add rotations to animaiton queue
                        data.animationQueue += rotations;

                        // figure out speed
                        if (rotations > 1) {
                            data.currentSpeed = options.speed / rotations;
                        }
                        // make sure above minimum
                        data.currentSpeed = (data.currentSpeed < 100) ? 100 : data.currentSpeed;
                        // Iterate thru every item
                        for (var i = 0; i < data.items.length; i++) {
                            var $item = $(data.items[i]);
                            var position = $item.data().currentPosition;
                            // Only move items that are within the boundaries of the carousel
                            // (but also the first flanking hidden item on either side if there is one)
                            if (position >= ((options.flankingItems*-1)-1) && position <= (options.flankingItems)+1) {
                                moveItem($item, direction);
                            // If the item is not in the boundaries, then that means it is a hidden flank image
                            // we don't want to move it, but we want to increment it's position
                            } else {
                                if (direction == true) {
                                    $item.data('currentPosition',position+1);
                                } else {
                                    $item.data('currentPosition',position-1);
                                }
                            }
                        }
                    }
                }
            }


            // EVENT HANDLERS
            $('img').live("click", function () {
                var itemPosition = $(this).data().currentPosition;
                var rotations = Math.abs(itemPosition);
                if (itemPosition < 0) {
                    rotateCarousel(true, rotations);
                } else {
                    rotateCarousel(false, rotations);
                }
            });

            $('#left').click(function () {
                rotateCarousel(false,1);
            });

            $('#right').click(function () {
                rotateCarousel(true,1);
            });

        });

    }

    $.fn.carousel.defaults = {
        startingItem:               4,
        startingItemSeparation:     100,
        itemSeparationFactor:       .5,
        startingWaveSeparation:     0,
        waveSeparationFactor:       .5,
        itemDecreaseFactor:         .9,
        opacityDecreaseFactor:      .5,
        centerOffset:               10,
        flankingItems:              4,
        useSlider:                  false,
        speed:                      300
    };

})(jQuery);