Ext.namespace('Ext.ux');
Ext.ux.Carousel = Ext.extend(Ext.util.Observable, {
	constructor: function(settings){
		//Apply Settings
		Ext.apply(this, settings);
		
		//Register events
		this.addEvents(
			'init'
			,'beforechange'
			,'change'
		);
		
		//Call super
		Ext.ux.Carousel.superclass.constructor.apply(this, arguments);
		
		//Init this component
		this.init();
	}
	
	//Private
	,items: []
	,animating: false
	,interval: null
	,currentIndex: 0
	,outerContainer: null
	
	//Public
	,width: 500
	,height: 500
	,autoHeight: false
	,container: null
	,overlay: null
	,nextButton: null
	,previousButton: null
	,selector: ''
	,wait: 5
	,animationDuration: 1 
	,easing: 'bounceOut'
	,autoStart: true
	,slideshow: true
	
	
	,init: function(){
		this.initListeners();
		this.initElements();
		this.initButtons();
		this.initItems();
		
		if(this.autoStart){
			this.start();
		}
		
		//Fire init event
		this.fireEvent('init');
	}
	,initListeners: function(){
		if(this.autoHeight){
			this.on('change', function(){
				this.adjustHeight();
			}, this);
		}
	}
	,initElements: function(){
		this.container = Ext.get(this.container);
		this.overlay = Ext.get(this.overlay);
		this.nextButton = Ext.get(this.nextButton);
		this.previousButton = Ext.get(this.previousButton);
		
		//Create the outer container
		this.outerContainer = Ext.get(Ext.DomHelper.insertBefore(this.container,{
			tag: 'div'
			,style: {
				position: 'relative'
				,width: this.width + 'px'
				,height: this.height + 'px'
				,overflow: 'hidden'
			}
		}));
		
		//Move the container inside the outer container
		this.outerContainer.appendChild(this.container);
		
		//show the container
		if(this.container != null){
			this.container.set({
				style: {
					position: 'relative'
				}
			})
			this.container.show();
		}
	}
	,initItems: function(){
		this.getItems();
	}
	
	,initButtons: function(){
		
		//Add handler for next button
		if(this.nextButton != null){
			this.nextButton.on('click', this.next, this);
		}
		
		//Add handler for previous button
		if(this.previousButton != null){
			this.previousButton.on('click', this.previous, this);
		}
		
		//Pause movement when user hovers over container
		if(this.overlay == null){
			this.overlay = this.container;
		}
		this.overlay.on('mouseover', function(){
			this.stop();
		}, this);
		this.overlay.on('mouseout', function(){
			this.start();
		}, this);
	}
	
	,getItems: function(){
		//Get the items
		this.items = this.container.select(this.selector).elements;
		
		//Hide, style and position all elements
		this.items.each(function(el){
			var item = Ext.get(el);
			
			//Style element
			item.set({
				style: {
					position:'absolute'
					,top:0
					,left:0
					,width: this.width + 'px'
					,overflow:'hidden'
				}
			});
			
			//Hide the element
			item.hide();
		}, this);
		
		//Align and show the first
		if(this.items.length){
			Ext.get(this.items[0]).setLeft(0 + 'px');
			Ext.get(this.items[0]).show();
		}
	}
	
	,alignItems: function(lastIndex, currentIndex, next){
		if(next == null){
			next = true;
		}
		var currentItem = Ext.get(this.items[lastIndex]);
		var nextItem = Ext.get(this.items[currentIndex]);
		var left = this.width;
		if(!next){
			left = -this.width;
		}
		
		//Position the next item
		nextItem.setLeft(left + 'px');
		nextItem.show();
		
		//If autoHeight is enabled adjust the height
		var largestHeight = currentItem.getHeight();
		if(nextItem.getHeight() > largestHeight){
			largestHeight = nextItem.getHeight();
		}
		this.outerContainer.setHeight(largestHeight + "px");
	}
	
	,next: function(){
		if(!this.animating){
			//get the old index
			var lastIndex = this.currentIndex;
			
			//get next index
			this.currentIndex = this.getNextIndex();
			
			//Do not run if last and current are the same
			if(lastIndex == this.currentIndex){
				return false;
			}
			
			//align the items
			this.alignItems(lastIndex, this.currentIndex, true);
			
			//animate the container
			this.container.animate(
			    {left: { to: -this.width, unit: 'px'} },
			    this.animationDuration,
			    this.animationComplete.createDelegate(this),
			    this.easing,
			    'motion'
			);
			
			//is animating
			this.animating = true;
			
			//restart interval
			this.reset();
			
			//Fire event
			this.fireEvent('beforechange');
		}
	}
	,previous: function(){
		if(!this.animating){
			
			//get the old index
			var lastIndex = this.currentIndex;
			
			//get previous index
			this.currentIndex = this.getPreviousIndex();
			
			//Do not run if last and current are the same
			if(lastIndex == this.currentIndex){
				return false;
			}
			
			//Align the items
			this.alignItems(lastIndex, this.currentIndex, false);
			
			//Animate the container
			this.container.animate(
			    {left: { to: this.width, unit: 'px'} },
			    this.animationDuration,
			    this.animationComplete.createDelegate(this),
			    this.easing,
			    'motion'
			);
			
			//Is animating
			this.animating = true;
			
			//restart interval
			this.reset();
			
			//Fire event
			this.fireEvent('beforechange');
		}
	}
	,animationComplete: function(){
		//hide all but the current
		for(var i=0; i < this.items.length; i++){
			if(i != this.currentIndex){
				Ext.get(this.items[i]).hide();
			}
		}
		
		//Set offsets back to zero
		var currentItem = Ext.get(this.items[this.currentIndex]);
		currentItem.setLeft(0);
		this.container.setLeft(0);
		
		this.animating = false;
		
		//Fire event
		this.fireEvent('change');
	}
	,adjustHeight: function(){
		var currentItem = Ext.get(this.getItem(this.currentIndex));
		this.outerContainer.setHeight(currentItem.getHeight() + "px");		
	}
	,start: function(){
		if(this.slideshow){
			this.interval = setInterval(function(){
				this.next();
			}.createDelegate(this), (this.wait*1000));
		}
	}
	,stop: function(){
		clearInterval(this.interval);
	}
	,reset: function(){
		this.stop();
		this.start();
	}
	
	//Utility Functions
	,getNextIndex: function(){
		var index = this.currentIndex+1;
			
		//Check the doundaries
		if(index > this.items.length-1){
			index = 0;
		}
		
		return index;
	}
	,getPreviousIndex: function(){
		var index = this.currentIndex-1;
			
		//Check boundaries
		if(index < 0){
			index = this.items.length-1;
		}
		
		return index;
	}
	,getItem: function(index){
		return this.items[index];
	}
	
});
