/*
 * FoundryShow - A jquery extension to make a scroll bar with snapping and go-to highlights.
 *
 * Copyright (c) 2011 Matthew Drake (w) The Foundry Agency
 *
 * Licensed under the MIT license:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 *
 */

( function($) {
	
	var defaults = {
		viewer:false,
		//content is, by default, all conveyor children
		content:false,		
		controls:{
			index:false,
			play:false,
			pause:false,
			next:false,
			prev:false		
		},
		logicalkeys:true,
		events:{
			snap:function(){},
			next:function(){},
			prev:function(){},
			pass:function(){},
			play:function(){},
			place:function(){},
			slide:function(){},
			pause:function(){},
			_pass:function(e,ui,item){
				
				//this._clearSnap.call(this,e,ui,item);
				this.events.pass.call(item,e,ui);
			}
		},		
		slice:1,
		repeat:true,
		animate:{
			cycle:150,
			
			reverse:false,
			pause:false,
			
			offset:0
		},
		index:0,
		slider:false,
		sliderOpts:{
			orientation:"horizontal",
			min:0,
			max:1,
			value:0			
		},
		value:0,
		max:1,
		min:0,
		snapSpeed:20,
		//left, center, right
		focusPoint:"center",
		//left, center, right
		restPoint:"left",
		abs:function(val){
			if(val<0)
				return val*-1;
			return val;
		},
		_input:{},
		_conveyor:false,
		_contentlength:0,
		
		_findRest:function(val){
			var self = this;
			
			
			
			return Math.max(Math.round(val+(self._framewidth/2)),this._totalwidth);
		},
		_findFocus:function(val){
			//This should always be the left side
			var self = this;
			
			if(isNaN(val))
				val = val.left;
			
			switch(self.focusPoint){
				
				case "left":
					return Math.round(val);
				case "right":
					return Math.round(val+(self._framewidth));
				case "center":
				default:
					return Math.round(val+(self._framewidth/2));
			}
			
			
		},
		_checkBounds:function(val){
			var self = this;
			return Math.max(Math.min(val,self._totalwidth-self._framewidth),0);
		},
		_checkFocus:function(e,ui){
			var data = this;
			var position = data._findFocus.call(data,data.value);
			
			var distances = [];
			for(var i =0;i<data.content.length;i++){
				var item = data.content[i];
				
				var val = $(item).data("foundryShow.pos").left;
				
				var mid = data._findFocus.call(data,Number(val));
				
				
				var dist = data.abs(position-mid);
				
				var distance ={item:item,distance:dist,index:i};
				
				distances.push(distance);
			}
			distances.sort(function(a,b){
				return a.distance-b.distance;
			});
			
			if(!data._closest||data._closest!=distances[0].item){
				data._closest = distances[0].item;
				data._set(distances[0].index);
				data.events._pass.call(data,e,ui,data._closest);
			}
			return data._closest;
		},
		
		//this is too long
		_doSnap:function(target,callback){
			var data = this;
			
			data._clearSnap();
			
			if(target ===null||target ===undefined){
				
				if(!data._closest)
					return;
				target=data._closest;			
			}else{
				if(typeof target !== 'object'){
					
					target = data.content[target];
					
				}				
			}
			
			var val = data.value;
			var posObj = $(target).data("foundryShow.pos");
			
			var restVal = data._checkBounds.call(data,posObj[data.restPoint]);
			
			
			//console.log(posObj[data.focusPoint]+":"+data.focusPoint);
			data._set($(target).data("foundryShow.i"));
			
			$(data.viewer).data("foundryShow.target",target);
			
			//console.log("snapping to:"+val+": from "+data.index+" : "+data.focusPoint);
			$(data.viewer).tween(
				
				function(e,data){
					var options = $(this).data("foundryShow");
					options._place.call(options,data.tpos,'snap');
					//$(this).slider("value",data.pos);
				},
				{
					length:data.snapSpeed,					
					ease:"outQuad",
					start:val,
					end:(restVal),
					oncomplete:function(e,d){
						var options = $(this).data("foundryShow");
						options.events.snap.call(this,e,data,$(this).data("foundryShow.target"));
						if(callback)
							callback.apply(this);
					}
				}
				
			);
			
			return target;
		},
		_clearSnap:function(){
			var data = this;
			$(data.slider).tween('remove');
			
		},
		_totalwidth:0,
		_framewidth:0,
		_indexclick:function(anch,index){
			var data = this;
			$(data.viewer).foundryShow('moveto',index);
			if(data._input["frame"]){				
				$(data._input["frame"]).enterFrame('resetCycle');
			}
		},
		_closest:false,
		
		//Hidden method to place conveyor, this should come from the _findRest method
		_place:function(val,src){
			var data=this;
			val = data._checkBounds.call(data,val);
			$(data.conveyor).css("left", "-" + val + "px");
			data._refresh.call(data,val,src);
			data.value=val;
			data.pos = val/data.max;
			data.events.place.call(data.viewer,data);
		},
		//Hidden method to place conveyor, this should come from the _findRest method
		_set:function(val){
			var data=this;
			data.index=val;
			
			if(data.controls.index){
				$(data.controls.index).toggleClass("active",false);
				$($(data.controls.index).get(data.index)).toggleClass("active",true);
			}
		},
		_next:function(){
			var data = this;
			$(data._input["frame"]).enterFrame('resetCycle');
			
			
			var length = data.content.length;
			
			var next = data.slice+data.index;
			var view = data.slice;
			
			
			if(next+data.slice>=length){
				if(next<length){
				
					data._set(length-view);
				}else{
					data._set(0);
				}
				
			}else{	
				data._set(next);
				
			}
			
			
			
			
			
			
			data._doSnap.call(data,data.index);
			
		},
		_prev:function(){
			var data = this;
			$(data._input["frame"]).enterFrame('resetCycle');

			
			var length = data.content.length;
			
			var next = data.index-data.slice;
			var view = data.slice;
			
			
			
			if(next<0){
				
				if(data.index>0){
				
					data._set(0);
				}else{
					data._set(length-view);
				}
				
			}else{	
				data._set(next);
				
			}
			
			data._doSnap.call(data,data.index);
		},
		
		//this is meant to refresh all controllers. 
		//The src param is meant to exempt certain controllers to avoid recursion
		_refresh:function(val,src){
			var data = this;
			if(!src)
				src='value';
			
			if(src!='slider'&&data.slider){
				$(data.slider).slider('value',val);				
			}
			if(data.slider&&src=='slider'){
				
				if(data._input["frame"]){
					
					$(data._input["frame"]).enterFrame('resetCycle');
				}
			}
		}
		
	};
	
	
	var methods = {
		    base : function( opt) { 
				
				/*
				 * 
				 * Extend defaults and create new options object
				 */
				var options = $.extend(true,{},defaults, opt);
				
				
				var i = options.i;
				
				/*
				 * 
				 * Declare viewer, this is the content container
				 */
				var viewer = $(this);
				
				//Frame width is a constant
				options._framewidth = $(viewer).width();
				
				//Set jquery ui classes as well as convenience classes for implementation
				$(viewer).toggleClass("foundry-slide-viewer slide-viewer ui-corner-all",true);
				
				//Add conveyor, uneccessary wrapper if plain data, returns viewer
				var conveyor = $($(viewer).wrapInner("<div class='content-conveyor ui-helper-clearfix'></div>")).children();
				
				//Content may be explicitly declared, but it probably isn't
				if(!options.content)
					options.content = $(conveyor).children();
				else if(options.content !== 'object')					
					options.content=$(options.content);					
				
				
				//Get all positions, that way we can asses focus and rest pos later
				$(options.content).each(function(i) {	
					var outerWidth =  $(this).outerWidth( true );
					
					var left = options._totalwidth;
					
					var right = options._totalwidth-options._framewidth+outerWidth;
					
					
					var center = options._totalwidth-(options._framewidth/2)+(outerWidth/2);
					
					var pos = {
							left:left,
							right:right,
							center:center};
					//console.log(pos);
					
					$(this).data("foundryShow.pos",pos);
					
					$(this).data("foundryShow.i",i);
				
					options._contentlength++;
					options._totalwidth += outerWidth;
				});
			
				//Force conveyor to hold all slides
				conveyor.width(options._totalwidth);
				
			    //Set viewer to options object
			    options.viewer=viewer;
			    //Set conveyor to options object
			    options.conveyor=conveyor;
			    
			    //Far left
			    options.max = (options._totalwidth - options._framewidth);
			    
			    //create slider if requested
			    if(options.slider){
			    	
			    	var sliderOpts = {
					    	max: options.max,
					    	//change and slide do identical things... for now
					      	slide: function(e, ui) {
			    				
								var data = $(this).data("foundryShow");
								var val = $(this).slider('value');
								
								data._place.call(data,val,'slider');
								
								data._checkFocus.call(data,e,ui);
								
								$(this).data("foundryShow",data);
								
								
								
					      	},
					      	change:function(e, ui){
					      		
					      		var data = $(this).data("foundryShow");
								
					      		var val = $(this).slider('value');
								data._place.call(data,val,'slider');
								data._checkFocus.call(data,e,ui);
								
								
								$(this).data("foundryShow",data);
					      	},
					      	//stop - check snap, snaps are only done by request
					      	stop:function(e, ui){
					      		
					      		//wait 4 frames then do snap
					      		$(this).wait(function(){
					      		var data = $(this).data("foundryShow");
								data._doSnap.call(data);
								$(this).data("foundryShow",data);
					      		},4);
					      	}
					    };
			    	
			    	//set slider to options array
			    	options.slider = $(options.slider).slider(sliderOpts);
			    	options._input["slider"]=options.slider;
			    	$(options.slider).data('foundryShow', options );
			    }
			    
			    if(options.controls.index){
			    	//may be a template
			    	if($(options.controls.index).length!=options.content.length){
			    		
			    		var template = $(options.controls.index).get(0);
			    		
			    		var p = $(template).parent();
			    		var html = $(p).html();
			    		p.html("");
			    		var count = options.content.length;
			    		for(var i=0;i<count;i++)
			    		{
			    			p.append(html);
			    		}
			    		p.children().each(function(i){
			    			$(this).attr("rel",i);
			    		});
			    		p.children().click(function(){
			    			options._indexclick.call(options,this,$(this).attr("rel"));
			    		});
			    		
			    	}
			    	
			    }
			    if(options.controls.prev){
			    	$(options.controls.prev).data("foundryShow",options);
			    	$(options.controls.prev).click(function(){
			    		var options = $(this).data("foundryShow");
			    		options._prev();
			    		if(options.index==0){
			    			$(this).toggleClass("active",false);			    			
			    		}else{
			    			$(this).toggleClass("active",true);
			    		}
			    	});
			    }
			    
			    if(options.controls.next){
			    	$(options.controls.next).data("foundryShow",options);
			    	$(options.controls.next).click(function(){
			    		
			    		var options = $(this).data("foundryShow");
			    		options._next();
			    		if(options.content.length-1==options.index){
			    			$(this).toggleClass("active",false);			    			
			    		}else{
			    			$(this).toggleClass("active",true);
			    		}
			    	});
			    }
			    //
				//redundant, probably
				$(this).data('foundryShow', options );
				
				//animation
				if(options.animate){
					
					
					
					var cy = $(this).enterFrame('cycle',function(e,d){
						
						var data = $(this).data("foundryShow");
						if(data.animate.pause)
							return;
						data._next();
						
					},{interval:options.animate.cycle,offset:options.animate.offset});
					
					options._input["frame"]=cy;
					
					
					
				}
				if(options.logicalkeys){
					$(this).hover(function(){
						
						$(window).bind('keyup',options, function(e) {
							var data = e.data;
							
							switch(Number(e.which)){
								case 37:
									data._prev();
									break;
								case 39:
									data._next();
									break;
							
							}
							
						});
						
						
					},function(){
						
						$(window).unbind('keyup');
						
					});
					
				}
				
				if(i){
					options.index=i;
					if(options.index>=options.content.length)
						options.index=0;
					
					options._doSnap.call(options,options.index);
					
					
				}
				
				return this;
				
			},
	
		    enable : function( ) { 
				$(this).slider("enable");
			},
			disable : function( ) { 
				$(this).slider("disable");
			},
			
		    reset : function( ) { 
				//pass
			},
		    moveto : function( options,i ) { 
				//pass
				if(!i)
					i=options;
				
				if(isNaN(i)){
					i = $(i).data("foundryShow.i");
				}
				
				var data = $(this).data("foundryShow");
				data._set(i);
				if(data.index>=data.content.length||data.index<0)
					data._set(0);
				var callback=null;
				if(options.callback)
					callback=options.callback;
				data._doSnap.call(data,data.index,callback);
			},
		    play : function( options ) { 
				var data = $(this).data("foundryShow");
				data.animate.pause=false;
				$(this).data("foundryShow",data);
			},
			stop : function( options ) { 
				var data = $(this).data("foundryShow");
				
				data.animate.pause=true;
				
				$(this).data("foundryShow",data);
			},
		    movetoAndStop : function( options,i ) { 
				$(this).foundryShow('moveto',options,i);
				$(this).foundryShow('stop',options);
			}
	};
	
	
$.fn.extend(
{
	foundryShow: function(method)
	{
		
		if ( methods[method] ) {
	      return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
	    } else if ( typeof method === 'object' || ! method ) {
	      return methods.base.apply( this, arguments );
	    } else {
	      $.error( 'Method ' +  method + ' does not exist on jQuery.foundryShow' );
	    }		
	}
});

$.extend(
	{
		foundryShow: function(method)
		{
			//return $("window").foundryShow(method);		
		}
	});


})(jQuery);
