if(typeof Prototype == 'undefined')
	throw 'you need Prototype for this LIB';

//const TICKER_DEBUG = true;
var TICKER_DEBUG = true;

var Ticker = Class.create({
	options: {},
	mousePosition: null,
	internal: {},
	ready: false,
	i: false,
	events: {},
	initialize: function(element, options, startup) {
		var self = this;
		
		//get element
		if(element.hide)
			this.element = element;
		else {
			element = $(element);
			if(!element)
				throw 'ticker element-node not found!';
			this.element = element;
		}
		
		//init events
		if(options && options.events) {
			this.events = options.events;
			delete options.events;
		}
		
		this.fireEvent('beforInit');
		
		if(typeof startup == 'function') {
			this.config = {};
			this.config.options = options;
			this.config.element = element;
			startup.call(this);
			options = this.config.options;
			element = this.config.element;
			delete this.configs;
		}
		
		//init options
		this.options = (function() {
			var o = Object.extend({
				from: 'right',
				onhover: 'toggle',
				drag: true,
				bindonmouse: false,
				pp: false,
				speed: 100,
				append: 8,
				debug: TICKER_DEBUG,
				delta: 1,
				scroll: false
			}, options);
			
			TICKER_DEBUG = (o.debug == true);
			
			switch(o.onhover.toLowerCase()) {
				case 'stop':
				case 'toggle':
					break;
				case 'bind':
					if(o.bindonmouse === true)
						break;
				default: o.onhover = 'none';
			}
			
			o.bind = (o.bind === true);
			o.pp = (o.pp === true);
			o.drag = (o.drag === true && o.onhover != 'none');
			o.from = o.from.toLowerCase();
			
			self.onDragCursor = 'e-resize';
			switch(o.from) {
				case 'left':
					o.to = 'right';
					break;
				case 'right':
					o.to = 'left';
					break;
				case 'top':
					o.to = 'bottom';
					self.onDragCursor = 'n-resize';
					break;
				case 'bottom':
					o.to = 'top';
					self.onDragCursor = 'n-resize';
					break;
				case 'top_left':
				case 'bottom_left':
				case 'top_right':
				case 'bottom_right':
					self.onDragCursor = 'move';
					break;
				default: o.from = 'right';
			}
			
			return o;
		})();
		
		if(this.options.bindonmouse === true) {	
			this.element.style.position = 'absolute';
			this.eventBind = this.mouseBinder.bindAsEventListener(this);
			document.observe('mousemove', this.eventBind);
			
			//doubleclick will stop binding element on mousepointer
			document.observe('dblclick', function(event) {
				self.detachMouse(event);
			});
		} //else 
		
		//okay, we can handle "drag" and "bindonmouse" at the same time
		if(this.options.drag === true && this.options.onhover != 'bind') {
			this.eventDrag = this.textDrag.bindAsEventListener(this);
			
			//init event "drag"
			this.element.onmousedown = function(event) {
				var event = event || window.event;
				if(!Event.isLeftClick(event))
					return true;
					
				var target = Event.element(event);
				if(target.nodeName == 'IMG')
					Event.stop(event); //we dont want to drag the img-element
					
				self.initializeTextDrag();
				document.observe('mousemove', self.eventDrag);
			};
			
			this.element.onmouseup = function() {
				document.stopObserving('mousemove', self.eventDrag);
				self.uninitializeTextDrag();
			}
		}
		
		//set events on element
		if(this.options.onhover == 'bind') {
			//bind element on mouse					
			this.element.observe('mouseover', this.detachMouse.bindAsEventListener(this));
		} 
		
		//ticker will stop on mouseover
		else if(this.options.onhover == 'stop')
			this.element.observe('mouseover', this.stop.bindAsEventListener(this));
			
		//ticker will stop and go on mouseover/out (default)
		else if(this.options.onhover != 'none') {
			this.element.observe('mouseover', this.stop.bindAsEventListener(this));
			this.element.observe('mouseout', this.resume.bindAsEventListener(this));
		}
		
		//scrolling will move contents left/right
		if(this.options.scroll !== false) {
			this.wheelEvent = this.mouseWheelHandler.bindAsEventListener(this);
			if(Prototype.Browser.Gecko) {
				//gecko (spidermonkey)
				Event.observe(window, 'DOMMouseScroll', this.wheelEvent);
			} else {
				//IE/Opera
				window.onmousewheel = document.onmousewheel = this.wheelEvent;
			}
		}
		
		if(TICKER_DEBUG && !$('debug'))
			document.body.appendChild(new Element('span', { id: 'debug' }));
		
		this.fireEvent('afterInit');
		this.start();
	},
	
	fireEvent: function(event) {
		if(typeof this.events[event] == 'function')
			this.events[event](this);
	},
	
	mouseWheelHandler: function(event) {
		//dom event IE?
		if(!event) event = window.event;
		
		//first, we have to check the target.
		if(Event.element(event) != this.element) {
			return true;
		} else {
			var delta = 0;
			//get wheel data
			if(event.wheelDelta) {
				//IE/Opera
				delta = event.wheelDelta / 120;
				delta = -delta;
			} else if(Prototype.Browser.Gecko) {
				delta = event.detail / 3;
			}
			
			event.delta = delta;
			this.mouseHandleDelta(event);
			Event.stop(event);
		}
	},
	
	mouseHandleDelta: function(event) {
		//remove focus from ticker
		document.body.focus();
		
		if(['left', 'right'].indexOf(this.options.from) > -1) {
			if(event.delta < 0) { //goto left
				var left = parseInt(this.ticker.style.left);
				left += this.options.append * this.options.delta;
				if(left > this.element.getWidth())
					left = (0 - this.ticker.getWidth());
			} else { //goto right
				var left = parseInt(this.ticker.style.left);
				left -= this.options.append * this.options.delta;
				if(left <= (0 - this.ticker.getWidth()))
					left = this.element.getWidth();
			}
			
			this.ticker.style.left = left + 'px';
		} else {
			if(event.delta < 0) { //go down 
				var top = parseInt(this.ticker.style.top);
				top += this.options.append * this.options.delta;
				if(top > this.element.getHeight())
					top = (0 - this.ticker.getHeight());
			} else {
				var top = parseInt(this.ticker.style.top);
				top -= this.options.append * this.options.delta;
				if(top <= (0 - this.ticker.getHeight()))
					top = this.ticker.getHeight();
			}
			
			this.ticker.style.top = top + 'px';
		}
	},
	
	start: function() {
		if(this.ready !== true) {
			this.element.style.overflow = 'hidden'; //overflow will be hidden
			this.element.style.whiteSpace = 'nowrap'; //ignore overflow-breaks
			this.element.style.width = ((this.element.style.width != '')
				? this.element.style.width
				: (this.element.getWidth() - 2) + 'px'
			);
			
			//ie only hide childnodes in the "hidden-overflow" of the parent-element 
			//when his position is "relative" or "absolute" (with z-index > 0)
			if(Prototype.Browser.IE) 
				this.element.style.position = 'relative';
				
			var span = new Element('span');
			span.update(/* we need al contents */ this.element.innerHTML);
			
			//delete contents
			this.element.innerHTML = '';	

			//set span position to "relative"
			span.style.position = 'relative';	

			//update "left" and "top" in inline-css stuff
			span.style.top = 0;
			span.style.left = 0;
			
			//insert element into "parent"
			this.element.update(span);
			
			//move text out of parent-element
			if(this.options.from == 'left')
				span.style.left = this.element.getWidth() + 'px';
			else if(this.options.from == 'right')
				span.style.left = (0 - span.getWidth()) + 'px';
				
			//define ticker element
			this.ticker = span;
			this.ready = true;
		}
		
		this.setInterval();
		this.fireEvent('afterStart');
	},
	
	stop: function(event) {
		if(this.i == false)
			return false;
			
		clearInterval(this.i);
		this.i = false;
	},
	
	resume: function(event) {
		if(this.i !== false)
			return false;
			
		this.setInterval();
	},
	
	setInterval : function() {
		var self = this;
		
		//function ready?
		if(!this.intervalFunction) {
			if(['left', 'right'].indexOf(this.options.from) > -1) {
				this.intervalFunction = function(dir) {
					if(self.moveText_lr(dir, self.options.append) == false) {
						if(self.options.pp === true) {
							clearInterval(self.i);
							self.options.from = self.options.to;
							self.options.to = dir;
							self.setInterval();
						} else self.moveText_lr(dir, true);
					}
				}
			} else if(['top', 'bottom'].indexOf(this.options.from) > -1) {
				this.intervalFunction = function(dir) {
					if(self.moveText_tb(dir, self.options.append) == false) {
						if(self.options.pp === true) {
							clearInterval(self.i);
							self.options.from = self.options.to;
							self.options.to = dir;
							self.setInterval();
						} else self.moveText_tb(dir, true);
					}
				}
			}
		}
		
		this.i = setInterval(function() { self.intervalFunction(self.options.from); }, this.options.speed);
	},
	
	moveText_lr: function(dir, p) {
		if(p === true) {
			this.ticker.style.left = ((dir == 'left') 
				? this.element.getWidth() + 'px'
				: (0 - this.ticker.getWidth()) + 'px'
			);
			
			return;
		}
		
		if(dir == 'left') { //text moves to right
			var newLeft = parseInt(this.ticker.style.left) - p;
			if((newLeft + p) < (0 - this.ticker.getWidth()))
				return false;
		} else { //if(dir == 'right')
			var newLeft = parseInt(this.ticker.style.left) + p;
			if(newLeft > (this.element.getWidth() + p))
				return false;
		}
		
		this.ticker.style.left = newLeft + 'px';
		if(TICKER_DEBUG) $('debug').update('left:' + newLeft + 'px');
	},
	
	moveText_tb: function(dir, p) {
		if(p === true) {
			this.ticker.style.top = ((dir == 'top') 
				? (0 - this.ticker.getHeight()) + 'px'
				: this.ticker.getHeight() + 'px'
			);
			
			return;
		}
		
		if(dir == 'top') { //text moves to bottom
			var newTop = parseInt(this.ticker.style.top) + p;
			if(newTop > this.ticker.getHeight())
				return false;
		} else { //if(dir == 'bottom')
			var newTop = parseInt(this.ticker.style.top) - p;
			if(newTop < (0 - this.ticker.getHeight()))
				return false;
		}
		
		this.ticker.style.top = newTop + 'px';
		if(TICKER_DEBUG) $('debug').update('top:' + newTop + 'px');
	},
	
	detachMouse: function(event) {
		if(this.element.style.position == 'absolute') {
			document.stopObserving('mousemove', this.eventBind);
			this.element.style.top = '';
			this.element.style.left = '';
			this.element.style.position = '';
		} else {
			this.element.style.position = 'absolute';
			this.mouseBinder(event);
			document.observe('mousemove', this.eventBind);
		}
	},
	
	mouseBinder: function(event) {	
		this.element.style.top = (Event.pointerY(event) + 10) + 'px';
		this.element.style.left = (Event.pointerX(event) + 15) + 'px';
	},
	
	initializeTextDrag: function() {
		this.element.style.cursor = this.onDragCursor;
	},
	
	uninitializeTextDrag: function() {
		this.mousePosition = null;
		this.element.style.cursor = 'auto';
	},
	
	textDrag: function(event) {
		if(['left', 'right'].indexOf(this.options.from ) > -1)
			this.dragText_lr(event);
		else if(['top', 'bottom'].indexOf(this.options.from) > -1)
			this.dragText_tb(event);
		else this.dragText_all(event);
		
		this.saveMousePosition(event);
	},
	
	saveMousePosition: function(event) {
		this.mousePosition = {
			left: Event.pointerX(event),
			top: Event.pointerY(event)
		}
		
		return false;
	},
	
	elementInternalForceGet: function() {
		var returnVal = this.element;
		$A(arguments).each(function(item) {
			if(returnVal[item])
				returnVal = returnVal[item];
		});
		
		return returnVal;
	},
	
	//see elementInternalForceGet: cache stuff
	elementInternalGet: function() {		
		var key = $A(arguments).toString();
		if(typeof this.internal[key] != 'undefined')
			return this.internal[key];
			
		var returnVal = this.element;
		$A(arguments).each(function(item) {
			if(returnVal[item])
				returnVal = returnVal[item];
		});
		
		this.internal[key] = returnVal;
		return returnVal;
	},
	
	dragText_all: function(event) {				
		this.dragText_lr(event);
		this.dragText_tb(event);
	},
	
	dragText_tb: function(event) {
		if(this.mousePosition == null) 
			return this.saveMousePosition(event);
			
		//drag
		if(Event.pointerY(event) > this.mousePosition.top) { //pointer go's down
			var newTop = parseInt(this.ticker.style.top) + (Event.pointerY(event) - this.mousePosition.top);
			if(newTop > this.element.getHeight()) {
				if(TICKER_DEBUG) $('debug').update('drag:down-failed');
				return;
			}
			
			if(TICKER_DEBUG) $('debug').update('drag:down');
		} else { //pointer go's up
			var newTop = parseInt(this.ticker.style.top) - (this.mousePosition.top - Event.pointerY(event));
			if(newTop < (0 - this.element.getHeight())) {
				if(TICKER_DEBUG) $('debug').update('drag:up-failed');
				return;
			}
			
			if(TICKER_DEBUG) $('debug').update('drag:up');
		}
		
		this.ticker.style.top = newTop + 'px';
	},
	
	dragText_lr: function(event) {
		if(this.mousePosition == null)
			return this.saveMousePosition(event);
			
		//drag
		if(Event.pointerX(event) > this.mousePosition.left) { //pointer go's to right
			var newLeft = parseInt(this.ticker.style.left) + (Event.pointerX(event) - this.mousePosition.left);
			if(newLeft > this.element.getWidth()) {
				if(TICKER_DEBUG) $('debug').update('drag:right-failed');
				return;
			}
			
			if(TICKER_DEBUG) $('debug').update('drag:right');
		} else { //pointer go's to left
			var newLeft = parseInt(this.ticker.style.left) - (this.mousePosition.left - Event.pointerX(event));
			if(newLeft < (0 - this.ticker.getWidth())) {
				if(TICKER_DEBUG) $('debug').update('drag:left-failed');
				return;
			}
			
			if(TICKER_DEBUG) $('debug').update('drag:left');
		}
		
		this.ticker.style.left = newLeft + 'px';
	}
});