var Canvas = window.Canvas || {};

(function () {
	/**
	 * Canvas Element Class
	 *
	 * @namespace Canvas.Element
	 * @class Element
	 * @constructor
	 * @param el {HTMLElement | String} Container element for the canvas.
	 */
	Canvas.Element = function(el, oConfig) {
		this._initElement(el);
		this._initConfig(oConfig);
		this._createCanvasBackground();
		this._createContainer();
		this._initEvents();
		this._initCustomEvents();
	};

	/**
	 * From YUI (2.4.0?)
   * We want to be able to use getElementsByTagName as a collection
   * to attach a group of events to.  Unfortunately, different 
   * browsers return different types of collections.  This function
   * tests to determine if the object is array-like.  It will also 
   * fail if the object is an array, but is empty.
   * @method _isValidCollection
   * @param o the object to test
   * @return {boolean} true if the object is array-like and populated
   * @static
   * @private
   */
  function isValidCollection(o) {
      try {
          return ( o                     && // o is something
                   typeof o !== "string" && // o is not a string
                   o.length              && // o is indexed
                   !o.tagName            && // o is not an HTML element
                   !o.alert              && // o is not a window
                   typeof o[0] !== "undefined" );
      } catch(ex) {
          return false;
      }
  };

	/**
	 * Constant for the default CSS class name that represents a Canvas
   * @property Canvas.Element.CSS_CANVAS
   * @static
   * @final
   * @type String
   */
  /// Canvas.Element.CSS_CANVAS = "canvas-module";	
	Canvas.Element.prototype.fillBackground = true;
	Canvas.Element.prototype.photoborder = true;
	Canvas.Element.prototype.polaroid = false;
	Canvas.Element.prototype._backgroundImg = null;
	
	/**
   * The object literal containing mouse position if clicked in an empty area (no image)
   * @property _groupSelector
   * @type object
   */
	Canvas.Element.prototype._groupSelector = null;
	
	/**
   * The array element that contains all the images of the canvas
   * @property _aImages
   * @type object
   */
	Canvas.Element.prototype._aImages = null;
	
	/**
   * The array element that contains all the boxes
   * @property _aBoxes
   * @type object
   */	
	Canvas.Element.prototype._aBoxes = null;
	
	/**
   * The element that references the canvas interface implementation
   * @property _oContext
   * @type object
   */
	Canvas.Element.prototype._oContext = null;
	
	/**
   * The main element that contains the canvas
   * @property _oElement
   * @type object
   */
	Canvas.Element.prototype._oElement = null;

	/**
   * The object literal containing config parameters
   * @property _oConfig
   * @type object
   */
	Canvas.Element.prototype._oConfig = null;
	
	/**
   * The object literal containing the current x,y params of the transformation
   * @property _currentTransform
   * @type object
   */
	Canvas.Element.prototype._currentTransform = null;

	Canvas.Element.prototype._savedState = null;

	Canvas.Element.prototype.savedmp = false;
	Canvas.Element.prototype.selected = null;

	/**
   * The object literal containing the target Hover or whatever
   * @property _hooverTarget
   * @type object
   */
	Canvas.Element.prototype._hoverTarget = null;
	
	/**
   * The Canvas class's initialization method. This method is automatically 
   * called by the constructor, and sets up all DOM references for 
   * pre-existing markup, and creates required markup if it is not 
   * already present.
   * @method _initElement
   * @param el {HTMLElement | String} el The element representing the Canvas
   */
	Canvas.Element.prototype._initElement = function(el) {
		if(YAHOO.util.Dom.inDocument(el)) {
			if(YAHOO.lang.isString(el)) {
				this._oElement = document.getElementById(el);
			} else {
				this._oElement = el;
			}
			/// YAHOO.util.Dom.addClass(this._oElement, Canvas.Element.CSS_CANVAS);
		}
		else {
			if (YAHOO.env.ua.ie) {
				var canvasEl = excanvas(document.createElement('canvas'));	
			}
			else {
				var canvasEl = document.createElement('canvas');
			}
			canvasEl.id = el + '';
			var oCanvas = document.body.insertBefore(canvasEl, document.body.firstChild);
			this._oElement = document.getElementById(el + '');
		}
	
		// it contains the active image and the listeners
		this._oContextTop = this._oElement.getContext('2d');
	};

	/**
   * The custom events initialization method. 
   * @method _initCustomEvents
   */
	Canvas.Element.prototype._initCustomEvents = function() {
		this.beforeRenderEvent = new YAHOO.util.CustomEvent('beforeRenderEvent', this, true);	
	};
	
	/**
   * For now we use an object literal without methods to store the config params
   * @method _initConfig
   * @param oConfig {Object} userConfig The configuration Object literal 
   * containing the configuration that should be set for this module. 
   * See configuration documentation for more details.
   */
	Canvas.Element.prototype._initConfig = function(oConfig) {
		this._oConfig = oConfig;
		this._oElement.width = this._oConfig.width;
		this._oElement.height = this._oConfig.height;
		this._oElement.style.width = this._oConfig.width + 'px';
		this._oElement.style.height = this._oConfig.height + 'px';
	};

	/**
   * Adds main mouse listeners to the whole canvas
   * @method _initEvents
   * See configuration documentation for more details.
   */
	Canvas.Element.prototype._initEvents = function() {
		YAHOO.util.Event.on(this._oElement, 'mousedown', this.onMouseDown, this, true);
		YAHOO.util.Event.on(this._oElement, 'mouseup', this.onMouseUp, this, true);
		YAHOO.util.Event.on(this._oElement, 'mousemove', this.onMouseMove, this, true);
		// Canvas.Element.addEventListener("mousedown", function(evt) { startTransform(evt); }, false);
	};
	
	/**
   * It creates a secondary canvas to contain all the images are not being translated/rotated/scaled
   * @method _createContainer
   */
	Canvas.Element.prototype._createContainer = function() {
		if (YAHOO.env.ua.ie) {
			var canvasEl = excanvas(document.createElement('canvas'));	
		}
		else {
			var canvasEl = document.createElement('canvas');
		}
		canvasEl.id = 'canvas-container';
		var oContainer = this._oElement.parentNode.insertBefore(canvasEl, this._oElement);
		oContainer.width = this._oConfig.width;
		oContainer.height = this._oConfig.height;
		oContainer.style.width = this._oConfig.width + 'px';
		oContainer.style.height = this._oConfig.height + 'px';
		// this will contain all images that are not on the top
		this._oContextContainer = oContainer.getContext('2d'); 
	};
	
	Canvas.Element.prototype._createCanvasBackground = function() {
		if (YAHOO.env.ua.ie) {
			var canvasEl = excanvas(document.createElement('canvas'));	
		}
		else {
			var canvasEl = document.createElement('canvas');
		}
		canvasEl.id = 'canvas-background';
		var oBackground = this._oElement.parentNode.insertBefore(canvasEl, this._oElement);
		oBackground.width = this._oConfig.width;
		oBackground.height = this._oConfig.height;
		oBackground.style.width = this._oConfig.width + 'px';
		oBackground.style.height = this._oConfig.height + 'px';
		// this will contain the background
		this._oContextBackground = oBackground.getContext('2d'); 
	};
	
	Canvas.Element.prototype.setCanvasBackground = function(oImg) {
		this._backgroundImg = oImg;
		var originalImgSize = oImg.getOriginalSize();
		
		this._oContextBackground.drawImage(oImg._oElement, 0, 0, originalImgSize.width, originalImgSize.height);
	};
	
	/**
   * Method that defines the actions when mouse is released on canvas.
	 * The method resets the currentTransform parameters, store the image corner
	 * position in the image object and render the canvas on top.
   * @method onMouseUp
   * @param e {Event} Event object fired on mouseup
   */
	Canvas.Element.prototype.onMouseUp = function(e) {
		if (this._aImages == null) {
			return;
		}
		
		if (this._currentTransform) {
			// determine the new coords everytime the image changes its position
			this._currentTransform.target.setImageCoords();
			
			var cmp = this.findMousePosition(e);
	
			// Check if I dropped something into or out of one of the boxes
			/*if(this._currentTransform.target.elementtype != "box")  {
				this.dropManager(this._currentTransform.target);
			}*/
		}

		this._currentTransform = null;
		this._groupSelector = null;
		
		// this is to clear the selector box
		this.renderAll();
	};
	
	/** 
   * Method that defines the actions when mouse is clicked on canvas.
	 * The method inits the currentTransform parameters and renders all the
	 * canvas so the current image can be placed on the top canvas and the rest
	 * in on the container one.
   * @method onMouseDown
   * @param e {Event} Event object fired on mousedown
   */
	Canvas.Element.prototype.onMouseDown = function(e) {
		var mp = this.findMousePosition(e);
		var doubleclick = false;
		if(Math.abs(mp.ex - this.savedmp.ex) < 2 && Math.abs(mp.ex - this.savedmp.ex) < 2) 
			doubleclick = true;

		this.savedmp = mp;

		// ignore if something else is already going on
		if (this._currentTransform != null || this._aImages == null) {
			return;
		}

		// determine whether we clicked the image
		var oImg = this.findTargetImage(mp, false, false);
		if (!oImg) {
			this._groupSelector = { ex: mp.ex, ey: mp.ey,
				 					top: 0, left: 0 };
			if(this.selected)
				this.selected.highlighted = false;
			
			IDidItInterface.updateTopMenu(this.selected, false);

			this.selected = null;
		} else if(!oImg.fixed) {
			if(this.selected)
				this.selected.highlighted = false;
				
			IDidItInterface.updateTopMenu(this.selected, false);
			this.selected = oImg;
			this.selected.highlighted = true; 
			IDidItInterface.updateTopMenu(this.selected, true);
			
			// determine if it's a drag or rotate case
			// rotate and scale will happen at the same time
			var action = (!this.findTargetCorner(mp, oImg)) ? 'drag' : 'rotate';
			
			this._currentTransform = { 	target: oImg,
										action: action,
										scalex: oImg.scalex,
										offsetX: mp.ex - oImg.left,
										offsetY: mp.ey - oImg.top,
			 							ex: mp.ex, ey: mp.ey,
										left: oImg.left, top: oImg.top,
										theta: oImg.theta 
									};
			
			this.renderAll(false);
						
			if(doubleclick) {
				this._currentTransform = null;
				this._groupSelector = null;		
				if(canvaselement.selected.elementtype == 'video')	
					IDidItInterface.loadContent("MediaVideo/view/" + canvaselement.selected._ID, true, IDidItInterface.videoplay_Callback);
				else if(canvaselement.selected.elementtype == 'image')
					IDidItInterface.loadContent("Media/view/" + canvaselement.selected._ID, true, IDidItInterface.photo_view_Callback);
				else if(canvaselement.selected.elementtype == 'photoshow')
					IDidItInterface.loadContent("Media/view/" + canvaselement.selected._ID, true);
			}
		} 
	};
	
	Canvas.Element.prototype.saveState = function(oBox) {
		var sibdata = [];
		
		for(var i in oBox._aImages)
			sibdata.push({top: oBox._aImages[i].top, left: oBox._aImages[i].left, scalex: oBox._aImages[i].scalex, scaley: oBox._aImages[i].scaley});
		
		return {scalex: oBox._Img.scalex,
				left: oBox._Img.left,
				top: oBox._Img.top,
				siblings: sibdata
		};
	};
	
	/**
   * Method that defines the actions when mouse is hovering the canvas.
	 * The currentTransform parameter will definde whether the user is rotating/scaling/translating
	 * an image or neither of them (only hovering). A group selection is also possible and would cancel
	 * all any other type of action.
	 * In case of an image transformation only the top canvas will be rendered.
   * @method onMouseMove
   * @param e {Event} Event object fired on mousemove
   */
	Canvas.Element.prototype.onMouseMove = function(e) {
		var mp = this.findMousePosition(e);
		
		if (this._aImages == null) {
			return;
		}
		
		/*if (this._groupSelector != null) {
			// We initially clicked in an empty area, so we draw a box for multiple selection.
			this._groupSelector.left = mp.ex - this._groupSelector.ex;
			this._groupSelector.top = mp.ey - this._groupSelector.ey;
			this.renderTop();
		} else */
		if (this._currentTransform == null) {
			// Here we are hovering the canvas then we will determine
			// what part of the pictures we are hovering to change the caret symbol.
			// We won't do that while dragging or rotating in order to improve the
			// performance.
			var targetImg = this.findTargetImage(mp, true, false);
			
			// Manage the hover event
			/*this.hoverManager(mp, targetImg);*/
			this.setCursor(mp, targetImg);
		}
		else {
			if (this._currentTransform.action == 'rotate') {
				this.rotateImage(mp);
				this.scaleImage(mp);
				
				/*if(this._currentTransform.target.elementtype != "box") { // If the image is a box, we don't allow rotation on it
					this.rotateImage(mp);
					this.scaleImage(mp);
				} else {
					this.scaleImage(mp);
					this.updateSiblingsR();
				}*/
			}
			else {								// We are moving the image
				this.translateImage(mp);
				
				/*if(this._currentTransform.target.elementtype != "box") {
					this.dragOverManager(this.findTargetImage({ex: this._currentTransform.target.left, ey: this._currentTransform.target.top}, true, this._currentTransform.target));
					this.translateImage(mp);	// Move it
				} else {
					this.dragOverManager(this.findTargetImage({ex: this._currentTransform.target.left, ey: this._currentTransform.target.top}, true, this._currentTransform.target));
					this.translateImage(mp);	// Move it
					this.updateSiblingsT();
				}*/
			}
			// only commit here. when we are actually moving the pictures
			this.renderTop();
		}		
	};

	Canvas.Element.prototype.hoverManager = function (mp, targetImg) {
		this.setCursor(mp, targetImg);
	};
	
	/*Canvas.Element.prototype.hoverManagerOn = function (mp, targetImg) {
		//if ("console" in window) console.log("%o", targetImg);
		
		if(targetImg.showtoolbar) {
			if(targetImg.elementtype == "box") {
				targetImg._oToolbar = Toolbars.box;
			} else if (targetImg.elementtype == "image") {
				targetImg._oToolbar = Toolbars.img;
				if(targetImg._inBox != null) {
					if(targetImg._inBox._Img.showtoolbar)
						targetImg._inBox._Img._oToolbar = Toolbars.box;
				}
			} else if (targetImg.elementtype == "video") {
				targetImg._oToolbar = Toolbars.vid;
				if(targetImg._inBox != null) {
					if(targetImg._inBox._Img.showtoolbar)
						targetImg._inBox._Img._oToolbar = Toolbars.box;
				}
			}
		}
		
		this.renderAll(false);
	};*/

	/*Canvas.Element.prototype.hoverManagerOff = function (targetImg) {
		if(targetImg.showtoolbar) {
			if(targetImg.elementtype == "box") {
				targetImg._oToolbar = null;
			} else if ((targetImg.elementtype == "image") || (targetImg.elementtype == "video")) {
				targetImg._oToolbar = null;
				if(targetImg._inBox != null) {
					if(targetImg._inBox._Img.showtoolbar)
						targetImg._inBox._Img._oToolbar = null;
				}
			}
		}

		this.renderAll(false);
	};*/

	/*Canvas.Element.prototype.dragOverManager = function (targetImg) {
		if((this._currentTransform.target.elementtype != "box")) {
			if(targetImg != this._currentTransform.dragovertarget) {
				if(targetImg.elementtype == "box") {
					if(targetImg && !this._currentTransform.dragovertarget) {
						targetImg._Box.dragOver(this._currentTransform.target);
						this._currentTransform.dragovertarget = targetImg;
					} else if (targetImg && this._currentTransform.dragovertarget) {
						this._currentTransform.dragovertarget._Box.dragOff();
						targetImg._Box.dragOver(this._currentTransform.target);
						this._currentTransform.dragovertarget = targetImg;
					} else { // if (!targetImg && this._currentTransform.dragovertarget) {
						this._currentTransform.dragovertarget._Box.dragOff();
						this._currentTransform.dragovertarget = null;
					}
				} else if (this._currentTransform.dragovertarget) {
					this._currentTransform.dragovertarget._Box.dragOff();
					this._currentTransform.dragovertarget = null;
				}
			}	
		}
	};*/

	// Check if a given image is currently on any of the boxes
	Canvas.Element.prototype.isInBox = function(oImg) {
		var tmp = [];
		
		if(!this._aBoxes)
			return null;
		
		for (var i = 0; i < this._aBoxes.length; i += 1) {
			var boxleft = this._aBoxes[i]._Img.left - this._aBoxes[i]._Img.currentWidth/2;
			var boxright = this._aBoxes[i]._Img.left + this._aBoxes[i]._Img.currentWidth/2;
			var boxtop = this._aBoxes[i]._Img.top - this._aBoxes[i]._Img.currentHeight/2;
			var boxbottom = this._aBoxes[i]._Img.top + this._aBoxes[i]._Img.currentHeight/2;
			var ci_x = oImg.left;
			var ci_y = oImg.top;

			if((ci_x > boxleft) && (ci_x < boxright) && (ci_y > boxtop) && (ci_y < boxbottom)) {
				tmp[tmp.length] = i;  
			}
		}
		
		if(tmp.length == 0) {
			return null;
		} else if(tmp.length == 1) {
			return this._aBoxes[tmp[0]];
		} else {
			// First we check which box was on top
			var m = 0;
			var l = 0;
			var k;
			for(i = 0; i < tmp.length; i++) {
				l = this.getZindex(this._aBoxes[tmp[i]]._Img);
				if (l > m) {
					m = l;
					k = i;
				}
			}
			
			return this._aBoxes[tmp[k]];
		}
	};

	// Manage the drop event. Check if an image was droped into or out of the boxes
	/*Canvas.Element.prototype.dropManager = function(oImg) {
		var contBox = this.isInBox(oImg);					// Check on what box did the image fell, if any
	
		if(!contBox) { 										// Image fell into none of the boxes
			if(oImg._inBox != null)	{						// Previously image was in a box
				oImg._inBox.delImage(oImg, true);			// Take it out of the previous box
			}
		} else {
			if(oImg._inBox != null) { 						// Image was in a box
				if(oImg._inBox != contBox)	{				// Box was diferent from the current one
					oImg._inBox.delImage(oImg, true);		// So we take it out of the old one
					contBox.addImage(oImg, true);			// And then add it to the new box
				} else {
					contBox.fitIntoBox(oImg);
				}
			} else {										// Image was not in a box
				contBox.addImage(oImg, true);				// So we just add it to the new box
			}
		}
	};*/

	/**
   * Translate image
   * @method translateImage
   * @param e {Event} the mouse event
   */	
	Canvas.Element.prototype.translateImage = function(mp) {
		this._currentTransform.target.left = mp.ex - this._currentTransform.offsetX;
		this._currentTransform.target.top = mp.ey - this._currentTransform.offsetY;
	};
	
	/*Canvas.Element.prototype.updateSiblingsT = function() {
		for (var i in this._currentTransform.target._Box._aImages) {
			this._currentTransform.target._Box._aImages[i].left = this._savedState.siblings[i].left + this._currentTransform.target.left - this._savedState.left;
			this._currentTransform.target._Box._aImages[i].top = this._savedState.siblings[i].top + this._currentTransform.target.top - this._savedState.top;
		}	
    };*/
    
	/**
   * Scale image
   * @method scaleImage
   * @param e {Event} the mouse event
   */	
	Canvas.Element.prototype.scaleImage = function(mp) {
		var lastLen = Math.sqrt(Math.pow(this._currentTransform.ey - this._currentTransform.top, 2) +
	                            Math.pow(this._currentTransform.ex - this._currentTransform.left, 2));
	    var curLen = Math.sqrt(Math.pow(mp.ey - this._currentTransform.top, 2) +
	                           Math.pow(mp.ex - this._currentTransform.left, 2));

		if(this._currentTransform.scalex * (curLen / lastLen) > 0.2) {
	    	this._currentTransform.target.scalex = this._currentTransform.scalex * (curLen / lastLen);
	    	this._currentTransform.target.scaley = this._currentTransform.target.scalex;
	    }
    };

	/*Canvas.Element.prototype.updateSiblingsR = function() {
		var aw, ah, bw, bh, pw, ph;
		
		for (var i in this._currentTransform.target._Box._aImages) {
			this._currentTransform.target._Box._aImages[i].scalex = (this._currentTransform.target.scalex / this._savedState.scalex)*this._savedState.siblings[i].scalex;
			this._currentTransform.target._Box._aImages[i].scaley = (this._currentTransform.target.scalex / this._savedState.scalex)*this._savedState.siblings[i].scaley; 

			aw = this._savedState.siblings[i].left - this._currentTransform.target.left;
			ah = this._savedState.siblings[i].top - this._currentTransform.target.top;
			
			bw = this._savedState.scalex * this._currentTransform.target.width/2;
			bh = this._savedState.scalex * this._currentTransform.target.height/2;
			
			pw = (this._currentTransform.target.scalex - this._savedState.scalex) * this._currentTransform.target.width * 0.5;
			ph = (this._currentTransform.target.scalex - this._savedState.scalex) * this._currentTransform.target.height * 0.5;
			
			this._currentTransform.target._Box._aImages[i].left = this._savedState.siblings[i].left + aw/bw*pw;
			this._currentTransform.target._Box._aImages[i].top = this._savedState.siblings[i].top + ah/bh*ph;
		}
    };*/


	/**
   * Rotate image
   * @method rotateImage
   * @param e {Event} the mouse event
   */	
	Canvas.Element.prototype.rotateImage = function(mp) {
		var lastAngle = Math.atan2(this._currentTransform.ey - this._currentTransform.top,
		                           this._currentTransform.ex - this._currentTransform.left);
		var curAngle = Math.atan2(mp.ey - this._currentTransform.top,
		                          mp.ex - this._currentTransform.left);
				
		this._currentTransform.target.theta = (curAngle - lastAngle) + this._currentTransform.theta;
	};
	
	/**
   * Method to set the cursor image depending on where the user is hovering.
 	 * Note: very buggy in Opera
   * @method setCursor
   * @param e {Event} the mouse event
   * @param targetImg {Object} image that the mouse is hovering, if so.
   */
	Canvas.Element.prototype.setCursor = function(mp, targetImg) {
		if (!targetImg) {
			this._oElement.style.cursor = 'default';
			return;
		}
		
		if(targetImg.fixed) {
				this._oElement.style.cursor = 'default';
				return;
		}
		
		var corner = this.findTargetCorner(mp, targetImg);
		if (!corner) {
			var action = this.getAction(mp, targetImg);

			if (action) 
				this._oElement.style.cursor = 'default';
			else
				this._oElement.style.cursor = 'move';
		} 
		else {
			if(corner == 'tr') {
				this._oElement.style.cursor = 'ne-resize';
			}
			else if(corner == 'br') {
				this._oElement.style.cursor = 'se-resize';
			}
			else if(corner == 'bl') {
				this._oElement.style.cursor = 'sw-resize';
			}
			else if(corner == 'tl') {
				this._oElement.style.cursor = 'nw-resize';
			}									
			else {
				this._oElement.style.cursor = 'default';
			}
		}
	};
	
	/**
   * Method to add an image to the canvas.
   * It actually only pushes the images in an array that will be rendered later in the canvas.
   * @method addImage
   * @param oImg {Object} Image elment to attach
   */
	Canvas.Element.prototype.addImage = function(oImg, elementtype) {
		if(YAHOO.lang.isNull(this._aImages)) {
			this._aImages = [];
		}

		oImg.elementtype = elementtype;
		if(elementtype == 'cbox') 
			this._aImages.unshift(oImg);
		else
			this._aImages.push(oImg);
		
		this.renderAll(false);
	};

	Canvas.Element.prototype.duplicateItem = function(oImg, params) {
		if(YAHOO.lang.isNull(this._aImages)) {
			this._aImages = [];
		}
	
		newImg = new Canvas.Img(oImg._oElement, oImg._ID, params);

		newImg.elementtype = oImg.elementtype;
		
		this._aImages.push(newImg);
		
		this.renderAll(false);
	};

	Canvas.Element.prototype.delImage = function(oImg) {
		for(var i in this._aImages) {
			if(this._aImages[i] == oImg) {
				this._aImages.splice(i,1);
				break;
			}
		}	
		
		return true;
	};

	// Gets the zindex of the image
	Canvas.Element.prototype.getZindex = function(oImg) {
		for(var i=0; i<this._aImages.length; i++) {
			if(this._aImages[i]._oElement.id == oImg._oElement.id) {
				break;
			}
		} 
		return i;	
	};

	Canvas.Element.prototype.createBox = function(imgparams, ajaxcall, ID) {
		if(!ID)
			ID = 0;
		
		origelement = YAHOO.util.Dom.get('cbox');
		imgelement = origelement.cloneNode(false);
		
		origelement.parentNode.appendChild(imgelement);
		
		var img = new Canvas.Img(imgelement, 0, imgparams);	
		img.elementtype = "box";

		this.addImage(img, "box");
		var Box = new Canvas.Container(img, ID);
		this.addBox(Box);

		if(ajaxcall) {
			CanvasAjax.addCollection(Box);
		}

		img.setImageCoords();

		return Box;
	};

	Canvas.Element.prototype.deleteBox = function (Box, ajaxcall) {
		// Remove the html <img> element
		var el = document.getElementById(Box._Img._oElement.id);
		el.parentNode.removeChild(el);
		
		Box.delAllImages(ajaxcall);	
		
		if(ajaxcall)
			CanvasAjax.removeCollection(Box);
		
		this.delImage(Box._Img);		
		Box._Img = null;
		
		for(i in this._aBoxes) {
			if(this._aBoxes[i] == Box) {
				this._aBoxes.splice(i,1);
				break;
			}
		}
	};

	/*Canvas.Element.prototype.createCBox = function(src, tagparams, imgparams) {
		var cbox = new Image();
		cbox.src = src;
		
		// This attribute must be present, otherwise the element will not be treated as a cbox
		cbox.setAttribute('class', BMConfig.css_cbox);
		
		for(var property in tagparams) {
			cbox.setAttribute(property, tagparams[property]);
		}

		document.body.appendChild(cbox);
		
		var img = new Canvas.Img(cbox, 0, imgparams);
		this.addImage(img, "cbox");
	};*/

	/*Canvas.Element.prototype.turnIntoBox = function (oImg, ajaxcall) {
		oImg.elementtype = "box";
		oImg._oElement.className = BMConfig.css_box;
		
		oImg.scalex = 1;
		oImg.scaley = 1;
		oImg.theta = 0.001;			// XXX: Nasty Hack
		
		oImg.setImageCoords();

		var Box = new Canvas.Container(oImg, 0);			// ID = 0, must be updated with an AJAX Call
		this.addBox(Box, true);
		
		if(ajaxcall) {
			CanvasAjax.addCollection(Box);
		}
		
		return Box;
	};*/

	Canvas.Element.prototype.addBox = function(oBox) {
		if(YAHOO.lang.isNull(this._aBoxes)) {
			this._aBoxes = [];
		}
		
		this._aBoxes.push(oBox);
	};

	/**
   * Method to render both the top canvas and the secondary container canvas.
   * @method renderAll
   * @param allOnTop {Boolean} Whether we want to force all images to be rendered on the top canvas
   */	
	Canvas.Element.prototype.renderAll = function(allOnTop) {
		//console.log("rendering all %s", Math.random());
		if(this._aImages) {
			// when allOnTop equals true all images will be rendered in the top canvas.
			// This is used for actions like toDataUrl that needs to take some actions on a unique canvas.
			var containerCanvas = (allOnTop) ? this._oContextTop : this._oContextContainer;
			
			this._oContextTop.clearRect(0,0,parseInt(this._oConfig.width), parseInt(this._oConfig.height));
			containerCanvas.clearRect(0,0,parseInt(this._oConfig.width), parseInt(this._oConfig.height));
			
			if (allOnTop) {
				var originalImgSize = this._backgroundImg.getOriginalSize();
				this._oContextTop.drawImage(this._backgroundImg._oElement, 0, 0, originalImgSize.width, originalImgSize.height);
			}
	
			// If we're transforming a box, we want the siblings to be render on the top canvas as well
			if(this._currentTransform) {
				if(this._currentTransform.target.elementtype == "box") {
					for (var i = 0, l = this._aImages.length-1; i <= l; i += 1) {
						if((this._aImages[i]._inBox == this._currentTransform.target._Box) || (this._aImages[i] == this._currentTransform.target)) {
							this.drawImageElement(this._oContextTop, this._aImages[i]);
						} else { 
							this.drawImageElement(containerCanvas, this._aImages[i]);
						}
					}					
				} else {
					// we render the rest of images
					for (var i = 0, l = this._aImages.length-1; i < l; i += 1) {
						this.drawImageElement(containerCanvas, this._aImages[i]);
					}
			
					// we render the top context
					this.drawImageElement(this._oContextTop, this._aImages[this._aImages.length-1]);
				}
			} else {
				// we render the rest of images
				for (var i = 0, l = this._aImages.length-1; i < l; i += 1) {
					this.drawImageElement(containerCanvas, this._aImages[i]);
				}
		
				// we render the top context
				this.drawImageElement(this._oContextTop, this._aImages[this._aImages.length-1]);
			}
		}
	};
	
	/**
   * Method to render only the top canvas.
	 * Also used to render the group selection box.
   * @method renderTop
   */
	Canvas.Element.prototype.renderTop = function() {
		this._oContextTop.clearRect(0,0,parseInt(this._oConfig.width), parseInt(this._oConfig.height));

		if(this._currentTransform) {
			if(this._currentTransform.target.elementtype == "box") {
				this.drawImageElement(this._oContextTop, this._currentTransform.target);
				for (var i = 0, l = this._aImages.length-1; i <= l; i += 1) {
					if(this._aImages[i]._inBox == this._currentTransform.target._Box) {
						this.drawImageElement(this._oContextTop, this._aImages[i]);
					}
				}
			} else {
				this.drawImageElement(this._oContextTop, this._aImages[this._aImages.length-1]);
			}
		} else {
			this.drawImageElement(this._oContextTop, this._aImages[this._aImages.length-1]);
		}
		
		if (this._groupSelector != null) {
			this._oContextTop.fillStyle = "rgba(0, 0, 200, 0.5)";
			this._oContextTop.fillRect(this._groupSelector.ex - ((this._groupSelector.left > 0) ? 0 : - this._groupSelector.left), this._groupSelector.ey - ((this._groupSelector.top > 0) ? 0 : - this._groupSelector.top), Math.abs(this._groupSelector.left), Math.abs(this._groupSelector.top));
			this._oContextTop.strokeRect(this._groupSelector.ex - ((this._groupSelector.left > 0) ? 0 : Math.abs(this._groupSelector.left)), this._groupSelector.ey - ((this._groupSelector.top > 0) ? 0 : Math.abs(this._groupSelector.top)), Math.abs(this._groupSelector.left), Math.abs(this._groupSelector.top));
		}
	};
	
	/**
   * Method that finally uses the canvas function to render the image
   * @method drawImageElement
   * @param context {Object} canvas context where the image must be rendered
   * @param oImg {Object} the image object
   */
	Canvas.Element.prototype.drawImageElement = function(context, oImg) {
		var offsetY = oImg.height / 2;
		var offsetX = oImg.width / 2;

		//console.log(oImg);

		context.save();
		context.translate(oImg.left, oImg.top);
		context.rotate(oImg.theta);
		context.scale(oImg.scalex, oImg.scaley);

		if (oImg.elementtype == "video") {
			oImg.setVideoBorderVisibility(true);
			this.drawVideoBorder(context, oImg, offsetX, offsetY);
		} else if ((oImg.elementtype == "image") || (oImg.elementtype == "photoshow")) {
			oImg.setPolaroidVisibility(true);
			this.drawBorder(context, oImg, offsetX, offsetY);
		} 

		var originalImgSize = oImg.getOriginalSize();

		// drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
		// A = oImg.width - oImg._oElement.width = oImg.borderwidth (if any)
		// B = oImg.height - oImg._oElement.height = oImg.borderwidth + oImg.polaroidheight
		// B - A = oImg.polaroidheight
		var polaroidHeight = ((oImg.height - originalImgSize.height) - (oImg.width - originalImgSize.width))/2;

		context.drawImage(oImg._oElement,
					- originalImgSize.width/2,
					(- originalImgSize.height)/2 - polaroidHeight,
					originalImgSize.width,
					originalImgSize.height);
		
		/*if(oImg._oToolbar) {
			if (oImg.elementtype == "box") {
				context.drawImage(oImg._oToolbar, 
							- originalImgSize.width/2 + 9,
							(- originalImgSize.height)/2 + 9,
							oImg._oToolbar.width,
							oImg._oToolbar.height);
			} else {
				context.drawImage(oImg._oToolbar, 
								- originalImgSize.width/2,  
								(- originalImgSize.height)/2 - polaroidHeight, 
								oImg._oToolbar.width, 
								oImg._oToolbar.height);
			}
		}*/			

		context.restore();
	};
		
	/**
   * Method that returns an object with the image lines in it given the coordinates of the corners
   * @method _getImageLines
   * @param oCoords {Object} coordinates of the image corners
   */
	Canvas.Element.prototype._getImageLines = function(oCoords) {
		return {
			topline: { 
				o: oCoords.tl,
				d: oCoords.tr 
			},
			rightline: { 
				o: oCoords.tr,
				d: oCoords.br 
			},
			bottomline: { 
				o: oCoords.br,
				d: oCoords.bl 
			},
			leftline: { 
				o: oCoords.bl,
				d: oCoords.tl 
			}
		}
	};

	/**
   * Method that determines what picture are we clicking on
   * Applied one implementation of 'point inside polygon' algorithm
   * @method findTargetImage
   * @param e {Event} the mouse event
   * @param hovering {Boolean} whether or not we have the mouse button pressed
   */	
	Canvas.Element.prototype.findTargetImage = function(mp, hovering, exclude) {
		// http://www.geog.ubc.ca/courses/klink/gis.notes/ncgia/u32.html
		// http://idav.ucdavis.edu/~okreylos/TAship/Spring2000/PointInPolygon.html
		for (var i = this._aImages.length-1; i >= 0; i -= 1) {
			if(this._aImages[i] != exclude) {	
				// In case we want to exclude an image from the search
				// we iterate through each image. If target found then return target
				var iLines = this._getImageLines(this._aImages[i].oCoords);
				var xpoints = this._findCrossPoints(mp, iLines);
				
				// if xcount is odd then we clicked inside the image
				// For the specific case of square images xcount == 1 in all true cases
				if (xpoints % 2 == 1 && xpoints != 0) {
					var target = this._aImages[i];
					var siblings = [];
					
					//reorder array
					if (!hovering) {
						this._aImages.splice(i, 1);
						this._aImages.push(target);
					}
					
					return target;
				}
			}
		}
		return false;
	};

	/**
   * Helper method to determine how many cross points are between the 4 image edges
   * and the horizontal line determined by the position of our mouse when clicked on canvas
   * @method _findCrossPoints
   * @param ex {Number} x coordinate of the mouse
   * @param ey {Number} y coordinate of the mouse
   * @param oCoords {Object} Coordinates of the image being evaluated
   */		
	Canvas.Element.prototype._findCrossPoints = function(mp, oCoords) {
		var b1, b2, a1, a2, xi, yi;
		var xcount = 0;
		var iLine = null;
		for (lineKey in oCoords) {
			iLine = oCoords[lineKey];
			// optimisation 1: line below dot. no cross
			if ((iLine.o.y < mp.ey) && (iLine.d.y < mp.ey)) {
				continue;
			}
			// optimisation 2: line above dot. no cross
			if ((iLine.o.y >= mp.ey) && (iLine.d.y >= mp.ey)) {
				continue;
			}
			// optimisation 3: vertical line case
			if ((iLine.o.x == iLine.d.x) && (iLine.o.x >= mp.ex)) { 
				xi = iLine.o.x;
				yi = mp.ey;
			}
			// calculate the intersection point
			else {
				b1 = 0; //(y2-mp.ey)/(x2-mp.ex); 
				b2 = (iLine.d.y-iLine.o.y)/(iLine.d.x-iLine.o.x); 
				a1 = mp.ey-b1*mp.ex;
				a2 = iLine.o.y-b2*iLine.o.x;

				xi = - (a1-a2)/(b1-b2); 
				yi = a1+b1*xi; 
			}

			// dont count xi < mp.ex cases
			if (xi >= mp.ex) {
				xcount += 1;
			}

			// optimisation 4: specific for square images
			if (xcount == 2) {
				break;
			}
		}

		return xcount;
	};

	/**
   * Determine which one of the four corners has been clicked
   * @method findTargetCorner
   * @param e {Event} the mouse event
   * @param oImg {Object} the image object
   */
	Canvas.Element.prototype.findTargetCorner = function(mp, oImg) {
		var xpoints = null;
		
		for (var i in oImg.oCoords) {
			xpoints = this._findCrossPoints(mp, this._getImageLines(oImg.oCoords[i].corner));
			if (xpoints % 2 == 1 && xpoints != 0) {
				return i;
			}
		}
		
		return false;
	};

	Canvas.Element.prototype.getAction = function(mp, oImg) {
		var xpoints = null;
		
		//if ("console" in window) console.log(oImg);
		
		for (var i in oImg.buttons) {
			xpoints = this._findCrossPoints(mp, this._getImageLines(oImg.buttons[i].corners));
			if (xpoints % 2 == 1 && xpoints != 0) {
				return oImg.buttons[i].action;
			}
		}
		
		return false;
	}

	/**
   * Determine which one of the four corners has been clicked
   * @method findTargetCorner
   * @param e {Event} the mouse event
   * @param oImg {Object} the image object
   */
	Canvas.Element.prototype.findMousePosition = function(e) {
		// srcElement = IE
		//var parentNode = (e.srcElement) ? e.srcElement.parentNode : e.target.parentNode;
		
		//console.log(document.documentElement.scrollTop);
		
		var windowscroll = this.getScrollXY();
		
		return {
			ex: e.clientX + windowscroll.left - this._oElement.parentNode.parentNode.offsetLeft,
			ey: e.clientY + windowscroll.top - this._oElement.parentNode.parentNode.offsetTop
		};
	};

	Canvas.Element.prototype.getScrollXY = function() {
		var retobj = {};
		var scrOfX = 0, scrOfY = 0;
		if( typeof( window.pageYOffset ) == 'number' ) {
			//Netscape compliant
			scrOfY = window.pageYOffset;
			scrOfX = window.pageXOffset;
		} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
			//DOM compliant
			scrOfY = document.body.scrollTop;
			scrOfX = document.body.scrollLeft;
		} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
			//IE6 standards compliant mode
			scrOfY = document.documentElement.scrollTop;
			scrOfX = document.documentElement.scrollLeft;
		}
		
		retobj.top = scrOfY;
		retobj.left = scrOfX;
		
		return retobj;
	};

	/**
   * Draw image border, if any. That includes both normal border and polaroid border
   * @method drawBorder
   * @param context {Object} context (layer) where the border will be drawn
   * @param oImg {Object} the Image object
   * @param offsetX {Number} The horizontal offset applied from the (0,0) of the canvas axis
   * @param offsetY {Number} The vertical offset applied from the (0,0) of the canvas axis
   */	
	Canvas.Element.prototype.drawBorder = function(context, oImg, offsetX, offsetY) {
		var outlinewidth = 6;
		if(oImg.highlighted) 
			context.fillStyle = '#05597B';
		else
			context.fillStyle = 'rgba(0, 0, 0, .2)';
			
		context.fillRect(-6 - offsetX, -6 - offsetY, oImg.width + (2 * outlinewidth), oImg.height + (2 * outlinewidth));
		context.fillStyle = '#fff';
		context.fillRect(-offsetX, -offsetY, oImg.width, oImg.height);
	};

	 /**
	* Draw Video Border
	*/
	Canvas.Element.prototype.drawVideoBorder = function(context, oImg, offsetX, offsetY) {
		var outlinewidth = 6;
		
		if(oImg.highlighted) 
			context.fillStyle = '#05597B';
		else
			context.fillStyle = 'rgba(0, 0, 0, .2)';
		
		context.fillRect(-6 - offsetX, -6 - offsetY, oImg.width + (2 * outlinewidth), oImg.height + (2 * outlinewidth));
		context.fillStyle = '#000';
		context.fillRect(-offsetX, -offsetY, oImg.width, oImg.height);
		context.fillStyle = '#fff';
		
		for(var i = 4; i < 2 * offsetX; i = i+10) {
			context.fillRect(-2 - offsetX + i, -2 - offsetY + 4, (3 * 2), (3 * 2));
		}
		
		for(var i = 4; i < 2 * offsetX; i = i+10) {
			context.fillRect(-2 - offsetX + i, offsetY-8, (3 * 2), (3 * 2));
		}	
			
	};

	/**
   * Export the specific canvas element to an Image. Created and rendered on the browser.
   * Beware of crossbrowser support.
   * @method canvasTo
   * @param format {String} the format of the output image. Either jpeg or png.
   */	
	Canvas.Element.prototype.canvasTo = function(format) {
		this.renderAll(true);
		if (format == 'jpeg' || format == 'png') {
			return this._oElement.toDataURL('image/'+format);
		}
	};
}());
