var BalloonTip = Class.create({
	initialize: function(element, options) {
		this.options = Object.extend({
			showDelay: 0, // seconds
			hideDelay: 0.2, // seconds
			width: 220,
			height: 'auto',
			content: 'Lorem ipsum dolor sit amet',
			shadowDistance: 7,
			contentOffset: { top: -2, left: 0 },
			offsetTop: 10,
			offsetLeft: 0,
			stemPosition: 'bottom',
			initialPosition: { left: 0, top: 0 },
			cssClass: false,
			animatedFade: true,
			autoFade: false,
			positioning: 'absolute',
			target: document.body
		}, options || {});
	
		if(element) {
			this.element = $(element);
			BalloonTip.tips[element.identify()] = this;
		}
		
		this.build();
		
		this.balloon.select('.close_link').each(function(link) {
			link.observe('click', this.hide.bind(this));
		}.bind(this));
		this.addObservers();
		Event.observe(window, 'unload', this.destroy.bind(this));	
	},
	
	addObservers: function() {
		if(this.element) {
			this.element.observe('mouseover', this.handleMouseover.bind(this));
			this.element.observe('mouseout', this.handleMouseout.bind(this));
			if(this.options.positioning == 'absolute') {
				this.element.observe('mousemove', this.updatePosition.bind(this));
			}
		}
		this.balloon.observe('mouseover', function() { window.clearTimeout(this.hideTimeoutId); }.bind(this));
		this.balloon.observe('mouseout', this.handleMouseout.bind(this));
	},
	
	removeObservers: function() {
		if(this.element) {
			this.element.stopObserving();
		}
		this.balloon.stopObserving();
	},
	
	disable: function() {
		this.removeObservers();
	},
	
	enable: function() {
		this.addObservers();
	},
	
	destroy: function() {
		this.balloon.stopObserving();
		if(this.element) {
			this.element.stopObserving();
			
			BalloonTip.tips[this.element.identify()] = null;
		}
		
		this.balloonContent.remove();
		this.balloon.remove();
	},
	
	build: function() {
		this.balloon = new Element('div', { 'class': 'balloon-tip stem-'+this.options.stemPosition+(this.options.cssClass ? ' '+this.options.cssClass : '') });
		this.balloonContent = new Element('div', { 'class': 'balloon-tip-content'});
		this.balloonContent.insert(this.options.content);
		
		var shadowTopRight = new Element('span', { 'class': 'balloon-tip-shadow-corner balloon-tip-shadow-topright' });
		var shadowTopLeft = new Element('span', { 'class': 'balloon-tip-shadow-corner balloon-tip-shadow-topleft' });
		var shadowBottomRight = new Element('span', { 'class': 'balloon-tip-shadow-corner balloon-tip-shadow-bottomright' });
		var shadowBottomLeft = new Element('span', { 'class': 'balloon-tip-shadow-corner balloon-tip-shadow-bottomleft' });
		
		if(this.options.stemPosition == 'bottom') {
			var shadowTop = new Element('span', { 'class': 'balloon-tip-shadow-top' });
			var shadowBottomLeftPart = new Element('span', { 'class': 'balloon-tip-shadow-bottom' });
			var shadowBottomRightPart = new Element('span', { 'class': 'balloon-tip-shadow-bottom' });
		} else if(this.options.stemPosition == 'top') {
			var shadowBottom = new Element('span', { 'class': 'balloon-tip-shadow-bottom' });
			var shadowTopLeftPart = new Element('span', { 'class': 'balloon-tip-shadow-top' });
			var shadowTopRightPart = new Element('span', { 'class': 'balloon-tip-shadow-top' });	
		}
		
		var shadowLeft = new Element('span', { 'class': 'balloon-tip-shadow-left' });
		var shadowRight = new Element('span', { 'class': 'balloon-tip-shadow-right' });
		
		var balloonStem = new Element('span', { 'class': 'balloon-tip-stem' });
		
		this.balloon.appendChild(shadowTopRight);
		this.balloon.appendChild(shadowTopLeft);
		this.balloon.appendChild(shadowBottomRight);
		this.balloon.appendChild(shadowBottomLeft);
		if(this.options.stemPosition == 'bottom') {
			this.balloon.appendChild(shadowTop);
			this.balloon.appendChild(shadowBottomLeftPart);
			this.balloon.appendChild(shadowBottomRightPart);
		} else if(this.options.stemPosition == 'top') {
			this.balloon.appendChild(shadowBottom);
			this.balloon.appendChild(shadowTopLeftPart);
			this.balloon.appendChild(shadowTopRightPart);			
		}
		this.balloon.appendChild(shadowLeft);
		this.balloon.appendChild(shadowRight);
		this.balloon.appendChild(balloonStem);
		
		this.balloon.appendChild(this.balloonContent);
		this.options.target.insert({ top: this.balloon });
		
		// figure out the size of the balloon
		if(this.options.width != 'auto' && !this.options.height != 'auto') {
			this.balloonContent.style.height = this.options.height+'px';
			this.balloonContent.style.width = this.options.width+'px';
		} else if(this.options.width == 'auto' && this.options.height != 'auto') {
			this.balloonContent.style.height = this.options.height+'px';
		} else if(this.options.width != 'auto' && this.options.height == 'auto') {
			this.balloonContent.style.width = this.options.width+'px';
		}
		
		var contentDimensions = this.balloonContent.getDimensions();
		var balloonWidth = contentDimensions.width + 2*this.options.shadowDistance;
		var balloonHeight = contentDimensions.height + 2*this.options.shadowDistance;
		this.balloon.setStyle({ width: balloonWidth+'px', height: balloonHeight+'px' });
		this.balloonContent.style.top = (this.options.shadowDistance+this.options.contentOffset.top)+'px';
		this.balloonContent.style.left = (this.options.shadowDistance+this.options.contentOffset.left)+'px';
				
		// position shadow bits
		var shadowCornerSize = shadowTopLeft.getDimensions();
		shadowLeft.style.height = (balloonHeight - 2*shadowCornerSize.height)+'px';
		shadowRight.style.height = shadowLeft.style.height;
		
		if(this.options.stemPosition == 'bottom') {
			shadowTop.style.width = (balloonWidth - 2*shadowCornerSize.width)+'px';
			var stemSize = balloonStem.getDimensions();
			balloonStem.style.left = ((balloonWidth - stemSize.width)/2)+'px';
			
			shadowBottomLeftPart.style.width = ((parseInt(shadowTop.style.width, 10)-stemSize.width)/2)+'px';
			shadowBottomRightPart.style.width = shadowBottomLeftPart.style.width;
			shadowBottomLeftPart.style.left = shadowCornerSize.width+'px';
			shadowBottomRightPart.style.right = shadowCornerSize.width+'px';
		} else if(this.options.stemPosition == 'top') {
			shadowBottom.style.width = (balloonWidth - 2*shadowCornerSize.width)+'px';
			var stemSize = balloonStem.getDimensions();
			balloonStem.style.left = ((balloonWidth - stemSize.width)/2)+'px';
			
			shadowTopLeftPart.style.width = ((parseInt(shadowBottom.style.width, 10)-stemSize.width)/2)+'px';
			shadowTopRightPart.style.width = shadowTopLeftPart.style.width;
			shadowTopLeftPart.style.left = shadowCornerSize.width+'px';
			shadowTopRightPart.style.right = shadowCornerSize.width+'px';			
		}
		
		this.balloon.hide();
		this.balloonSize = { width: balloonWidth, height: balloonHeight };
	},
	
	updateContent: function(content) {
		this.balloonContent.update(content);
	},
	
	handleMouseover: function(event) {
		window.clearTimeout(this.hideTimeoutId);
		this.updatePosition(event);
		
		if(!this.visible()) {
			this.show();
		}
	},
	
	visible: function() {
		return this.balloon.visible();
	},
	
	show: function() {
		if(this.options.animatedFade) {
			var finalTopValue = parseInt(this.balloon.style.top, 10);
			var finalLeftValue = parseInt(this.balloon.style.left, 10);
			this.balloon.setStyle({ top: (finalTopValue-10)+'px' });
			new Effect.Parallel([
			  new Effect.Move(this.balloon, { sync: true, x: finalLeftValue, y: finalTopValue, mode: 'absolute' }), 
			  new Effect.Appear(this.balloon, { sync: true })
			], { duration: 0.5 });
		} else {
			this.balloon.show();
			//new Effect.Appear(this.balloon, { duration: 0.3 });
		}
		
		if(this.options.autoFade) {
			this.hideTimeoutId = this.hide.bind(this).delay(this.options.autoFade);
		}
		//this.balloоn.show();
	},
	
	updatePosition: function(event) {
		switch(this.options.positioning) {	
		case 'relative':
			var elementOffset = this.element.positionedOffset();
			
			if(this.options.stemPosition == 'top') {
				var top = (elementOffset.top+this.options.offsetTop);
				var left = (elementOffset.left-(this.balloonSize.width/2)-this.options.offsetLeft);
			} else {
				var top = (elementOffset.top-this.balloonSize.height-this.options.offsetTop);
				var left = (elementOffset.left-(this.balloonSize.width/2)-this.options.offsetLeft);			
			}
			
			break;
		case 'absolute':
			var mouseX = event ? Event.pointerX(event) : this.options.initialPosition.left;
			var mouseY = event ? Event.pointerY(event) : this.options.initialPosition.top;
			
			if(this.options.stemPosition == 'top') {
				var top = (mouseY+this.options.offsetTop);
				var left = (mouseX-(this.balloonSize.width/2)-this.options.offsetLeft);
			} else {
				var top = (mouseY-this.balloonSize.height-this.options.offsetTop);
				var left = (mouseX-(this.balloonSize.width/2)-this.options.offsetLeft);			
			}
			break;
		}
		
		this.balloon.setStyle({
			top: top+'px',
			left: left+'px'
		});	
	},
	
	handleMouseout: function() {
		this.hideTimeoutId = this.hide.bind(this).delay(this.options.hideDelay);
	},
	
	hide: function() {
		if(this.options.animatedFade) {
			new Effect.Parallel([
			  new Effect.Move(this.balloon, { sync: true, x: 0, y: -10, mode: 'relative' }), 
			  new Effect.Fade(this.balloon, { sync: true })
			], { duration: 0.5 });
			
			//new Effect.Fade(this.balloon, { duration: 0.5 });
		} else {
			this.balloon.hide();
		}
	}
});

BalloonTip.tips = {};