/**
 * The MIT License
 * Copyright (c) 2009 Christian Zenker <christian.zenker@599media.de>
 * inspired by Fabio Zendhi Nagao's (http://zend.lojcomm.com.br)
 *		iFishEye (http://zendold.lojcomm.com.br/ifisheye/)
 **/
var CzFishEye = new Class({

	/**
	 * Options
	 **/
	options: {
		idContainer:	'fisheye-container',
		classImage:		'fisheye-img',
		classLabel:     'fisheye-label',
		eyeRadius:		192,
		norm:			'p1',
		blankPath:      'typo3/clear.gif',
		useAxis:        'x',
		onCreate:		null,
		onTooltipShow:	null,
		onTooltipHide:	null
	},
	
	/**
	 * Constructor
	 *
	 * @param	Object		Options
	 */
	initialize: function(options) {
		this.setOptions(options);
		
		// adding a custom, more simple, function to calculate the position
		// might cause some problems, but speeds the whole rendering up
		// the native function is espacially very slow in FF3 on Linux
		Element.implement({
			getFastPosition: function(){
					var el = this, left = 0, top = 0;
					do {
						left += el.offsetLeft || 0;
						top += el.offsetTop || 0;
						el = el.offsetParent;
					} while (el);
					return {'x': left, 'y': top};
				}
		});
		
		var container = $(this.options.idContainer);
		
		this.entries = [];
		var labels = container.getElements('.'+this.options.classLabel);
		// foreach image in container
		container.getElements('.'+this.options.classImage).each(function(el, i) {
				//switch for IE6
			if(Browser.Engine.trident && Browser.Engine.version < 5) {
				var src = el.getProperty('src');
				if(src.substr(src.length - 3) == "png") {
					//apply AlphaImageLoader if png and Internet Explorer
					el.setStyle('filter', 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="'+ src +'",sizingMethod="scale")');
					el.setProperty('src', this.options.blankPath);
				}
			}
			
			if(labels[i] !== undefined) {
				el.addEvents({
					'mouseenter': this.onMouseEnter.bindWithEvent(this, i),
					'mouseleave': this.onMouseLeave.bindWithEvent(this, i)
				});
				labels[i].setStyle('visibility', 'hidden');
			}
			
			//add all items to the array
			this.entries.push({
				//size of thumb
				thumb: {
					width: el.getStyle('width').toInt(),
					height: el.getStyle('height').toInt()
				},
				//size of fullsize pic
				fullsize: {
					width: el.getProperty('width'),
					height: el.getProperty('height')
				},
				//the img tag
				img: el,
				//the captions text
				label: labels[i]
			});
//			el.removeProperties('width', 'height');
		}.bind(this));
		
		//add Event Listeners for container
		container.addEvents({
			'mousemove': this.onContainerMouseMove.bindWithEvent(this),
			'mouseleave': this.onContainerMouseLeave.bindWithEvent(this)
		});
		this.fireEvent('onCreate');
	},
	
	/**
	 * Event fired when mouse is over an item
	 * @param Event
	 */
	onMouseEnter: function(event, i) {
		this.entries[i].label.setStyle('visibility', 'visible');
	},
	/**
	 * Event fired when mouse leaves an item
	 * @param Event
	 */
	onMouseLeave: function(event, i) {
		this.entries[i].label.setStyle('visibility', 'hidden');
	},
	
	/**
	 * Event called when mouse enters the container (don't call this directly)
	 *
	 * @param	Event		event: the event fired by Mootools
	 **/
	onContainerMouseEnter: function(event) {
	},
	
	/**
	 * Event called when mouse moves in the container (don't call this directly)
	 *
	 * @param	Event		event: the event fired by Mootools
	 **/
	onContainerMouseMove: function(event) {
		this.entries.each(function(entry, index) {
			entry.img.get('morph').cancel();
			entry.img.setStyles(this.getImageSize(index, event.page));
		}.bind(this));
		return false;
	},
	
	/**
	 * Event called when mouse leaves the container (don't call this directly)
	 *
	 * @param	Event		event: the event fired by Mootools
	 **/
	onContainerMouseLeave: function(event) {
		this.entries.each(function(entry) {
			//morph back to thumb size
			entry.img.morph(entry.thumb);
		});
	},
	
	/**
	 * get an images size depending on the position of the mouse pointer
	 *
	 * @param	Integer		index: Index of the image in the entries array
	 * @param	Object		pointer: Object {x:UInteger,y:UInteger} that holds coordinates of the mouse pointer
	 *
	 * @return Object {width:UInteger,height:UInteger} holds width and height of the image
	 **/
	getImageSize: function(index, pointer) {
		// get center point of the object
		var dist = this.getDistance(index, pointer);
		
		if(dist > this.options.eyeRadius) {
			//if: element not inside the eye-radius -> thumb size
			return this.entries[index].thumb;
		}else{
			//else: calculate new size
			return this.getSize(index, dist);
		}
	},
	
	/**
	 * calculates distance from the center of one picture to the mouse pointer
	 *
	 * @param	Integer		index: Index of the image in the entries array
	 * @param	Object		pointer: Object {x:UInteger,y:UInteger} that holds coordinates of the mouse pointer
	 *
	 * @return	uint		distance
	 **/
	getDistance: function(index, pointer) {
		var imgPos = this.entries[index].img.getFastPosition(this.options.container), imgSize = this.entries[index].img.getSize();		
		var delta = {
			x: Math.abs(pointer.x - (imgPos.x + imgSize.x/2)),
			y: Math.abs(pointer.y - (imgPos.y + imgSize.y/2))
		};
		
		var dist = 0;
		if(this.options.useAxis.length > 1) {
			//if: calculate distance from both axis
			switch(this.options.norm.toLowerCase()) {
				case 'pn': //maximum norm
					dist = Math.max(delta.x, delta.y);
					break;
				case 'p2': //euclidean norm
					dist = Math.sqrt(Math.pow(delta.x,2) + Math.pow(delta.y,2));
					break;
				case 'p1': //manhattan norm
				default:
					dist = delta.x + delta.y;
					break;
			}
		}else{
			//else: calculate distance from one axis
			dist = delta[this.options.useAxis];
		}
		return Math.round(dist);
	},
	
	/**
	 * calculate new size of an image depending on its distance to the pointer
	 *
	 * @param	Integer		index: Index of the image in the entries array
	 * @param	UInteger	dist: distance to the pointer
	 *
	 * @return Object	{width:UInteger,height:UInteger} size of the object
	 **/
	getSize: function(index, dist) {
		var ratio = (dist/this.options.eyeRadius);
		//calculations uses a linear function
		var size = {
			width: this.entries[index].fullsize.width.toInt() + ratio * (this.entries[index].thumb.width - this.entries[index].fullsize.width),
			height: this.entries[index].fullsize.height.toInt() + ratio * (this.entries[index].thumb.height - this.entries[index].fullsize.height)
		};
		return size;
	}
	
}).implement(new Events).implement(new Options);
