var InfiniSlider = new Class({

	Implements: [Events,Options],
	options: {
		container: null, 			/* outer container */
		transition: Fx.Transitions.linear, /* type of transition to use*/
		duration: 800, 				/* transition duration */
		autoSlideTime: false, 			/* auto slide - as milliseconds ( ie: 10000 = 10 seconds ). Leave as false to not auto-slide */
		indexItems: null,			/* array of items to use as a clickable index of all the rotating items. Optional */
		indexEventName: 'click',	/* event to watch for interaction on index items. would normally be click to jump to specific item */
		addMouseEvents: true,		/* Automatically add pause and resume mouse events on hover */

		nextButton: null,			/* Next Button */
		previousButton: null,		/* Previous Button */

		heightOverride: null,		/* Manual override for height */
		widthOverride: null,		/* Manual override for width */

		animateOn: 'left',				/* Which direction to animate, left for horizontal or top for vertical */
		showItems: 1					/* Number of items to show */
	},

					/*--------------------------------------------------------o
	----------------\                     Member Variables                    |
		variables    \-------------------------------------------------------*/

	indexItems: null,

	initialize: function(options){

		//set up the options (mainly just combines the defaults with whatever is passed in the options array)
		this.setOptions(options);

		this.container = this.options.container;

		if (this.container == null) return false;

		this.items = this.container.getChildren();

		this.itemsCount = this.items.length;

		if ( this.itemsCount <= this.options.showItems ) {
			return false;
		}

		this.items.each(function (elem, idx) {
			elem.setStyles( {
				position: 'absolute',
				left: '0px',
				display: 'none'
			});
		});

		// Find largest dimensions for sliding
		var maxHeight = 0;
		var maxWidth = 0;
		for (i = 0; i < this.itemsCount; i++) {
			var dimensions = this.items[i].measure(function() {
				return this.getComputedSize({
					styles: ['padding', 'border', 'margin']
				});
			});

			if (dimensions.totalHeight > maxHeight) {
				maxHeight = dimensions.totalHeight;
			}

			if (dimensions.totalWidth > maxWidth) {
				maxWidth = dimensions.totalWidth;
			}
		}

		if (this.options.heightOverride) {
			this.itemHeight = this.options.heightOverride;
		} else {
			//get the height of the first item
			this.itemHeight = maxHeight;
		}

		if (this.options.widthOverride) {
			this.itemWidth = this.options.widthOverride;
		} else {
			//get the width of the first item
			this.itemWidth = maxWidth;
		}

		if (this.options.animateOn == 'left' || this.options.animateOn == 'right') {
			this.itemDimension = this.itemWidth;
		}

		if (this.options.animateOn == 'top' || this.options.animateOn == 'bottom') {
			this.itemDimension = this.itemHeight;
		}

		// Defaults
		this.containerHeight = this.itemHeight;
		this.containerWidth = this.itemWidth;

		// Show multiple items
		if (this.options.showItems > 0) {
			if (this.options.animateOn == 'left' || this.options.animateOn == 'right') {
				this.containerWidth = this.itemWidth * this.options.showItems;
			}

			if (this.options.animateOn == 'top' || this.options.animateOn == 'bottom') {
				this.containerHeight = this.itemHeight * this.options.showItems;
			}
		}

		// set up the container
		this.container.setStyles({
			overflow : 'hidden',
			position : 'relative',
			height   : this.containerHeight
		});

		// Show first items
		for(i = 0; i < this.options.showItems; i++) {
			this.items[i].setStyle(this.options.animateOn, i * this.itemDimension);
			this.items[i].setStyle('display', 'block');
		}
		this.items[0].addClass('current');


		/* Set the current item index to 0. this property is
		used to determine which item is visible (and to
		calculate which item should be displayed next) */
		this.currentItem = 0;

		// used to determine if the sliding animation is in the process of animating.
		this.animating = false;

		// used to determine if the auto-sliding is running.
		this.isRunning = false;

		/* set up the index items if necessary. Index items are for the user
		to jump to a specific item when they click a thumbnail etc. It's up
		to you to generate these yourself and in the right order. */
		if (this.options.indexItems) {
			this.indexItems = $$(this.options.indexItems);
			this.indexItems.each(function(indexItem,indexItemIndex) {
				parentThis = this;
				if (indexItemIndex == 0) {
					indexItem.addClass('current');
				}
				indexItem.addEvent(this.options.indexEventName,function(e) {
					e.stop();
					this.slideTo(indexItemIndex);
					this.stopAutoSlide();
					return false;
				}.bind(parentThis));

				indexItem.addEvent('mouseleave',function(e) {
					e.stop();
					this.startAutoSlide();
					return false;
				}.bind(parentThis));

				indexItem.setStyle('cursor', 'pointer');

			},this);
		}

		//add events for pausing and resuming the sliding when on an item
		if (this.options.addMouseEvents) {
			this.items.addEvents({
				'mouseenter':function(){
					this.stopAutoSlide();
				}.bind(this),

				'mouseleave':function(){
					this.startAutoSlide();
				}.bind(this)
			});
		}

		var parentThis = this;
		// Attach next and previous buttons
		if (this.options.nextButton) {
			$(this.options.nextButton).addEvent('click', function(e) {
				e.stop();
				parentThis.slideTo('next');
				parentThis.stopAutoSlide();
				return false;
			});
			$(this.options.nextButton).addEvent('mouseleave',function(e) {
				e.stop();
				parentThis.startAutoSlide();
				return false;
			});

			$(this.options.nextButton).setStyle('cursor', 'pointer');
		}

		if (this.options.previousButton) {
			$(this.options.previousButton).addEvent('click', function(e) {
				e.stop();
				parentThis.slideTo('previous');
				parentThis.stopAutoSlide();
				return false;
			});
			$(this.options.previousButton).addEvent('mouseleave',function(e) {
				e.stop();
				parentThis.startAutoSlide();
				return false;
			});
			$(this.options.previousButton).setStyle('cursor', 'pointer');
		}

		//start the show!
		if (this.options.autoSlideTime) {
			this.startAutoSlide();
		}

	},

	/* slide to an element elements. You can use natural language
	for the item you want to see, such as next, previous, first, last.
	Typically you'll want to view the 'next' slide. This is how the auto
	rotator works, and is the default if no index is specified. Of course,
	you can also specify a numeric index of the item you want to view as
	well. */
	slideTo: function( index ){
		if (this.animating) return false;

		this.reverse = false;
		this.direction = null;

		// if it's not auto fading (
		if (index == "auto") {
			index = 'next';
		} else {
			this.stopAutoSlide();
			this.startAutoSlide();
		}

		if (index == 'next') {
			index = this.currentItem+1;
			this.direction = 'next';
		}

		if (index == 'previous') {
			index = this.currentItem-1;
			this.direction = 'previous';
		}

		if (index == 'first') {
			index = 0;
		}

		if (index == 'last') {
			index = this.itemsCount-1;
		}

		if (index > (this.itemsCount - 1)) {
			index = 0;
		}

		if (index < 0) {
			index = this.itemsCount - 1;
		}

		if (index > (this.itemsCount - 1)) {
			index = 0;
		}

		if (index < this.currentItem) {
			this.reverse = true;
		}

		this.newItem = this.items[index];
		this.oldItem = this.items[this.currentItem];

		if (index == this.currentItem) return false;

		this.animating = true;

		if ((this.reverse || this.direction == 'previous') && this.direction != 'next') {
			this.oldItemEnd = this.itemDimension * this.options.showItems;
			this.newItemStart = -this.itemDimension;
		} else {
			this.oldItemEnd = -this.itemDimension;
			this.newItemStart = this.itemDimension;
		}

		/* Old item to hide */
		var itemScroll = this.currentItem;

		if ((this.reverse || this.direction == 'previous') && this.direction != 'next') {
			itemScroll = this.currentItem + (this.options.showItems - 1);
		} else {
			itemScroll = this.currentItem;
		}

		// check edge cases
		if (itemScroll >= this.itemsCount) {
			itemScroll = itemScroll - this.itemsCount;
		}

		if (itemScroll < 0) {
			itemScroll = this.itemsCount - 1 + itemScroll;
		}

		itemScroll = this.items[itemScroll];

		this.animationFx = new Fx.Tween(itemScroll, {
			property: this.options.animateOn,
			transition: this.options.transition,
			duration: this.options.duration
		});
		this.animationFx.start(this.oldItemEnd);

		this.oldItem.removeClass('current');



		// iterate the items to scroll
		for(elem = 0; elem < this.options.showItems; elem++) {
			/* New Items to show */
			this.doItemSlide(index, elem);

		}

		if (this.indexItems) {
			this.indexItems[this.currentItem].removeClass('current');
			this.indexItems[index].addClass('current');
		}

		this.newItem.addClass('current');
		this.currentItem = index;

	},

	doItemSlide: function (index, elem) {
		// get the item to scroll
		var itemScroll = index;
		if ((this.reverse || this.direction == 'previous') && this.direction != 'next') {
			itemScroll = index + this.options.showItems - 1 - elem;
		} else {
			itemScroll = index + elem;
		}

		// check edge cases
		if (itemScroll >= this.itemsCount) {
			itemScroll = itemScroll - this.itemsCount;
		}

		if (itemScroll < 0) {
			itemScroll = (this.itemsCount - 1) + (itemScroll + 1);
		}

		itemScroll = this.items[itemScroll];
		itemScroll.show();

		this.animationFx = new Fx.Tween(itemScroll, {
			property: this.options.animateOn,
			transition: this.options.transition,
			duration: this.options.duration,
			complete: this.stoppedAnimating()
		});

		// Set original position to slide in from and do slide
		if ((this.reverse || this.direction == 'previous') && this.direction != 'next') {
			startPos = this.newItemStart + (((this.options.showItems - 1 - elem)) * this.itemDimension);
			itemScroll.setStyle(this.options.animateOn, startPos);
			this.animationFx.start(startPos + this.itemDimension);
		} else {
			itemScroll.setStyle(this.options.animateOn, this.newItemStart * (elem + 1));
			this.animationFx.start(this.itemDimension * (elem));
		}
	},




	/* starts auto sliding */
	startAutoSlide: function(){
		if (!this.options.autoSlideTime) return false;
		/* to prevent us from setting up multiple periodicals.
		Things can go a bit wobbly otherwise.  */
		if (this.isRunning) return;
		this.isRunning = true;
		this.autoSlide = this.slideTo.periodical(this.options.autoSlideTime, this, 'auto');
	},

	/* stops auto sliding */
	stopAutoSlide: function(){
		if (!this.options.autoSlideTime) return false;
		clearInterval(this.autoSlide);
		this.isRunning = false;
	},

	stoppedAnimating: function(){
		this.doStopAnimating.delay(this.options.duration, this);
	},

	doStopAnimating: function() {
		this.animating = false;
	}
})
