/*!
 * Waterwheel Carousel
 * Version 1.0
 * http://www.bkosolutions.com
 *
 * Copyright 2010 Brian Osborne
 * Licensed under GPL version 3
 * http://www.gnu.org/licenses/gpl.txt
 *
 * Plugin written by Brian Osborne
 * for use with the jQuery JavaScript Framework
 * http://www.jquery.com
 *
 */
(function($) {

    $.fn.waterwheelCarousel = function (orientation, options) {
        options = $.extend({}, $.fn.waterwheelCarousel.defaults, options || {});
        if (orientation != "horizontal" || orientation != "veritical") {
            orientation = "horizontal";
        }
        return $(this).each(function () {
            var data = {
                itemsContainer:         $(this),
                totalItems:             $(this).children().length,
                containerWidth:         $(this).width(),
                containerHeight:        $(this).height(),
                items:                  [],
                itemDistances:          [],
                waveDistances:          [],
                itemOpacities:          [],
                containerIDTag:         "#"+$(this).attr("id"),
                carouselRotationsLeft:  0,
                currentlyMoving:        false,
                itemsAnimating:         0,
                currentSpeed:           options.speed
            }
            beforeLoaded();
            preload(function () {
                setupDistanceArrays();
                setupCarousel();
                setupStarterRotation();
            });
            function getItem(itemNum) {
                return data.itemsContainer.children().eq(itemNum - 1);
            }
            function getPreviousNum(num) {
                var newNum = (num == 1) ? null : num--;
                return newNum;
            }
            function getNextNum(num) {
                var newNum = (num == data.totalItems) ? null : num++;
                return newNum;
            }
            function beforeLoaded() {
                data.itemsContainer.find("img").hide();
            }
            function preload(callback) {
                var $imageElements = data.itemsContainer.find("img");
                var loadedImages = 0;
                var totalImages = $imageElements.length;
                $imageElements.each(function () {
                    $(this).load(function () {
                        loadedImages++;
                        if (loadedImages == totalImages) {
                            callback();
                        }
                    });
                    if (this.complete) {
                        $(this).trigger('load');
                    }
                });
            }

            function setupDistanceArrays() {
                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;
                }
                data.itemOpacities[data.itemOpacities.length-1] = 0;
            }

            function setupCarousel() {
                data.items = data.itemsContainer.children('img');
                for (var i = 0; i < data.items.length; i++) {
                    data.items[i] = $(data.items[i]);
                }
                data.itemsContainer
                    .css('position','relative')
                    .find('img')
                        .each(function (i) {
                            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();
                        });
            }
						
            function setupStarterRotation() {
                var $centerItem = data.itemsContainer.children('img').eq(options.startingItem - 1);
                var $previousItems = $centerItem.prevAll();
                var $nextItems = $centerItem.nextAll();
                data.carouselRotationsLeft = 1;
                var counter;
                counter = 1;
                $previousItems.each(function () {
                    for (i = 0; i < counter; i++) {
                        moveItem($(this),false);
                    }
                    counter++;
                });
                counter = 1;
                $nextItems.each(function () {
                    for (i = 0; i < counter; i++) {
                        moveItem($(this),true);
                    }
                    counter++;
                });
            }

            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) {
                var oldPosition = $item.data().currentPosition;
                var newDistanceFromCenter = Math.abs(newPosition);
                    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);
                    var waveSeparation = 0, centeringNumber
                    if (orientation == "horizontal")
                        centeringNumber = heightDifference / 2;
                    else
                        centeringNumber = widthDifference / 2;
                    if ((newPosition > -1 && (newPosition < oldPosition)) || (newPosition < 1 && (newPosition > oldPosition))) {
                        waveSeparation -= centeringNumber;
                        waveSeparation += data.waveDistances[Math.abs(newPosition)];
                    } else if ((newPosition > -1 && (newPosition > oldPosition)) || (newPosition < 1 && (newPosition < oldPosition))) {
                        waveSeparation += centeringNumber;
                        waveSeparation -= data.waveDistances[Math.abs(newPosition) - 1];
                }

                    var itemSeparation = 0;
                    if (Math.abs(newPosition) < Math.abs(oldPosition)) {
                        itemSeparation = data.itemDistances[Math.abs(newPosition)];
                    } else {
                        itemSeparation = data.itemDistances[Math.abs(newPosition)-1];
                    }
                    if (newPosition > 0 || (newPosition == 0 && oldPosition == 1)) {
                        if (orientation == "horizontal")
                            itemSeparation += widthDifference;
                        else
                            itemSeparation += heightDifference;
                    }
                    if (newPosition < oldPosition) {
                        itemSeparation = itemSeparation * -1;
                    }

                    var newOpacity;
                    if (newPosition == 0) {
                        newOpacity = 1;
                    } else {
                        newOpacity = data.itemOpacities[Math.abs(newPosition)-1];
                    }

                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;
                $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 moveItem($item, direction) {
                var oldPosition = $item.data('currentPosition'), newPosition;
                if (direction == false) {
                    newPosition = oldPosition - 1;
                } else {
                    newPosition = oldPosition + 1;
                }
                if (Math.abs(newPosition) <= options.flankingItems + 1) {
                    data.itemsAnimating++;
                    performCalculations($item, newPosition);
                    $item.css('z-index',$item.data().depth);
                    $item
                        .animate({
                            left: $item.data().left,
                            width: $item.data().width,
                            height: $item.data().height,
                            top: $item.data().top,
                            opacity: $item.data().opacity
                        },data.currentSpeed,options.animationEasing, function () {
                            itemAnimationComplete($item, newPosition, direction);
                        });
                } else if (Math.abs(newPosition) > options.flankingItems) {
                    $item.data('currentPosition',newPosition);
                }
            }
            function itemAnimationComplete($item, newPosition, direction) {
                if (newPosition == 0) {
                    options.movedToCenter($item);
                }
                data.itemsAnimating--;
                if (data.itemsAnimating == 0) {
                    data.carouselRotationsLeft -= 1;
                    data.currentlyMoving = false;
                    if (data.carouselRotationsLeft > 0) {
                        rotateCarousel(direction, 0);
                    } else {
                        data.currentSpeed = options.speed;
                    }
                }
            }
            function stopAnimations() {
                for (var i = 0; i < data.items.length; i++) {
                    data.items[i].stop();
                }
            }
            function rotationAllowed(direction) {
                if (data.currentlyMoving == true)
                    return false;
                if (direction == true && data.items[0].data().currentPosition == 0)
                    return false;
                if (direction == false && data.items[data.totalItems-1].data().currentPosition == 0)
                    return false;
                return true;
            }

            function rotateCarousel(direction, rotations) {
                if (rotationAllowed(direction)) {
                    data.currentlyMoving = true;
                    data.itemsAnimating = 0;
                    data.carouselRotationsLeft += rotations;
                    if (options.quickerForFurther == true) {
	                    if (rotations > 1) {
	                        data.currentSpeed = options.speed / rotations;
	                    }
	                    data.currentSpeed = (data.currentSpeed < 100) ? 100 : data.currentSpeed;
                    } else {
                    	data.currentSpeed = options.speed;
                    }
                    for (var i = 0; i < data.items.length; i++) {
                        var $item = $(data.items[i]);
                        var currrentPosition = $item.data().currentPosition;
                        if (currrentPosition >= ((options.flankingItems*-1)-1) && currrentPosition <= (options.flankingItems)+1) {
                            moveItem($item, direction);
                        } else {
                            if (direction == true) {
                                $item.data('currentPosition',currrentPosition+1);
                            } else {
                                $item.data('currentPosition',currrentPosition-1);
                            }
                        }
                    }
                }
            }

            $('img',data.containerIDTag).live("click", function () {
                var itemPosition = $(this).data().currentPosition;
                var rotations = Math.abs(itemPosition);
                if (itemPosition < 0) {
                    rotateCarousel(true, rotations);
                } else if (itemPosition > 0) {
                    rotateCarousel(false, rotations);
                } else {
                    options.clickedCenter($(this));
                }
            });
            $('#left').click(function () {
                rotateCarousel(false,1);
            });
            $('#right').click(function () {
                rotateCarousel(true,1);
            });
        });
    };

    $.fn.waterwheelCarousel.defaults = {
        startingItem:               1,
        startingItemSeparation:     150,
        itemSeparationFactor:       .5,
        startingWaveSeparation:     30,
        waveSeparationFactor:       .75,
        itemDecreaseFactor:         .8,
        opacityDecreaseFactor:      .5,
        centerOffset:               40,
        flankingItems:              4,
        speed:                      300,
        animationEasing:			'linear',
        quickerForFurther:			true,
        movedToCenter:              $.noop,
        clickedCenter:              $.noop
    };
})(jQuery);
