SmoothScroller = function(e, configParams) {
var defaultConfig = {
	height: 	240,
	prevButton:	null,
	nextButton:	null,
	clipArea:	null, 
	auto:		false,
	pxps:	1000,
	pxpsauto:	30
};

this.config = $.extend({}, defaultConfig, configParams || {});

this.ul = $('ul', this.config.clipArea);
this.ul.css({top:'0px'});

this.animrunning = false;
var scrollerObj = this;
this.config.prevButton.bind('click', function() {
	scrollerObj.ul.stop();
	scrollerObj.animrunning = false;
	clearInterval(scrollerObj.autoAnimInt);
	clearInterval(scrollerObj.animInt);
	
	scrollerObj.start(scrollerObj, 'up');
	/*
	scrollerObj.animInt = setInterval(function() {
			scrollerObj.start(scrollerObj, 'up');
	} , 10);*/
});
this.config.nextButton.bind('click', function() {
	scrollerObj.ul.stop();
	scrollerObj.animrunning = false;
	clearInterval(scrollerObj.autoAnimInt);
	clearInterval(scrollerObj.animInt);
	
	scrollerObj.start(scrollerObj, 'down');
	/*
	scrollerObj.animInt = setInterval(function() {
			scrollerObj.start(scrollerObj, 'down');
	} , 10);*/
});
/*					this.config.prevButton.bind('mouseout', function() {
	scrollerObj.ul.stop();
	scrollerObj.animrunning = false;
	clearInterval(scrollerObj.autoAnimInt);
	clearInterval(scrollerObj.animInt);
});
this.config.nextButton.bind('mouseout', function() {
	scrollerObj.ul.stop();
	scrollerObj.animrunning = false;
	clearInterval(scrollerObj.autoAnimInt);
	clearInterval(scrollerObj.animInt);
});*/

e.bind('mouseenter', function() {
	scrollerObj.ul.stop();
	scrollerObj.animrunning = false;
	clearInterval(scrollerObj.autoAnimInt);
	clearInterval(scrollerObj.animInt);
});
e.bind('mouseleave', function() {
	scrollerObj.ul.stop();
	scrollerObj.animrunning = false;
	clearInterval(scrollerObj.autoAnimInt);
	clearInterval(scrollerObj.animInt);
	scrollerObj.autoAnimInt = setInterval(function() {
			scrollerObj.start(scrollerObj, 'down', true);
		} , 250);
});

if(this.config.auto == true) {
	scrollerObj.ul.stop();
	scrollerObj.animrunning = false;
	clearInterval(scrollerObj.autoAnimInt);
	clearInterval(scrollerObj.animInt);
	scrollerObj.autoAnimInt = setInterval(function() {
			scrollerObj.start(scrollerObj, 'down', true);
		} , 250);
}
};

SmoothScroller.prototype.start = function(scrollerObj, direction, auto) {
if(scrollerObj.animrunning == false) {
	scrollerObj.animrunning = true;
	var topval = scrollerObj.ul.css('top');
	topval = parseInt(topval.substring(0, topval.length));
	
	var millisec = 1000;
	var ease = 'linear';
	
	if(direction == 'up') {
		if(auto == true) {
			topval += scrollerObj.config.pxpsauto;
		}
		else {
			millisec = Math.floor(scrollerObj.config.height / scrollerObj.config.pxps * 1000);
			ease = 'swing';
			topval += scrollerObj.config.pxps;
		}
		topval = Math.min(topval,0);
	} 
	else if(direction == 'down') {
		var following = scrollerObj.ul.outerHeight()-(Math.abs(topval)+scrollerObj.config.clipArea.outerHeight());
		var dy = Math.max(following, 0);
		if(auto == true) {
			dy = Math.min(scrollerObj.config.pxpsauto, dy);
			if(following<scrollerObj.config.height) {
				var firstElement = $('li:first', scrollerObj.ul).remove();
				firstElement.appendTo(scrollerObj.ul);
				
				topval += firstElement.outerHeight(true);
				scrollerObj.ul.css({top:topval+'px'});
				
				following = scrollerObj.ul.outerHeight()-(Math.abs(topval)+scrollerObj.config.clipArea.outerHeight());
				dy = Math.max(following, 0);
				dy = Math.min(scrollerObj.config.pxpsauto, dy);
			}
		}
		else {
			millisec = Math.floor(scrollerObj.config.height / scrollerObj.config.pxps * 1000);
			ease = 'swing';
			dy = Math.min(scrollerObj.config.height, dy);
		}
		topval -= dy;
		/*
		if(dy==0 && auto == true) {
			millisec = 300;
			ease = 'swing';
			topval = 0;
		}*/
	}
	
	scrollerObj.ul.animate({top: topval+'px'}, millisec, ease, function() {
		scrollerObj.animrunning = false;
	});
}
};

/** constructor */
SmoothScroller2 = function(e, configParams) {
	//1. set config stuff
	var defaultConfig = {
		prevButton:	null,
		nextButton:	null,
		clipArea:	null, 
		auto:	false,
		pageScrollTime: 300,
		pageScrollEase: 'swing',
		autoScrollTimeInterval: 60,
		autoScrollDelta: 1
	};
	
	this.config = $.extend({}, defaultConfig, configParams || {});
	
	//2. init, calulate/get static values and store them
	this.pageScrollRunning = false;
	this.fwddirection = true;
	this.value = 0;
	this.ul = $('ul', this.config.clipArea);
	this.ul.css({top:'0px'});	
	this.viewportHeight = this.config.clipArea.outerHeight();
	
	//original dimension
	this.oheight = this.ul.outerHeight(true);
	
	//4. make some element clones to prepare the continous scrolling due to the viewport's height
	var heightSum = 0;
	var n=0;
	for( var li = $('li:first', this.ul) ; li && heightSum<this.viewportHeight*2 ; li=li.next() ) {
		n++;
		heightSum += li.outerHeight(true);
		li.clone().appendTo(this.ul);
	}
	//final height
	this.ulHeight = this.ul.outerHeight(true);
	this.addedHeight = this.ulHeight-this.oheight;
					
	var scrollerObj = this;
	this.config.prevButton.bind('click', function() {
		scrollerObj.scrollPage(false);
	});
	this.config.nextButton.bind('click', function() {
		scrollerObj.scrollPage(true);
	});
	e.bind('mouseenter', function() {
		scrollerObj.stopAutoScroll();
	});
	e.bind('mouseleave', function() {
		scrollerObj.startAutoScroll();
	});
	
	if(this.config.auto) {
		this.startAutoScroll();
	}
};

/** moves smoothly and continously with setInterval */
SmoothScroller2.prototype.autoScroll = function(scrollerObj, fwddirection) {
	//do the job
	scrollerObj.value = scrollerObj.getNextValue(fwddirection, scrollerObj.config.autoScrollDelta);
	scrollerObj.ul.css('top', scrollerObj.value); 
};

SmoothScroller2.prototype.startAutoScroll = function() {
	//1. stops all jQuery animation
	this.stopPageScroll();
	
	//2. stops autoScroll animation
	this.stopAutoScroll(); 
	
	//3. starts the loop
	var scrollerObj = this;
	scrollerObj.autoAnimInt = setInterval(function() {
			scrollerObj.autoScroll(scrollerObj, true);
		} , this.config.autoScrollTimeInterval);
};

/** kills the autoScroll animation with clearInterval and refresh the current value */
SmoothScroller2.prototype.stopAutoScroll = function() {
	//update the value from actual position
	var cssValue = this.ul.css('top'); 
	this.value = parseInt(cssValue.substring(0, cssValue.length))
	clearInterval(this.autoAnimInt);
};

/** moves 1 page amount with the jQuery animate function */
SmoothScroller2.prototype.scrollPage = function(fwddirection) {
	if(!this.pageScrollRunning) {
		//XXX disable buttons
		this.pageScrollRunning = true;
		this.stopAutoScroll();
	
		//2. do the job 
		this.value = this.getNextValue(fwddirection, this.viewportHeight);
		var thisObj = this;
		this.ul.animate({top: this.value+'px'}, this.config.pageScrollTime, this.config.pageScrollEase, function() {
				thisObj.pageScrollRunning = false;
				//XXX enable buttons
			});
	}
};

SmoothScroller2.prototype.stopPageScroll = function() {
	//update the value from actual position
	var cssValue = this.ul.css('top'); 
	this.value = parseInt(cssValue.substring(0, cssValue.length))
	this.ul.stop();
	this.pageScrollRunning = false;
};

/*
TODO: 
	- need to set on init: none
	- need to set in config:
		
TODO: 	- use packed jQuery
		- use packed scroller

XXX: - horizontal version
	- wire button elements from config
*/

/** check if animated element's border will reach the viewport (clipping area)  */
SmoothScroller2.prototype.isEndComing = function(fwddirection, delta) {
	return fwddirection ? ((-this.value) > this.oheight) : ((-this.value+delta) < this.addedHeight);
};

/** returns the next animation value also SETS ANIMATION POSITION (if needed)  */
SmoothScroller2.prototype.getNextValue = function(fwddirection, delta) {
	if(this.isEndComing(fwddirection, delta)) {
		//jumping to a new position (calulated from the original height)
		this.value = fwddirection ? this.value+this.oheight : this.value-this.oheight;
		this.ul.css('top', this.value);
	}
	return fwddirection ? (this.value-delta) : (this.value+delta);
};
