/*!
 * jQuery Scrolly plugin v0.1
 * Author Mike Parkin
 */
$.fn.scrollyTags = {};

$.fn.scrolly = function(options){

	var settings = {
		
		/**
		 * Selectors used in the iteration process
		 */
		selectors: {
			container: '.container',	// Holder of the items to scroll through
			controls: '.navigation',
			items: '*'
		},
		
		classes: {
			active: 'active',
			enabled: 'scrolly-enabled',
			disabled: 'scrolly-disabled'
		},

		tag: false,
		
		/**
		 * Should we go back to item 1 when we get to end?
		 */
		wrapAround: true,
		
		/**
		 * Auto scroll time 0|false for no scroll
		 */
		autoscroll: false,
		
		/**
		 * If auto scrolling, should we stop when user is hovered over?
		 */
		pauseOnHover: true,
		
		/**
		 * Automatically hide all elements and show the first
		 */
		autohide:true,

		/**
		 * Auto hide the controls if there arent enough elements
		 */
		autoHideControls: true,

		/**
		 * Minimum elements required to not hide controls
		 */
		minimumElements: 0,
		
		/**
		 * Move to a particular element
		 */
		move: function(i){
							
			// Hide the active element
			this.hide(this.active());
			
			// Show the next index
			this.show(this.at(i));
			
			// Run the moved callback
			this.moved();
		},
				
		/**
		 * Move to the previous position or wrap around if allowed
		 */
		prev: function(){
			if ( ! this.canMovePrev()){
				return;
			}
		
			if ( ! this.willWrapPrev())
			{
				prev = this.valuePrevIncrement();
			}
			else
			{
				if ( ! this.wrapAround)
				{
					return;
				}
				
				prev = this.valuePrevWrap();
			}
						
			this.move(prev);
		},
		
		/**
		 * Move to the next position or wrap around if allowed
		 */
		next: function(){
			
			if ( ! this.canMoveNext()){
				return;
			}

			if ( ! this.willWrapNext())
			{
				next = this.valueNextIncrement();
			}
			else
			{
				if ( ! this.wrapAround)
				{
					return;
				}
				
				next = this.valueNextWrap();
			}
									
			this.move(next);		
		},

		/**
		 * Determine if the scroller can move to the previous position
		 */
		canMovePrev: function(){
			return this.total() > 1 && (this.current() > 1 || this.wrapAround);
		},

		/**
		 * Determine if the scroller can move to next position
		 */
		canMoveNext: function(){
			return this.total() > 1 && (this.current() < this.total() || this.wrapAround);
		},
		
		/**
		 * Will we wrap goin prev?
		 */
		willWrapPrev: function(){
			return (this.current() <= 1);
		},
		
		/**
		 * Value if we increment prev
		 */
		valuePrevIncrement: function(){
			return this.current() - 1;
		},
		
		/**
		 * Value if we wrap around prev
		 */
		valuePrevWrap: function(){
			return this.total();
		},
		
		/**
		 * Will we wrap going next?
		 */
		willWrapNext: function(){
			return (this.current() > this.total() - 1);
		},
		
		/** 
		 * Value if we increment next
		 */
		valueNextIncrement: function(){
			return this.current() + 1;
		},
		
		/**
		 * Value if we wrap going next
		 */
		valueNextWrap: function(){
			return 1;
		},				
		
		/**
		 * Return all of the items
		 */
		items: function(){
			return $(this.selectors.container, this.el).children(this.selectors.items);
		},
		
		/**
		 * Get the node at an index
		 */
		at: function(i){
			return $(this.selectors.container, this.el).children(this.selectors.items).eq(i-1);
		},	
		
		/**
		 * Return the active element 
		 */
		active: function(){
			return this.items().filter('.'+this.classes.active); 
		},
				
		/**
		 * Get the total number of elements in the container
		 */
		total: function(){
			return $(this.selectors.container, this.el).children(this.selectors.items).length;
		},
		
		/**
		 * Get the index of the current element
		 */
		current: function(){
			return this.active().prevAll().length + 1;
		},

		/**
		 * hide the controls
		 */
		hideControls: function(){
			return this.controls().hide();
		},

		/**
		 * Show the controls
		 */
		showControls:function(){
			return this.controls().show();
		},
		
		/**
		 * Show an element of a container
		 */
		show: function(item){
			$(item).stop(true,true).addClass(this.classes.active).fadeIn(500);
		},
		
		/**
		 * Hide an element of a container
		 */
		hide: function(item){
			$(item).removeClass(this.classes.active).hide();
		},

		/**
		 * Return the controls of the scroller
		 */
		controls: function(){
			return $(this.selectors.controls, this.el);
		},

		/**
		 * Should controls be hidden
		 */
		shouldHideControls: function(){
			return this.total() < this.minimumElements;
		},
		
		/**
		 * Attach a selector to the prev event
		 */
		bindPrev: function(anchor){
			anchor.click(function(){
				settings.prev();
				return false;
			});
		},
		
		/**
		 * Attach a selector to the next event
		 */
		bindNext: function(anchor){
			anchor.click(function(){
				settings.next(anchor);
				return false;
			});
		},
		
		
		/**
		 * Automatically hide the first element
		 */
		autohideSetup: function(){
			if (this.total()){
				// Hide all, show the first and add active class
				this.items().hide().eq(0).show().addClass(this.classes.active);

				// Run the moved event
				this.moved(1);
			}			
		},
		
		/**
		 * Automatically scroll through elements
		 */
		autoscrollSetup: function(){
			if (this.pauseOnHover){
				this.startPauseOnHover();
			}
			
			this.startAutoProgress();
		},

		/**
		 * Pause on hover
		 */
		startPauseOnHover: function(){
			var self = this;

			this.el.hover(function(){
				self.stopAutoProgress();
			}, function(){
				self.startAutoProgress();
			});
		},

		/**
		 * Stop pause on hover
		 */
		stopPauseOnHover: function(){
			this.el.unbind('mouseenter mouseleave');
		},

		beforeAutoProgress: function(){},
		beforeStopAutoProgress: function(){},

		/**
		 * Start callback timers for autoscroll
		 */
		startAutoProgress: function(){
			var self = this;
			this.stopAutoProgress();
			this.beforeAutoProgress();
			this.timer = setTimeout(function(){
				self.next();
				self.startAutoProgress();
			}, this.autoscroll);
		},

		/**
		 * Stop callback timers for autoscroll
		 */
		stopAutoProgress: function(){
			this.beforeStopAutoProgress();
			clearTimeout(this.timer);			
		},

		/**
		 * Stopping auto progress
		 */
		disable: function(){

			if (this.autoscroll){
				this.stopAutoProgress();

				if (this.pauseOnHover){
					this.stopPauseOnHover();
				}
			}

			this.el.removeClass(this.classes.enabled).addClass(this.classes.disabled);
		},


		/**
		 * Setup the stage, i.e hide all show first
		 */
		enable: function(){
			if (this.el.hasClass('scrolly-enabled')){
				return;
			}

			this.showControls();

			if (this.autohide) {
				this.autohideSetup();
			}

			if (this.autoscroll) {
				this.autoscrollSetup();
			}

			this.el.addClass(this.classes.enabled).removeClass(this.classes.disabled);
		},

		destroy: function(){

			if (this.autoscroll){
				this.stopAutoProgress();

				if (this.pauseOnHover){
					this.stopPauseOnHover();
				}
			}

			this.el = {};
		},
		
		/**
		 * Called before setup
		 */
		before: function(){},
		
		/**
		 * Called after setup
		 */
		after: function(){},
		
		/**
		 * Moved callback
		 */
		moved: function(){}
	}	
	
	// Extend the defaults
	$.extend(true, settings, options);

	/**
	 * Apply to each
	 */
	return this.each(function(){
	
		if (settings.tag){
			if (typeof $.fn.scrollyTags[settings.tag] !== 'undefined'){
				$.fn.scrollyTags[settings.tag].destroy();
			}

			$.fn.scrollyTags[settings.tag] = settings;
		}
		
		// Store the current scrolly
		settings.el = $(this);
		
		// Run the preSetup callback
		settings.before();
		
		// Run the setup code
		settings.enable();

		// Auto hide the controls
		if (settings.autoHideControls && settings.shouldHideControls()){
			settings.hideControls();
		}
		
		// Execute the ready callback
		settings.after();
	});
}
