/**
*	Positioner Class
*
*	Accepts a position definition object and a layer, when position() function
*	called, it positions the layer according to the position rules.  The position
*	definition in a position object can be applied to any layer (not just the layer
*	used to create the object) by passing the alternative layer to the position()
*	function directly, this will override any layer set before hand. Can position
*	layers based on 1) particular x,y values; 2) the position of other objects on 
*	screen, screen size, or mouse position; 3) any combination of the two.
*	
*	Extends the StageElementClass
*
*/


/*

Positioning is performed by specifying an alignFrom point on the target layer
that indicates which part of the layer to align to a "base point", and an 
alignTo definition that indicates the base point to which the target point on 
the layer should align. Offsets can be applied to the base point.  The current 
width and height of the target layer is checked to calcuate the final position.
The alignTo value can be a fixed point on screen, or can be a point that is 
based off of the position of other objects on screen, the screen width, or the 
current mouse position.  The alignTo x and y values can be set independently
using any of these methods (x can be based on mouse position, y can be fixed).

After creating a position object for a layer, calling the position() function 
will recalculate the position the layer should be moved to based on current 
screen state, and reposition the layer to that location.

For example, creating a position like this will position the Layer object represented
by the variable myLayer, so that the center top of the layer will be on the right side
of the screen, and 10 pixels from the top:

	new Positioner(mylayer,1,["__screen",10],[mylayer,0]);

positionObj = {
	layer 	[object, string]
		The target layer to align, either an reference to a layer class 
		object, or the string id of a div. A specific layer object or div name
		can also be passed to the position function, which will override the 
		one saved in this object, this allows the position settings in this 
		object to be applied to different divs.
		
	alignFrom [number]
		The target point on the target layer to align to the base point, this
		is a number represented by the align constants listed below, basically
		the left corner of the top side of the layer is 0, and the numbers 
		increment clockwise around the layer.  Default alignFrom point is 0,
		top left of the layer.
	
	alignTo [array [number/string], string, object, LayerClass]		
		Specification of a base point to align the target layer to, can be: 
		1)	An array of x,y where each element in the array can be a 
			number for a specific value, a string that is the id's of an 
			object on screen (a string in the x element of the array means
			find this object, and use its x value as the x value of the base
			point), or the strings "__screen" or "__mouse" (ie: "__screen" 
			in the x element means use the screen width as the x value of 
			the base number, mouse means use the current position of the
			mouse to get this number).

		2)	A string that can be the id of an element on screen whose
			x,y values should be used to align this object to, or 
			the strings "__screen" or "__mouse", which means use both
			the x/y values of the screen width/height or mouse position.
			
		3)	A reference to a screen object
		
		4)	A reference to a Layer class object
		
	alignOffset [array [number/string/LayerClass/object]]
		An array of x,y that indicates an offset that can be added to the
		base point indicated by the alignTo value.  for example: an array 
		of [0,5] will move the final position of the layer down 5 pixels.
		If string is passed, the x,y values will be taken from the respective 
		width/height of the object whose name is passed. If Layer object, it
		gets the width/height of the layer object.  If numbers are passed, 
		they can be negative to be subtracted from base point position. 
		To subtract the width of an object whose name is passed, set the 
		subtractOffset array to [true,false].
	
	subtractOffset [array [boolean]]
		An array of length 2, each element being a flag indicating that the 
		alignOffset values should be subtracted	from the position represented
		by that element in the array [subtractXOffset,subtractYOffset].
	
	padding [number]
		Additional padding to add to offset, used to allow something to 
		align to an object, but be xx pixels from the object.
	
	
	alignFrom/alignTo position numbers are based on 
	vertical (top, middle, bottom), and horizontal (left,
	center, right).  Which means number 2 below means 
	align the top-right most corner to the base point.
	
	0					1					2
		_________________________________	
		|								|	
		|								|
	7	+								+	3
		|								|
		|_______________________________|	
	6					5					4
	
*/

//
//		Constants for alignTo/alignFrom:

//	Top side:
var POSITIONER_TL = 0;
var POSITIONER_TC = 1;
var POSITIONER_TR = 2;

//	Right side:
var POSITIONER_RC = 3;

//	Bottom side:
var POSITIONER_BR = 4;
var POSITIONER_BC = 5;
var POSITIONER_BL = 6;

//	Left side:
var POSITIONER_LC = 7;


/**
*	Positioner Constructor
*
*	Extends stage element class
*/
function Positioner(container, layer, alignFrom, alignTo, alignOffset, subtractOffset, padding){
	//	Establish superclass and inherit methods, StageElement contains 
	//	helper methods for objects that work with the screen
	this.inheritFrom = StageElement;
	this.inheritFrom(container);
	this.id="Positioner";
	
	//
	//	Variables used by this object
	this.layer = null;
	
	//
	//	Methods included
	
	//	Calculates the current position based on the alignTo and alignFrom definitions
	//	returns an array of an x,y value
	this.getPosition=positioner_getPosition;
	//	Calculates a base point from an alignTo definition, returns x,y array
	this.calcBasePoint=positioner_calcBasePoint;
	
	this.calcPos=positioner_calcPos;
	this.calcSize=positioner_calcSize;
	
	//	Positions the target layer to the position returned from getPosition
	this.position=positioner_position;
	
	//	Sets the layer to use by default
	this.setLayer=positioner_setLayer;
	
	//this.setAlignTo = positioner_setAlignTo;
	this.setAlignFrom=positioner_setAlignFrom;
	this.setAlignOffset=positioner_setAlignOffset;
	
	
	//	Save passed parameters
	
	if(layer){this.setLayer(layer);}
	
	this.alignTo=alignTo;
	this.subtractOffset=subtractOffset;
	this.alignFrom=parseInt(alignFrom);
	this.padding=padding;
	
	if(alignOffset){this.setAlignOffset(alignOffset);}
}	//	Positioner


/**
*	getPosition
*	Calculates the current position based on the alignTo and alignFrom definitions 
*	returns an array of an x,y value.  If a layer is passed, it gets the target
*	position of that layer, otherwise it gets the target position of the layer
*	set in this object.
*/
function positioner_getPosition(targetLayer){
	var layer = this.layer;
	var fromOffset = [0,0];
	if(targetLayer){
		layer = new Layer(this.container, targetLayer);
		layer.build();
		}
	
	if((layer === null)||(this.alignTo === null)){return;}
	
	//	Default align from is top left (0)
	if(!this.alignFrom || isNaN(parseInt(this.alignFrom))){this.alignFrom = 0;}
	
	//	Get width/height of target layer
	var w = layer.getWidth();
	var h = layer.getHeight();
	
	//	Calculate base point from alignTo and offset values
	basePos = this.calcBasePoint(layer, this.alignTo, this.alignOffset, this.subtractOffset, this.padding);
	
	//alert("layer ["+layer.div.id+"] size = " + w + "x" + h + ", base pos =" + basePos);
	
	//	Position Layer based on value of alignFrom
	
	//	Set top value first (vertical align = fromOffset[0])
	//	Align to middle of left/right side:
	if((this.alignFrom==7)||(this.alignFrom==3)){fromOffset[1]=Math.round(h/2);}
	//	Align to bottom:
	if((this.alignFrom==6)||(this.alignFrom==5)||(this.alignFrom==4)){fromOffset[1]=h;}
	
	//	Set left value (horizontal align = fromOffset[1])
	//	Align to center of top/bottom side:
	if((this.alignFrom==1)||(this.alignFrom==5)){fromOffset[0]=Math.round(w/2);}
	//	Align to right:
	if((this.alignFrom==2)||(this.alignFrom==3)||(this.alignFrom==4)){fromOffset[0]=w;}
	
	//this.log.debug("Aligning  " + layer.div.id + ", with alignFrom= " + this.alignFrom + " to  " +[basePos[0]-fromOffset[0],basePos[1]-fromOffset[1]]);
	//	Subtract fromOffset from basePos to get actual position of layer
	return [basePos[0]-fromOffset[0],basePos[1]-fromOffset[1]];
}	//	getPosition


/**
*	calcBasePoint
*
*	Calculates a base point from an alignTo and alingOffset definitions
*	and returns the resulting x,y array.
*/
function positioner_calcBasePoint(layer, basePoint, offset, subtractOffset, padding){
	var basePos = [0,0];
	
	//	Get base point represented by alignTo definition
	
	if(typeof basePoint == "string"){
		//	Have an object definition to use to get x,y from
		basePos =  this.calcPos(basePoint,layer);
		}
	else if(typeof basePoint == "object"){
		//	Object is an array
		if(basePoint.length){
			//	Have array of x,y definitions
			//	Get x pos
			basePos[0] = this.calcPos(basePoint[0],layer)[0];
			//	Get y pos
			basePos[1] = this.calcPos(basePoint[1],layer)[1];
			}
		//	Plain old object
		else{basePos =  this.calcPos(basePoint,layer);}
		}
	
	//alert("Base point " + basePos);
	if(!subtractOffset||(typeof subtractOffset!="object")||!subtractOffset.length){
		subtractOffset=[false,false];
		}
	
	//	Add/subtract any offset to basePos
	if(offset && offset.length){
		if(subtractOffset[0]){basePos[0] -= this.calcSize(offset[0])[0];}
		else {basePos[0] += this.calcSize(offset[0])[0];}
		if(subtractOffset[1]){basePos[1] -= this.calcSize(offset[1])[1];}
		else {basePos[1] += this.calcSize(offset[1])[1];}
	}
	
	//	Add any padding to base pos
	if(padding){
		if((typeof padding=="object") && padding.length){
			//this.log.warn("Padding is a array");
			basePos[0]+=padding[0];
			basePos[1]+=padding[1];
			}
		else if(!isNaN(parseInt(padding))) {
			//this.log.warn("Padding is a number");
			basePos[0]+=padding;
			basePos[1]+=padding;
			}
		else {
			this.log.warn("Could not identify padding variable : " + padding);
			}
		}
	
	return basePos;
	
}	//	calcBasePoint



/**
*	calcPos
*	Helper function for calcBasePoint that gets the current position for
*	a alignTo definition, and returns an array of the x,y values for the
*	type of object defined. calcBasePoint method then takes the part of this
*	array its interested in, and uses it in positioning the layer.
*/
function positioner_calcPos(posDef,layer){
	//	If value is number, return the number in 
	//	both parts of the array
	if(!isNaN(parseInt(posDef))){
		var val = parseInt(posDef);
		return [val,val];
		}
	
	//	If value is "__screen", return the screen width and height
	if(posDef=="__screen"){return [this.getScreenWidth(),this.getScreenHeight()];}
	
	if(posDef=="__mouse"){
		return [];
		}
	
	//	Convert string to object reference
	if(typeof posDef == "string"){
		var tempLayer = new Layer();
		var posDef = tempLayer.findObj(posDef);
		}
	
	if(typeof posDef == "object"){
		if(posDef.id == "Layer"){
			//	Layer class object..
			var pos = posDef.getAbsPosition();
			}
		else{
			//	Screen object...
			var tempLayer = new Layer();
			var pos = tempLayer.getAbsPosition(posDef);
			//this.log.debug("Found object " + posDef + " at position " + pos);
			}
		return pos;
		}
	
	return null;
}	//	calcPos


/**
*	calcSize
*	Helper function that calculates the offset, based on whether the
*	offset value is an array of numbers, or strings that refer to 
*	screen objects.
*/
function positioner_calcSize(offsetDef){
	
	//	If value is number, return the number in 
	//	both parts of the array
	if(!isNaN(parseInt(offsetDef))){
		var val = parseInt(offsetDef);
		return [val,val];
		}
		
	var size=[0,0];
	
	if(typeof offsetDef == "string"){
		var tempLayer = new Layer();
		var obj = tempLayer.findObj(offsetDef);
		size[0] = tempLayer.getWidth(obj);
		size[1] = tempLayer.getHeight(obj);
		}
	else if(typeof offsetDef == "object"){
		if(offsetDef.id == "Layer"){
			size[0] = offsetDef.getWidth();
			size[1] = offsetDef.getHeight();
			}
		else{
			var tempLayer = new Layer();
			size[0] = tempLayer.getWidth(offsetDef);
			size[1] = tempLayer.getHeight(offsetDef);
			}
		}
	return size;
}	//	calcSize


/**
*	position
*	Positions the div represented by the passed layer object, or the layer 
*	in this object, based on the rules specified in the alignTo and alignFrom 
*	values.
*/
function positioner_position(targetLayer){
	var layer = this.layer;
	if(targetLayer){
		if(typeof targetLayer=="string"){
			layer = new Layer(this.container, targetLayer);
			layer.build();
			}
		else if((typeof targetLayer!="object")||(targetLayer.id!="Layer")){return;}
		}
	
	if(layer === null){return;}
	
	
	//	Set position of layer using array returned from getPosition
	var pos=this.getPosition(targetLayer);
	
	//this.log.info("Positioning to " + pos);
	
	layer.setLeft(pos[0]);
	layer.setTop(pos[1]);
	
}	//	position


/**
*	setLayer
*	
*	Sets the layer that will be positioned by default by this object, can be
*	passed as layer class object, div reference or string. If a layer reference
*	or string, a layer class object will be created.
*/
function positioner_setLayer(layer){
	if((typeof layer == "object") && (layer.id=="Layer")){
		//	Have layer class object, if not built, build it now
		if(!layer.built){
			layer.build();
			}
		this.layer = layer;
		}
	else if(typeof layer == "string"){
		this.layer = new Layer(this.container, layer);
		this.layer.build();
		}
}	//	setLayer


/**
*	setAlignFrom
*	
*	Sets the alignFrom value, which decides which part of the target layer will
*	line up to the alignTo value. Passed as number, from 0 to 7, with 0 being
*	top-left (the default), and going clockwise around the layer.
*/
function positioner_setAlignFrom(alignFrom){
	this.alignFrom = parseInt(alignFrom);
	if(isNaN(this.alignFrom) || (this.alignFrom < 0) || (this.alignFrom > 10)){this.alignFrom = 0;}
}	//	setAlignFrom


/**
*	setAlignOffset
*
*	Sets the align offset array to use to adjust the exact positioning of the layer.
*/
function positioner_setAlignOffset(alignOffset){
	if(!alignOffset || (alignOffset.length != 2)){this.alignOffset =[0,0];}
	else {this.alignOffset = alignOffset;}
}	//	setAlignOffset


