(function ($) {
    $.fn.waterwheelCarousel = function (options) {
        options = $.extend({}, $.fn.waterwheelCarousel.defaults, options || {});
        return $(this).each(function () {
            var data = {
                itemsContainer: $(this).find(".carousel-images"),
                totalItems: $(this).find(".carousel-images div").length,
                containerWidth: $(this).width(),
                containerHeight: $(this).height(),
                currentCenterItem: null,
                items: [],
                itemDistances: [],
                waveDistances: [],
                itemWidths: [],
                itemHeights: [],
                itemOpacities: [],
                carouselRotationsLeft: 0,
                currentlyMoving: false,
                itemsAnimating: 0,
                currentSpeed: options.speed,
                intervalTimer: null
            };
            beforeLoaded();
            preload(function () {
                setupDistanceArrays();
                setupCarousel();
                setupStarterRotation()
            });

            function autoPlay(stop) {
                clearTimeout(data.autoPlayTimer);
                if (!stop && options.autoPlay !== 0) {
                    data.autoPlayTimer = setTimeout(function () {
                        if (options.autoPlay > 0) {
                            moveOnce(false)
                        } else {
                            moveOnce(true)
                        }
                    }, Math.abs(options.autoPlay))
                }
            }
            function getPreviousNum(num) {
                var newNum = (num === 1) ? null : num - 1;
                return newNum
            }
            function getNextNum(num) {
                var newNum = (num === data.totalItems) ? null : num + 1;
                return newNum
            }
            function beforeLoaded() {
                data.itemsContainer.find('div').hide()
            }
            function preload(callback) {
                var $imageElements = data.itemsContainer.find('img'),
                loadedImages = 0,
                totalImages = $imageElements.length;
                $imageElements.each(function () {
                    $(this).load(function () {
                        loadedImages += 1;
                        if (loadedImages === totalImages) {
                            callback()
                        }
                    });
                    if (this.complete || $.browser.msie) {
                        $(this).trigger('load')
                    }
                })
            }
            function setupDistanceArrays() {
                data.itemDistances[0] = options.startingItemSeparation;
                data.waveDistances[0] = options.startingWaveSeparation;
                data.itemWidths[0] = data.itemsContainer.find('div:first').width();
                data.itemHeights[0] = data.itemsContainer.find('div:first').height();
                data.itemOpacities[0] = (1 * options.opacityDecreaseFactor);
                var i;
                for (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.itemWidths[i] = data.itemWidths[i - 1] * options.itemDecreaseFactor;
                    data.itemHeights[i] = data.itemHeights[i - 1] * options.itemDecreaseFactor;
                    data.itemOpacities[i] = data.itemOpacities[i - 1] * options.opacityDecreaseFactor;
                    if (i === options.flankingItems) {
                        data.itemWidths[i + 1] = data.itemWidths[i] * options.itemDecreaseFactor;
                        data.itemHeights[i + 1] = data.itemHeights[i] * options.itemDecreaseFactor
                    }
                }
                data.itemOpacities[data.itemOpacities.length - 1] = 0
            }
            function setupCarousel() {
                data.items = data.itemsContainer.find('div');
                var i;
                for (i = 0; i < data.items.length; i++) {
                    data.items[i] = $(data.items[i])
                }
                data.itemsContainer.css('position', 'relative').find('div').each(function (i) {
                    var newLeft, newTop;
                    if (options.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,
                        'visibility': 'visible',
                        'position': 'absolute',
                        'z-index': options.flankingItems + 2,
                        'opacity': 1
                    }).data({
                        currentPosition: 0,
                        oldPosition: 0,
                        width: $(this).width(),
                        owidth: $(this).width(),
                        height: $(this).height(),
                        oheight: $(this).height(),
                        top: newTop,
                        left: newLeft,
                        opacity: 1,
                        index: i
                    }).show()
                })
            }
            function setupStarterRotation() {
                options.startingItem = (options.startingItem === 0) ? Math.round(data.totalItems / 2) : options.startingItem;
                data.carouselRotationsLeft = 1;
                data.items[options.startingItem - 1].addClass(options.activeClassName);
                options.movedToCenter(data.items[options.startingItem - 1]);
                data.currentCenterItem = data.items[options.startingItem - 1];
                var counter, itemNum, i;
                counter = 1;
                for (itemNum = options.startingItem - 2; itemNum >= 0; itemNum--) {
                    for (i = 0; i < counter; i++) {
                        moveItem(data.items[itemNum], false)
                    }
                    counter++
                }
                counter = 1;
                for (itemNum = options.startingItem; itemNum < data.items.length; itemNum++) {
                    for (i = 0; i < counter; i++) {
                        moveItem(data.items[itemNum], true)
                    }
                    counter++
                }
            }
            function performCalculations($item, newPosition) {
                var oldPosition = $item.data().currentPosition;
                var newDistanceFromCenter = Math.abs(newPosition);
                var newWidth = data.itemWidths[newDistanceFromCenter];
                //var newHeight = data.itemHeights[newDistanceFromCenter];
                var newHeight = newWidth / 1.74;
                var widthDifference = Math.abs($item.data().width - newWidth);
                var heightDifference = Math.abs($item.data().height - newHeight);
                var waveSeparation = 0,
                centeringNumber;
                if (options.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 (options.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 (options.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('oldPosition', oldPosition);
                $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);
                    if (newPosition === 0) {
                        options.movingToCenter($item)
                    }
                    if (oldPosition === 0) {
                        options.movingFromCenter($item);
                        $item.removeClass(options.activeClassName)
                    }
                    $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)
                    })
                    $item.find('img').animate({
                        width: $item.data().width,
                        height: $item.data().height
                    }, data.currentSpeed, options.animationEasing)
                } else if (Math.abs(newPosition) > options.flankingItems) {
                    $item.data('oldPosition', oldPosition);
                    $item.data('currentPosition', newPosition)
                }
            }
            function itemAnimationComplete($item, newPosition, direction) {
                if (newPosition === 0) {
                    data.currentCenterItem = $item;
                    $item.addClass(options.activeClassName)
                }
                if ($item.data().oldPosition === 0) {
                    options.movedFromCenter($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;
                        if (data.currentCenterItem !== null) {
                            options.movedToCenter(data.currentCenterItem)
                        }
                        autoPlay()
                    }
                }
            }
            function stopAnimations() {
                var i;
                for (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
                    }
                    var i;
                    for (i = 0; i < data.items.length; i++) {
                        var $item = $(data.items[i]);
                        var currentPosition = $item.data().currentPosition;
                        if (currentPosition >= ((options.flankingItems * -1) - 1) && currentPosition <= (options.flankingItems) + 1) {
                            moveItem($item, direction)
                        } else {
                            $item.data('oldPosition', currentPosition);
                            if (direction === true) {
                                $item.data('currentPosition', currentPosition + 1)
                            } else {
                                $item.data('currentPosition', currentPosition - 1)
                            }
                        }
                    }
                }
            }
            function moveOnce(direction) {
                var currentCenterIndex = data.currentCenterItem.data().index;
                if ((currentCenterIndex === 0 && direction === true) || (currentCenterIndex === (data.totalItems - 1) && direction === false)) {
                    clearTimeout(data.autoPlayTimer);
                //                    if (options.edgeReaction === 'reset') {
                //                        rotateCarousel(!direction, data.totalItems - 1)
                //                    } else if (options.edgeReaction === 'reverse' && options.autoPlay !== 0) {
                //                        rotateCarousel(!direction, 1);
                //                        options.autoPlay *= -1
                //                    }
                } else {
                    rotateCarousel(direction, 1)
                }
            }
            
            $(this).find('.carousel-images div').mouseenter(function(){
                $(this).data.imgW = $(this).find('img').width();
                $(this).data.imgH = $(this).find('img').height();
                //$(this).data.oldLeft = $(this).css('left');
                $(this).data.oldLeft = $(this).position().left;
                $(this).data.oldTop = $(this).find('img').offset().top;
                $(this).data.oldZ = $(this).css('z-index');
                $(this).animate({opacity: 1, left: ($(this).data.oldLeft - (235 - $(this).data.imgW) / 2)}, {duration: 200, queue: false});
                $(this).find('img').animate({width: '235px', height: '135px'}, {duration: 200, queue: false});
                $(this).siblings().animate({opacity: 0.3}, {duration: 200, queue: false});
                $(this).css({'z-index': 1000});
            });
            $(this).find('.carousel-images div').mouseleave(function(){
                $(this).siblings().animate({opacity: 1}, {duration: 200, queue: false});
                $(this).animate({left: $(this).data.oldLeft}, {duration: 200, queue: false});
                $(this).find('img').animate({width: $(this).data.imgW, height: $(this).data.imgH, left: $(this).data.oldLeft}, {duration: 200, queue: false});
                $(this).css({'z-index': $(this).data.oldZ});
            });
            
            $(this).find('.carousel-images div').live("click", function () {
//                autoPlay(true);
//                options.autoPlay = 0;
//                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))
//                }
            });
            $(this).find('.carousel-images a').live("click", function (event) {
                var isCenter = ($(this).find('div').width() === $(this).find('div').data().owidth) ? true : false;
                if (options.linkHandling === 1 || (options.linkHandling === 2 && !isCenter)) {
                    event.preventDefault();
                    return false
                }
            });
            $(this).find('.carousel-controls .carousel-prev').live('click', function (e) {
                moveOnce(true);
                e.preventDefault();
                return false
            });
            $(this).find('.carousel-controls .carousel-next').live('click', function (e) {
                moveOnce(false);
                e.preventDefault();
                return false
            });
            if (options.keyboardNav) {
                $(document).keydown(function (e) {
                    if (e.which === 37 || e.which === 38) {
                        moveOnce(true)
                    } else if (e.which === 39 || e.which === 40) {
                        moveOnce(false)
                    }
                    if (options.keyboardNavOverride && (e.which === 37 || e.which === 38 || e.which === 39 || e.which === 40)) {
                        e.preventDefault();
                        return false
                    }
                })
            }
        })
    };
    $.fn.waterwheelCarousel.defaults = {
        startingItem: 0,
        startingItemSeparation: 170,
        itemSeparationFactor: 0.8,
        startingWaveSeparation: 30,
        waveSeparationFactor: 0.75,
        itemDecreaseFactor: 0.8,
        opacityDecreaseFactor: 0.5,
        centerOffset: 40,
        flankingItems: 4,
        speed: 300,
        animationEasing: 'linear',
        quickerForFurther: true,
        linkHandling: 2,
        autoPlay: 0,
        orientation: 'horizontal',
        activeClassName: 'active',
        keyboardNav: false,
        keyboardNavOverride: true,
        edgeReaction: 'reset',
        movingToCenter: $.noop,
        movedToCenter: $.noop,
        clickedCenter: $.noop,
        movingFromCenter: $.noop,
        movedFromCenter: $.noop
    }
})(jQuery);
