Effect.Scroll = Class.create(Effect.Base, {
	initialize: function(element)
	{
		this.element = $(element);
		if (!this.element) throw(Effect._elementDoesNotExistError);

		var options = Object.extend({
			shift: 100,
			type: 'left'
		}, arguments[1] || { });
		this.start(options);
	},
	setup: function()
	{
		if (this.options.type == 'left')
			this.originalScrollLeft = this.element.scrollLeft;
		else
			this.originalScrollTop = this.element.scrollTop;
	},
	update: function(position) {
		if (this.options.type == 'left')
			this.element.scrollLeft = this.originalScrollLeft + (this.options.shift * position).round();
		else
			this.element.scrollTop = this.originalScrollTop + (this.options.shift * position).round();
	}
});

var Slider = Class.create();

Slider.prototype = {
	initialize: function()
	{
		this.options = Object.extend({
			id: 'slider',
			autoStart: false,
			effectDuration: 0.5,
			frameDelay: 5,
			direction: 'left',
			doNotPause: false
		}, arguments[0] || { });

		// No object found
		if (!$(this.options.id)) return;

		// Flags
		this.sliding = false;
		this.playing = false;

		// periodical executer object
		this.pe = null;

		// Window area
		this.window = $(this.options.id);
		this.windowWidth = this.window.getWidth();
		this.windowHeight = this.window.getHeight();

		// Content area
		this.content = $(this.window.down(0));
		var elements = this.content.childElements();

		// No objects inside content found
		if (elements.length == 0) return;

		// Check that all elements are equal in sizes
		var elementWidth = parseInt(elements[0].getWidth());
		var elementHeight = parseInt(elements[0].getHeight());
		for (var i = 0; i < elements.length; i++)
		{
			if (elements[i].getWidth() != elementWidth)
			{
				alert('Width of all elements must be equal');
				return;
			}
			if (elements[i].getHeight() != elementHeight)
			{
				alert('Height of all elements must be equal');
				return;
			}
		}
		
		// Determine content size and frame slide size
		if (this.options.direction == 'up' || this.options.direction == 'down')
		{
			if (this.windowHeight % elementHeight > 0)
			{
				alert('Window height must be divisible by element height');
				return;
			}
			this.shift = elementHeight;
			this.contentWidth = this.windowWidth;
			this.contentHeight = parseInt(elements.length*elementHeight);

			// Content height is less than window height. No slide is needed
			if (this.contentHeight <= this.windowHeight) return;

			var tail = this.windowHeight/elementHeight;
			this.contentHeight = this.contentHeight + parseInt(tail*elementHeight);
		}
		else
		{
			if (this.windowWidth % elementWidth > 0)
			{
				alert('Window width must be divisible by element width');
				return;
			}
			this.shift = elementWidth;
			this.contentWidth = parseInt(elements.length*elementWidth);
			this.contentHeight = this.windowHeight;

			// Content width is less than window window. No slide is needed
			if (this.contentWidth <= this.windowWidth) return;

			var tail = this.windowWidth/elementWidth;
			this.contentWidth = this.contentWidth + parseInt(tail*elementWidth);
		}

		this.elementsCount = elements.length;

		for (var i = 0; i < tail; i++)
		{
			this.content.appendChild(elements[i].cloneNode(true));
		}

		// Set style for content area
		this.content.setStyle({width: this.contentWidth+'px', height: this.contentHeight+'px'});

		// Define previous and next buttons
		this.prevBtn = $(this.options.id+'-prev');
		this.nextBtn = $(this.options.id+'-next');
		
		if (this.prevBtn)
		{
			this.prevBtn.observe('click', (function(event)
			{
				event.stop();
				if (this.playing && !this.options.doNotPause) this.playPause();
				this.prev();
			}).bindAsEventListener(this));
		}
		if (this.nextBtn)
		{
			this.nextBtn.observe('click', (function(event)
			{
				event.stop();
				if (this.playing && !this.options.doNotPause) this.playPause();
				this.next();
			}).bindAsEventListener(this));
		}

		// Define paging
		if ($(this.options.id+'-paging') && $(this.options.id+'-paging').childElements().length > 0)
		{
			this.paging = $(this.options.id+'-paging');
			$(this.options.id+'-paging').childElements()[0].addClassName('active');
			for (var i = 0; i < $(this.options.id+'-paging').childElements().length; i++)
			{
				$(this.options.id+'-paging').childElements()[i].id = 'e'+(i+1);
				$(this.options.id+'-paging').childElements()[i].observe('click', (function(event)
				{
					var target = event.findElement('a');
					if (target)
					{
						event.stop();
						this.page(target.id.substr(1));
					}
				}).bindAsEventListener(this));
			}
		}

		// Start playing
		if (this.options.autoStart)
		{
			this.playing = true;
			this.pe = new PeriodicalExecuter(this.loop.bind(this), this.options.frameDelay);
		}

		// Define play/pause button
		if ($(this.options.id+'-play-pause'))
		{
			$(this.options.id+'-play-pause').observe('click', (function(event) { event.stop(); this.playPause(); }).bindAsEventListener(this));
			if (this.playing)
				$(this.options.id+'-play-pause').className = 'play';
			else
				$(this.options.id+'-play-pause').className = 'pause';
		}
	},

	next: function()
	{
		if (this.options.direction == 'up' || this.options.direction == 'down')
		{
			if (this.window.scrollTop >= this.contentHeight - this.windowHeight)
			{
				this.window.scrollTop = 0;
			}
		}
		else
		{
			if (this.window.scrollLeft >= this.contentWidth - this.windowWidth)
			{
				this.window.scrollLeft = 0;
			}
		}

		this.slideTo(this.shift);
	},

	prev: function()
	{
		if (this.options.direction == 'up' || this.options.direction == 'down')
		{
			if (this.window.scrollTop == 0)
			{
				this.window.scrollTop = this.contentHeight - this.windowHeight;
			}
		}
		else
		{
			if (this.window.scrollLeft == 0)
			{
				this.window.scrollLeft = this.contentWidth - this.windowWidth;
			}
		}

		this.slideTo(-this.shift);
	},

	page: function(position)
	{
		if (this.options.direction == 'up' || this.options.direction == 'down')
		{
			if (this.window.scrollTop >= this.contentHeight - this.windowHeight)
			{
				this.window.scrollTop = 0;
			}
		}
		else
		{
			if (this.window.scrollLeft >= this.contentWidth - this.windowWidth)
			{
				this.window.scrollLeft = 0;
			}
		}

		if (this.options.direction == 'up' || this.options.direction == 'down')
			var from = this.window.scrollTop;
		else
			var from = this.window.scrollLeft;

		this.slideTo((position - 1)*this.shift - from);
	},

	slideTo: function(shift)
	{
		if (this.sliding) return;
		this.sliding = true;
		this.stop();
		new Effect.Scroll(this.window, {
			duration: this.options.effectDuration,
			shift: shift,
			type: (this.options.direction == 'left' || this.options.direction == 'right' ? 'left' : 'top'),
			afterFinish: (function()
			{
				this.sliding = false;
				if (this.options.frameDelay > 0 && this.playing)
				{
					this.pe = new PeriodicalExecuter(this.loop.bind(this), this.options.frameDelay);
				}
				if (this.paging)
				{
					if (this.options.direction == 'up' || this.options.direction == 'down')
						var selected = this.window.scrollTop/this.shift;
					else
						var selected = this.window.scrollLeft/this.shift;

					if (selected > this.elementsCount -1)
						selected = 0;

					for (var i = 0; i < this.paging.childElements().length; i++)
					{
						if (i == selected)
							this.paging.childElements()[i].addClassName('active');
						else
							this.paging.childElements()[i].removeClassName('active');
					}
				}
			}).bind(this)
		});
	},

	stop: function()
	{
		if (this.pe) this.pe.stop();
	},

	playPause: function()
	{
		if (this.playing)
		{
			if ($(this.options.id+'-play-pause')) $(this.options.id+'-play-pause').className = 'pause';
			this.playing = false;
			this.stop();
		}
		else
		{
			if ($(this.options.id+'-play-pause')) $(this.options.id+'-play-pause').className = 'play';
			this.playing = true;
			this.loop();
		}
	},

	loop: function()
	{
		if (this.options.direction == 'left' || this.options.direction == 'up')
			this.next();
		else
			this.prev();
	}
}

