/*
	Namespace: BGEffects
*/
var BGEffects = {
	DefaultOptions : {
		// BGEffects options:
		cssSelector 	: '.effectsContainer',	// (string) CSS selector for retrieving nested background containers
		increment 		: 1, 					// (number) differential incrementation between layers
		loop 			: true,					// (boolean) defines if effect should restart automatically
		
		
		// script.aculo.us Effect core options:
		duration 		: 10.0,		// (number) effect duration in seconds
		fps 			: 24,		// (number) maximum number of frames per second
		
		// Private options:
		queue : {position : 'end', scope : 'BGEffects'}
	}
};

/*
	Class: PerspectiveEffect
		Simulates a perspective background effect
		using background images from nested containers.
		Inherits from <Effect.Base>.
 */
BGEffects.PerspectiveEffect = Class.create(Effect.Base, {
	
	layers : null,
	_options : null,
	
	/*
		Constructor: initialize
			Initializes an instance of <BGEffects.PerspectiveEffect>.
		
		Parameters:
			- element: (ID | Element) the container element reference
			- options: (literal) a list of <BGEffects.DefaultOptions> parameters
				
	*/
	initialize : function(element, options){

		this.element = $(element);
		if (!this.element) throw(Effect._elementDoesNotExistError);
		
		// Options are stored locally to avoid loosing effect parameters:
		this._options = Object.extend(BGEffects.DefaultOptions, options || {});
		
		// Retrieve background containers and initialize layers:
		this.layers = [];
		var candidate = this.element;
		do {
			this.layers.push(new BGEffects.EffectLayer(candidate, this._options.increment * (this.layers.size() + 1)));
		} while(candidate = candidate.down(this._options.cssSelector));
	},
	
	/*
		Function: stop
			Runs background animation.
	*/
	play : function(){
		// Play effect using stored options:
		this.start(this._options);
	},
	
	/*
		Function: stop
			Terminates background animation.
	*/
	stop : function(){
		if(this.state && this.state === "running"){
			this.cancel();
		}
	},
	
	/*
		Function: restart
			Stops and starts animation again. 
	*/
	restart : function(){
		this.stop();
		this.play();
	},
	
	// @Implements
	update : function(position){
		this.layers.each(function(layer){
			layer.bgX += layer.increment;
			layer.update();
		});
	},
	
	// @Implements
	finish : function() {
		if(this._options.loop){
			this.restart();	
		}
	}
});

/*
	Class: POVEffect
		Simulates a perspective background effect
		using the mouse pointer as point of view (POV).
		Inherits from <BGEffects.PerspectiveEffect>.
*/
BGEffects.POVEffect = Class.create(BGEffects.PerspectiveEffect, {
	
	/*
		Property: mouse
			The <MouseTracker> monitoring instance.
	*/
	mouse : null,
	
	/*
		Property: povOrigin
			The horizontal position for referencing the point of view origin.
	*/
	povOrigin : 0,
	
	/*
		Constructor: initialize
			Initializes an instance of <Effect.POVEffect>.
		
		Parameters:
			- element	: (ID | Element) the container element reference
			- options	: (literal) a list of <BGEffects.DefaultOptions> parameters
			- povElement: (ID | Element) the reference element for POV origin. If null, container element will be used as POV reference.
				
	*/
	initialize : function($super, element, options, povElement){
		$super(element, options);
		this.mouse = new BGEffects.MouseTracker(povElement || element);
		
		// Compute POV origin:
		this.povOrigin = this.element.getWidth() / 2;
	},
	
	// @Override
	update : function(position){
		var acceleration = (this.mouse.x - this.povOrigin) * -1 / 100;
		this.layers.each(function(layer){
			layer.bgX += layer.increment * acceleration;
			layer.update();
		});
	}
});

/*
	Class: EffectLayer
		Layer abstraction for updating background positions.
		TODO: handle vertical background position
 */
BGEffects.EffectLayer = Class.create({
	element : null,
	increment : 1,
	bgX : 0,
	bgY : 0,
	
	/*
		Constructor: initialize
			Initializes an instance of <BGEffects.EffectLayer>.
		
		Parameters:
			- element	: (ID | Element) the container element reference
			- increment	: (number) the amount that may increment each position update
				
	*/
	initialize : function(element, increment){
		this.element = $(element);
		this.increment = increment;
		
		// Retrieve current background position:
		this.bgY = this.getBgY();
	},
	
	/*
		Function: update
			Updates element's background position.
	*/
	update : function(){
		this.element.setStyle('background-position: ' + this.bgX.toFixed(3) + 'px ' + this.bgY);
	},
	
	/*
		Function: getBgX
			Retrieves the *horizontal* position of element's background.
	*/
	getBgX : function(){
		return this.element.getStyle('background-position-x') || this.element.getStyle('background-position').split(' ')[0];
	},
	
	/*
		Function: getBgY
			Retrieves the *vertical* position of element's background.
	*/
	getBgY : function(){
		return this.element.getStyle('background-position-y') || this.element.getStyle('background-position').split(' ')[1];
	}
});

/*
	Class: MouseTracker
		Tracks mouse coordinates *within* a given container.
*/
BGEffects.MouseTracker = Class.create({
	
	offsetLeft : 0,
	offsetTop : 0,
	handler : null,
	
	x : 0,
	y : 0,
	
	initialize : function(container){
		container = $(container);
		this.handler = container.on('mousemove', this.update.bind(this));
		
		// Store offsets for cross-browser support:
		if(container.cumulativeOffset){
			this.offsetLeft = container.cumulativeOffset()[0];
			this.offsetTop = container.cumulativeOffset()[1];
		}
	},

	update : function(event){
		// Contextualize coordinates:
		this.x = event.pointerX() - this.offsetLeft;
		this.y = event.pointerY() - this.offsetTop;
		//_(this.x + ':' + this.y);
	},
	
	start : function(){
		// Delegate:
		this.handler.start();	
	},
	
	stop : function(){
		// Delegate:
		this.handler.stop();	
	}
});
