/*
 * Javascript/Canvas Textured 3D Renderer v0.3
 * Copyright (c) 2008 Jacob Seidelin, cupboy@gmail.com
 * This software is free to use for non-commercial purposes. For anything else, please contact the author.
 */

var Canvas3D = {};

(function() {

Canvas3D.addEvent = function(oObject, strEvent, fncAction) {
	if (oObject.addEventListener) { 
		oObject.addEventListener(strEvent, fncAction, false); 
	} else if (oObject.attachEvent) { 
		oObject.attachEvent("on" + strEvent, fncAction); 
	}
}

Canvas3D.Scene = function(oContainer, iWidth, iHeight) {

	this._oContainer = oContainer;


	this._iWidth = iWidth;
	this._iHeight = iHeight;

	this._oActiveCamera = null;

	this._aObjects = [];

	this._bRunning = false;

	this._bMouseRotate = true;
	this._bMouseRotateY = true;
	this._bMouseRotateX = true;

	this._oUpVector = new Canvas3D.Vec3(0,1,0);

	this._oAmbientLight = {r:70,g:70,b:70};

	this._bDrawLights = false;
	this._aLights = [];

	var me = this;

	this._iMaxZ = 10000000;

	var oInputOverlay = document.createElement("span");
	oInputOverlay.style.width = this._iWidth+"px";
	oInputOverlay.style.height = this._iHeight+"px";
	oInputOverlay.style.zIndex = this._iMaxZ + 1;
	//oInputOverlay.style.backgroundColor = "#ff0000";
	oInputOverlay.style.position = "absolute";
	this._oContainer.appendChild(oInputOverlay);

	Canvas3D.addEvent(oInputOverlay, "selectstart", function() {return false;});

	this._bIsRotating = false;

	Canvas3D.addEvent(oInputOverlay, "mousedown", 
		function(e) {
			e = e || window.event;
			me._iMouseDownX = e.clientX;
			me._iMouseDownY = e.clientY;
			me._bMouseIsDown = true;
		}
	);

	Canvas3D.addEvent(oInputOverlay, "mouseup", 
		function(e) {
			e = e || window.event;
			me._bMouseIsDown = false;
			if (me._bIsRotating) {
				me._bIsRotating = false;
				me.setDirty(true);
			}
		}
	);

	Canvas3D.addEvent(oInputOverlay, "mousemove",
		function(e) {
			e = e || window.event;
			if (me._bMouseRotate) {
				if (me._bMouseIsDown) {
					me._bIsRotating = true;

					var iMouseX = e.clientX;
					var iMouseY = e.clientY;
					var fDeltaX = (iMouseX - me._iMouseDownX) / 3;
					var fDeltaY = -((iMouseY - me._iMouseDownY) / 3);
					me.rotateCamera(fDeltaX, fDeltaY);
					me._iMouseDownX = e.clientX;
					me._iMouseDownY = e.clientY;
				}
			}
		}
	);


	Canvas3D.addEvent(document, "keydown",
		function(e) {
			e = e || window.event;
			var iKeyCode = e.keyCode;
			var oCam;
			if (iKeyCode == 87) { // "w"
				if (oCam = me.getActiveCamera()) {
					var oTarget = oCam.getLookAt();
					var fDist = oTarget.dist(oCam.getPosition());
					var fMove = 50;
					if (fDist - fMove < 40) {
						fMove = fDist - 40;
					}
					oCam.moveForward(fMove);

				}
			}
			if (iKeyCode == 83) { // "s"
				if (oCam = me.getActiveCamera()) {
					//oCam.setScale(oCam.getScale() / 1.5);
					oCam.moveForward(-50);
					//oCam.lookAt(oCam.getLookAt(), me.getUpVector());

				}
			}
		}
	);

}

Canvas3D.Scene.prototype.rotateCamera = function(fX, fY)
{
	var oCam = this.getActiveCamera();

	// pitch the camera, but only if we're not too low or if we're moving the camera up
	//if ((oCam.getPosition().y > 15 || fY < 0) && (oCam.getPosition().y < 50 || fY > 0)) 
		oCam.pitchAroundTarget(fY);

	oCam.yawAroundTarget(fX);


	oCam.lookAt(oCam.getLookAt(), this.getUpVector());
	oCam.updateRotationMatrix();
}


Canvas3D.Scene.prototype.setUpVector = function(oVec)
{
	this._oUpVector = oVec;
}

Canvas3D.Scene.prototype.getUpVector = function()
{
	return this._oUpVector;
}

Canvas3D.Scene.prototype.getAmbientLight = function()
{
	return this._oAmbientLight;
}

Canvas3D.Scene.prototype.zoomCamera = function(fZoom)
{
	this.getActiveCamera().moveForward(fZoom);
}

Canvas3D.Scene.prototype.getObjects = function()
{
	return this._aObjects;
}

Canvas3D.Scene.prototype.addObject = function(obj)
{
	var oObjectCanvas = document.createElement("canvas");
	if (!oObjectCanvas.getContext) return;

	var oObjectContext = oObjectCanvas.getContext("2d");

	Canvas3D.addEvent(oObjectCanvas, "selectstart", function() {return false;});

	oObjectCanvas.width = this._iWidth;
	oObjectCanvas.height = this._iHeight;

	oObjectCanvas.style.width = this._iWidth+"px";
	oObjectCanvas.style.height = this._iHeight+"px";

	oObjectCanvas.style.position = "absolute";

	this._oContainer.appendChild(oObjectCanvas);

	this._aObjects.push(
		{
			canvas : oObjectCanvas,
			context : oObjectContext,
			object : obj
			
		}
	);

	obj.setScene(this);

	this.setDirty(true);
}

Canvas3D.Scene.prototype.addLight = function(oLight)
{
	oLight.setScene(this);
	return this._aLights.push(oLight);
}

Canvas3D.Scene.prototype.getLights = function()
{
	return this._aLights;
}


Canvas3D.Scene.prototype.clearObjects = function()
{
	this._aObjects = [];
}


Canvas3D.Scene.prototype.setActiveCamera = function(oCam)
{
	this._oActiveCamera = oCam;
}

Canvas3D.Scene.prototype.getActiveCamera = function()
{
	return this._oActiveCamera;
}

Canvas3D.Scene.prototype.begin = function()
{
	this._bRunning = true;
	this.getActiveCamera().setDirty(true);
	this.drawAll();
	var me = this;
	this._iInterval = setInterval(function() { me.drawAll(); }, 50);
}

Canvas3D.Scene.prototype.end = function()
{
	this._bRunning = false;
	clearInterval(this._iInterval);
}

Canvas3D.Scene.prototype.setDirty = function(bDirty)
{
	this._bDirty = bDirty;
}

Canvas3D.Scene.prototype.getDirty = function()
{
	return this._bDirty;
}

Canvas3D.Scene.prototype.drawAll = function()
{
	if (!this._bRunning) return;

	var oCam = this.getActiveCamera();

	var iOffsetX = Math.floor(this._iWidth / 2);
	var iOffsetY = Math.floor(this._iHeight / 2);

	var aObjects = this._aObjects;
	var bCamDirty = oCam.getDirty();


	for (var c=0;c<aObjects.length;c++) {
		var oObject = aObjects[c];
		if (bCamDirty || this.getDirty() || oObject.object.getDirty()) {

			if (oObject.object.getForcedZ() > -1) {
				oObject.canvas.style.zIndex = oObject.object.getForcedZ();
			} else {
				var oObjectPos = oCam.transformPoint(oObject.object.getPosition());
				oObject.canvas.style.zIndex = 10000000 - Math.round(oObjectPos.z*100);
			}
			oObject.context.clearRect(0,0,this._iWidth,this._iHeight);

			if (!(this._bIsRotating && oObject.object.getHideWhenRotating())) {
				oObject.object.draw(oObject.context, iOffsetX, iOffsetY);
			}
		}

		if (this._bDrawLights) {
			for (var c=0;c<this._aLights.length;c++) {
				var oLight = this._aLights[c];
				oLight.draw(this._oLightContext, iOffsetX, iOffsetY);
			}
		}
	}
	oCam.setDirty(false);
	this.setDirty(false);

}

})();
