var SmoothScroller = Class.create();

SmoothScroller.NBR_CONTROLS	= 0;
SmoothScroller.INTERVAL		= 20;
SmoothScroller.SMOOTHNESS	= 1/5;

SmoothScroller.prototype = {
// ------------------------------------- variables -------------------------------------//
	index			: 0,
	elements		: [],	// Each elements that moves
	elementsName	: [],
	debugBox		: null,

	scrollPosition	: 0,	// Updated at each interval
	pageHeight		: 0,	// The total of the page height (including all of it content)
	clientHeight	: 0,	// The size of the client window
	maximumScroll	: 0,	// Maximum possible scroll, it's the page scroll total minus the navigateur window height
	currentPosition	: 0,	// This value tries to get closer and closer to the {scrollPostion}
	distance		: 0,	// The distance between the {scrollPositon} and the {currentPosition}
	
	smoothness		: SmoothScroller.SMOOTHNESS,
	
// ------------------------------------- functions -------------------------------------//
	add:function(el,options){
		el = $(el);
		
		if (!options) options = {};
		
		// Depending on the "style" parameter, the default scroll action get defined
		if (options.style=='absolute'){
			options.init 		= this.initiateElement_absolute;
			options.onScroll	= this.updateElement_top;
		}else if (options.style=='relative'){
			options.init 		= this.initiateElement_relative;
			options.onScroll	= this.updateElement_top;
		}else if (options.style=='margin'){
			options.init 		= this.initiateElement_margin;
			options.onScroll	= this.updateElement_margin;
		}
		
		// Initiate the element 
		if (options.init)	options.init(el);
		else				this.initiateElement_relative(el);
		
		// Use for the debugBox screen
		this.elementsName.push(el.id);
		
		// Information about the added element get saved
		this.elements.push({
			element		: el,
			onScroll	: (options.onScroll ? options.onScroll : this.updateElement_top),	// The callback, depending on the "style" property, default callback are specified
			wait		: options.wait>0 ? options.wait : 0,								// Number of pixel to wait before he start moving
			distance	: options.distance>0 ? options.distance : 0							// The possible distance (in px) he can do
		});
		
		return this;
	},
	debug:function(){
		// Create debug box
		var box = this.debugBox = new Element('div');
		box.setStyle({
			position		: 'fixed',
			bottom			: '10px',
			right			: (this.index*200+10)+'px',
			zIndex			: 1000,
			border			: '1px solid #000',
			backgroundColor	: '#FFF',
			color			: '#000',
			fontSize		: '11px',
			padding			: '1em'
		});
/*
		// [Fix IE6 and lower]
		var ieVersion = navigator.userAgent.match(/(?:msie\s)([\d]+)/i);
		ieVersion = ieVersion ? parseInt(ieVersion[1],10) : -1;
		
		if (Prototype.Browser.IE && ieVersion<=6){
			box.setStyle({
				position	: 'absolute',
				bottom		: '0',
				top			: 'expression((window.pageYOffset-150)+px);'
			});
		}
*/
		document.body.appendChild(box);
	
		setInterval(this.updateDebugBox.bind(this), 100);
	},
	updateMaximumScroll:function(){
		this.pageHeight		= (document.body.clientHeight || document.documentElement.clientHeight),
		this.clientHeight	= (window.innerHeight || document.documentElement.offsetHeight);
		this.maximumScroll	= this.pageHeight - this.clientHeight;
	},
	updateDebugBox:function(){
		this.debugBox.update((
			'<b style="display:block; background-color:#000; color:#FFF; text-align:center; width:150px;">Smoothscroll</b><br>'+
			'Index : '+this.index+'<br>'+
			'Scroll position : '+this.scrollPosition+'<br>'+
			'Maximum scroll : '+this.maximumScroll+'<br>'+
			'Current position : '+this.currentPosition+'<br>'+
			'Distance : '+this.distance+'<br>'+
			'Page height : '+this.pageHeight+'<br>'+
			'Client height : '+this.clientHeight+'<br>'+
			'Smoothness : '+this.smoothness+'<br>'+
			'Elements : '+this.elementsName+'<br>'+
			'Variables : '+(this.variables||'')+''
		));
	},
	
	initiateElement_relative:function(el){
		var top = parseInt(el.getStyle('top'), 10);
		el.setAttribute('original', top);
		el.style.position	= 'relative';
	},
	
	initiateElement_absolute:function(el){
		var top = parseInt(el.getStyle('top'), 10);
		el.setAttribute('original', top);
		el.style.position	= 'absolute';
	},
	
	initiateElement_margin:function(el){
		var top = parseInt(el.getStyle('marginTop'), 10);
		el.setAttribute('original', top);
	},
	
	initiateElement:function(el){
		// Force the "relative" position if none is specified
		if ('absolute relative'.indexOf(el.getStyle('position'))<=-1) el.setStyle({position:'relative'});
	},
	updateElement_top:function(distance, current, scroll, monitor){
		var y = parseInt(this.style['top'], 10) || 0;
		//monitor.variables = this.style.top;
		var yOriginal = parseInt(this.getAttribute('original'), 10) || 0;
		//this.style.top = y+distance+'px';
		this.style.top = yOriginal+current+'px';
	},
	
	updateElement_margin:function(distance, current, scroll, monitor){
		this.style.marginTop = current+'px';
	},
// ------------------------------------- events -------------------------------------//
	onScroll:function(){
		// Get the current real scroll position
		this.scrollPosition	= (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
		
		// Avoid the posibilities of scrolling outside of the current page
		if (this.scrollPosition > this.maximumScroll) this.scrollPosition = this.maximumScroll;

		this.distance = Math.floor((this.scrollPosition - this.currentPosition)*this.smoothness);
		this.currentPosition = this.currentPosition + this.distance;

		for (var i=0; i<this.elements.length; i++){
			var info 		= this.elements[i],
				distance	= this.distance,
				current		= this.currentPosition,
				scroll		= this.scrollPosition;
			
			if (info.wait>0){ current -= info.wait; if (current<0) current = 0; }
			if (info.distance>0) if (current>info.distance) current=info.distance;
			
			info.onScroll.bind(info.element)(distance, current, scroll, this, info);
		}
	},
	
// ------------------------------------- constructor -------------------------------------//
	initialize:function (options){
		this.updateMaximumScroll();
		
		// Reset those elements to be sure
		this.elements		= [];
		this.elementsName	= [];

		// The index is used to differenciate one instance from another
		this.index = SmoothScroller.NBR_CONTROLS++;
		
		setInterval(this.onScroll.bind(this), SmoothScroller.INTERVAL);

		// Update de maximumScroll constantly 
		setInterval(this.updateMaximumScroll.bind(this), 100); 
	}
};
