var ScrollBar = Class.create();
ScrollBar.prototype = {

	initialize: function (element, options) {
		this.options = Object.extend({
			min: 0,
			max: 100,
			width: 20,
			onScroll: function () {}
		}, options || {});
		
		this.element = $(element);
		this.element.addClassName('scrollbar');
		
		this.createElements();
		this.setScale(this.options.min, this.options.max, this.options.width);
		this.setPosition(0);
		this.updateElements();
	},
	
	createElements: function () {
		this.container = $(document.createElement('div'));
		this.element.appendChild(this.container);
		this.positionElement = $(document.createElement('div'));
		var styledPositionElement = document.createElement('div');
		$(styledPositionElement).addClassName('position');
		this.positionElement.appendChild(styledPositionElement);
		this.container.appendChild(this.positionElement);
		
		Event.observe(this.positionElement, 'mousedown', this.startDrag.bindAsEventListener(this));
	},
	
	setScale: function (min, max, width) {
		this.min = min;
		this.max = max;
		this.range = this.max - this.min;
		this.width = width;
		this.positionWidth = parseInt(this.width / this.range * this.container.getWidth());
		this.maxPosition = (this.container.getWidth() - this.positionWidth)
		this.left = 0;
		this.updateElements();
	},
	
	setPosition: function (position) {
		if (position < this.min)
			position = this.min;
		if (position > this.max)
			position = this.max;
		this.position = position;
		this.left = parseInt(this.position / this.range * this.maxPosition);
		this.updateElements();
	},
	
	userSetPosition: function (position) {
		if (position < 0)
			position = 0;
		if (position > this.maxPosition)
			position = this.maxPosition;
			
		this.left = position;
		this.position = parseInt(this.left / this.maxPosition * this.range + this.min);
		this.updateElements();
		this.options.onScroll(this.position, this);
	},
	
	updateElements: function () {
		this.positionElement.style.width = this.positionWidth + 'px';
		this.positionElement.style.marginLeft = this.left + 'px';
	},
	
	startDrag: function (e) {
		this.dragging = true;
		Event.stop(e);
		this.pointerOffset = Event.pointerX(e) - this.left;
		this.endDragListener = this.endDrag.bindAsEventListener(this);
		Event.observe(document, 'mouseup', this.endDragListener);
		this.continueDragListener = this.continueDrag.bindAsEventListener(this);
		Event.observe(document, 'mousemove', this.continueDragListener);
	},
	
	continueDrag: function (e) {
		Event.stop(e);
		var currentX = Event.pointerX(e);
		this.userSetPosition(currentX - this.pointerOffset);
	},
	
	endDrag: function (e) {
		Event.stop(e);
		Event.stopObserving(document, 'mousemove', this.continueDragListener);
		Event.stopObserving(document, 'mouseup', this.endDragListener);
		this.dragging = false;
	}

};

var Scroller = Class.create();
Scroller.prototype = {

	initialize: function (element, options) {
		this.options = Object.extend({
			delay:	25,
			followMouse: true,
			onSelect: function () {}
		}, options || {});
		
		this.container = $(element);
		this.container.setStyle({
			overflow: 'hidden'
		});

		if (!this.options.leftButton)
			this.options.leftButton = this.container.id + '_left';
		this.leftButton = $(this.options.leftButton);
		if (this.leftButton) {
			Event.observe(this.leftButton, 'mousedown', Event.stop);
			Event.observe(this.leftButton, 'click', this.pageScroll.bindAsEventListener(this, -1));
		} 
		
		if (!this.options.rightButton)
			this.options.rightButton = this.container.id + '_right';
		this.rightButton = $(this.options.rightButton);
		if (this.rightButton) {
			Event.observe(this.rightButton, 'mousedown', Event.stop);
			Event.observe(this.rightButton, 'click', this.pageScroll.bindAsEventListener(this, 1));
		} 
		
		this.list = this.container.down('ul');
		this.list.setStyle({
			overflow:	'hidden',
			listStyle:	'none',
			padding:	'0px',
			margin:		'0px',
			width:		'10000px' // is there a way to dynamically do this?
		});
		
		this.itemCount = 0;
		this.maxOffset = 0;
		this.itemIds = {};
		this.offset = new NumberSlider();
		
		this.items().each(this.prepareItem.bind(this));
		Event.observe(window, 'load', this.activate.bind(this));
	},
	
	prepareItem: function (item) {
		if (!item.id)
			item.id = this.container.id + '_item_' + (this.itemCount + 1);
		this.itemIds[this.itemCount] = item.id;
		item.scrollerIndex = this.itemCount;
		Event.observe(item, 'click', this.itemClicked.bindAsEventListener(this, this.itemCount)); 
		this.itemCount++;
	},
	
	activate: function () {
		var totalItemWidth = this.items().inject(0, function (sum, item) {
			return sum + item.getWidth();
		});
		
		this.maxOffset = totalItemWidth - this.container.getWidth();

		this.selectIndex(0);

		var scrollBarElement;
		if (!this.options.scrollBar)
			this.options.scrollBar = this.container.id + '_scrollbar';
		scrollBarElement = $(this.options.scrollBar);
		if (scrollBarElement)
			this.scrollbar = new ScrollBar(scrollBarElement, {
				max: this.maxOffset,
				width: this.container.getWidth(),
				onScroll: this.userScroll.bind(this)
			});

		if (this.options.autoNext) {
			this.options.slideShowDelay = this.options.autoNext;
			this.startSlideShow();
		}

		if (this.options.followMouse) {
			Event.observe(this.container, 'mousemove', this.followMouse.bindAsEventListener(this));
			Event.observe(this.container, 'mouseout', this.endFollowMouse.bindAsEventListener(this));
		}
	},
	
	items: function () {
		return $A(this.list.getElementsByTagName('li'));
	},
	
	itemClicked: function (e, index) {
		Event.stop(e);
		this.selectIndex(index);		
	},
	
	selectIndex: function (index) {
		var item = $(this.itemIds[index]);
		this.selectItem(item);
	},
	
	selectItem: function (item) {
		if (this.selectedItem)
			this.selectedItem.removeClassName('selected');
		item.addClassName('selected');
		this.selectedItem = item;
		this.scrollToItem(item);
		this.options.onSelect(item.scrollerIndex, item);
	},
	
	scrollToItem: function (item) {
		var itemOffset = Position.cumulativeOffset(item)[0] - Position.cumulativeOffset(this.list)[0];
		var containerWidth = this.container.getWidth();
		var itemWidth = item.getWidth();
		this.scrollToOffset(parseInt(itemOffset - ((containerWidth - itemWidth) / 2)));
	},
	
	scrollToOffset: function (offset) {
		this.stopSlideShow();
		if (offset < 0)
			offset = 0;
		if (offset > this.maxOffset)
			offset = this.maxOffset;
		this.offset.slideTo(offset);
		this.scroll();
	},
	
	scrollBy: function (change) {
		this.scrollToOffset(this.offset.target + change);
	},
	
	scroll: function () {
		this.offset.step();
		this.setOffset(this.offset.value);
		if (this.offset.moving)
			setTimeout(this.scroll.bind(this), this.options.delay);
	},
	
	setOffset: function (offset) {
		this.list.style.marginLeft = -offset + 'px';
		if (this.scrollbar) 
			this.scrollbar.setPosition(offset);
	},
	
	userScroll: function () {
		this.stopSlideShow();
		this.offset.jumpTo(this.scrollbar.position);
		this.setOffset(this.scrollbar.position);
	},
	
	startSlideShow: function () {
		if (!this.slideShowTimer) {
			this.slideShowTimer = setTimeout(this.slideShowNext.bind(this), this.options.slideShowDelay);
		}
	},
	
	stopSlideShow: function () {
		if (this.slideShowTimer)
			clearTimeout(this.slideShowTimer);
	},
	
	slideShowNext: function () {
		this.slideShowTimer = null;
		this.selectNext();
		this.startSlideShow();
	},
	
	selectNext: function () {
		var index = this.selectedItem.scrollerIndex + 1;
		if (index >= this.itemCount)
			index = 0;
		this.selectIndex(index);
	},
	
	followMouse: function (e) {
		if (!(this.scrollbar && this.scrollbar.dragging) && Position.within(this.container, Event.pointerX(e), Event.pointerY(e))) {
			var overlap = Position.overlap('horizontal', this.container);
			var width = this.container.getWidth();
			var center = parseInt(width / 2);
			var difference = overlap * width - center;
			this.autoScrollBy = -parseInt(difference / 3);
			if (Math.abs(this.autoScrollBy) < 5)
				this.autoScrollBy = 0;
			if (!this.autoScrollTimer)
				this.autoScroll();
		}
	},
	
	autoScroll: function () {
		this.scrollBy(this.autoScrollBy);
		this.autoScrollTimer = setTimeout(this.autoScroll.bind(this), 50);
	},
	
	endFollowMouse: function (e) {
		if (!Position.within(this.container, Event.pointerX(e), Event.pointerY(e))) {
			if (this.autoScrollTimer) {
				clearTimeout(this.autoScrollTimer);
				this.autoScrollTimer = null;
			}
		}
	},
	
	pageScroll: function (e, sign) {
		Event.stop(e);
		var pageSize = this.container.getWidth();
		this.scrollBy(pageSize * sign);
	}

};