/**
 * dogma Library - Physical Simulation Interface
 *
 * Diese Bibliothek simuliert für kleine Objekte Schwerkraft und
 * Beschleunigungen.
 *
 * @author Steffen Müller
 * @version 0.1
 */

// dVector ////////////////////////////////////////////////////////////////////

function dVector ()
{
	this.x = 0;
	this.y = 0;
	this.z = 0;
}

dVector.prototype.normalize = function ()
{
	var l = this.length();
	if (!l) return;

	this.x /= l;
	this.y /= l;
	this.z /= l;
}

dVector.prototype.length = function ()
{
	return Math.sqrt(this.x*this.x + this.y*this.y + this.z*this.z);
}

dVector.prototype.add = function (ov)
{
	var res = new dVector();
	res.x = this.x + ov.x;
	res.y = this.y + ov.y;
	res.z = this.z + ov.z;
	return res;
}

dVector.prototype.sub = function (ov)
{
	var res = new dVector();
	res.x = this.x - ov.x;
	res.y = this.y - ov.y;
	res.z = this.z - ov.z;
	return res;
}

dVector.prototype.set = function (ov)
{
	this.x = ov.x;
	this.y = ov.y;
	this.z = ov.z;
}

dVector.prototype.scalarMult = function (factor)
{
	this.x *= factor;
	this.y *= factor;
	this.z *= factor;
}
// dPhysics ///////////////////////////////////////////////////////////////////

function dPhysics (divid)
{
	this.window = document.getElementById(divid);
	if (!this.window)
	{
		dError("dPhysics: could not find div with id '"+divid+"'");
		return;
	}

	// Konstanten
	this.PASSIVE = 1;
	this.ACTIVE = 2;
	this.STATIC = 3;

	// Welten-Setup
	this.frames = Array();
	this.listeners = Array();
	this.actors = Array();
	this.zfactor = 5;
	this.gravity = new dVector ();
	this.gravity.x = 0;
	this.gravity.y = .7;
	this.gravity.z = 0;
	this.active_treshold = 30;
	this.active_actor = null;
	this.active_damper = .2;
	this.framediff = 10;

	// Windowgröße
	this.win_top = dGetPosY(this.window);
	this.win_bottom = this.win_top + dGetNumericStyle(this.window, 'height');
	this.win_left = dGetPosX(this.window);
	this.win_right = this.win_left + dGetNumericStyle(this.window, 'width');

	// Verbindung mit der Außenwelt
	document.onmousemove = this.documentMouseMove;
	document.onmouseup = this.documentMouseUp;

	this.frameListener = window.setInterval(this.doFrame, 10);
	document.physics = this;
}

dPhysics.prototype.addListener = function (cause, code)
{
	this.listeners[cause] = code;
}

dPhysics.prototype.callListener = function (cause, target)
{
	this.listener_target = target;
	if (this.listeners[cause])
	{
		eval(this.listeners[cause]);
	}
}

dPhysics.prototype.addFrame = function (name, img, w, h, ox, oy)
{
	this.frames[name] = Array();
	this.frames[name]['img'] = img;
	this.frames[name]['width'] = w;
	this.frames[name]['height'] = h;
	this.frames[name]['offx'] = ox;
	this.frames[name]['offy'] = oy;
}

dPhysics.prototype.setFrame = function (a, name)
{
	if (!this.frames[name]) return;

	a.src = this.frames[name]['img'];
	a.style["width"] = this.frames[name]['width']+"px";
	a.style["height"] = this.frames[name]['height']+"px";
	a.dim.x = this.frames[name]['width'];
	a.dim.y = this.frames[name]['height'];
	a.pos.x += this.frames[name]['offx'];
	a.pos.y += this.frames[name]['offy'];
	if (a.pInit)
		this.setPos(a);
}

dPhysics.prototype.addActor = function (frame, x, y, z)
{
	// Koordinaten Fenster-relativ machen
	x += this.win_top;
	y += this.win_left;

	// Positionieren und erstellen
	var a = document.createElement('img');
	a.pos = new dVector();
	a.pos.x = x;
	a.pos.y = y;
	a.pos.z = z;
	a.acc = new dVector();
	a.dim = new dVector();
	a.dim.z = 1;
	a.lastPos = new dVector();
	a.pCounter = 0;
	this.setFrame(a, frame);
	a.pInit = true;

	// Eigenschaften
	a.dStatus = this.PASSIVE;
	a.bounceY = 0.5;
	a.bounceX = 0.8;
	this.setPos(a);

	// Events
	a.onmousedown = this.actorMouseDown;

	// Style und endgültige Position
	a.style["position"] = "absolute";
	this.window.appendChild(a);
	this.actors.push(a);
	return a;
}

dPhysics.prototype.doFrame = function ()
{
	var p = document.physics;

	for (var i = 0; i < p.actors.length; i++)
	{
		var a = p.actors[i];
		if (a.dStatus == p.PASSIVE)
		{
			// Gravity simulieren
			if (!a.onground)
				a.acc = a.acc.add(p.gravity);
			a.pos = a.pos.add(a.acc);
			p.setPos(a);
		}
		else if (a.dStatus == p.ACTIVE)
		{
			a.pCounter++;
			if (a.pCounter >= p.framediff)
			{
				a.acc = a.pos.sub(a.lastPos);
				a.acc.scalarMult(p.active_damper);
				a.lastPos.set(a.pos);
				a.pCounter = 0;
			}
		}
	}
}

dPhysics.prototype.setPos = function (a)
{
	// Ground ermitteln
	a.groundy = this.win_bottom - a.pos.z*this.zfactor - a.dim.y;

	// Irgendwo durchgeschlagen?
	if (a.pos.y < this.win_top)
	{
		a.pos.y = this.win_top;
		a.acc.y = (-1)*a.acc.y*a.bounceY;
		this.callListener("crash", a);
	}
	if (a.pos.y > a.groundy)
	{
		a.pos.y = a.groundy;
		if (a.onground == false) this.callListener("touchdown", a);

		a.onground = true;
		a.acc.y = (-1)*a.acc.y*a.bounceY;
		if (a.acc.y > -2)
			a.acc.y = 0;
		else
			this.callListener("crash", a);
	}
	else if (a.pos.y < a.groundy - 5)
	{
		if (a.onground == true) this.callListener("takeoff", a);
		a.onground = false;
	}

	if (a.onground)
		a.acc.x *= a.bounceX;

	if (a.pos.x <= this.win_left)
	{
		a.pos.x = this.win_left;
		a.acc.x = (-1)*a.acc.x*a.bounceY;
		this.callListener("crash", a);
		this.callListener("crashleft", a);
	}
	if (a.pos.x >= this.win_right-a.dim.x)
	{
		a.pos.x = this.win_right-a.dim.x;
		a.acc.x = (-1)*a.acc.x*a.bounceY;
		this.callListener("crash", a);
		this.callListener("crashright", a);
	}

	if (a.dStatus == this.ACTIVE)
	{
		// In Benutzerhand - keine Beschleunigung
		a.acc.x = a.acc.y = a.acc.z = 0;
	}
	// Position verändern
	a.style["top"] = a.pos.y+"px";
	a.style["left"] = a.pos.x+"px";
}

dPhysics.prototype.actorMouseDown = function (event)
{
	var p = document.physics;
	event = dGetEvent(event);
	var target = dGetTarget(event);

	if (target.dStatus == p.STATIC) return false;
	if (p.active_actor) return false;
	p.active_actor = target;
	target.dStatus = p.ACTIVE;
	target.acc.x = target.acc.y = target.acc.z = 0;
	target.lastPos.set (target.pos);

	p.mouseoffsetx = event.clientX - dGetNumericStyle(target, "left");
	p.mouseoffsety = event.clientY - dGetNumericStyle(target, "top");

	p.callListener('mousedown', target);
	return false;
}


dPhysics.prototype.documentMouseMove = function (event)
{
	var p = document.physics;
	var event = dGetEvent (event);
	var target = p.active_actor;
	if (target && target.pos)
	{
		target.pos.x = event.clientX-p.mouseoffsetx;
		target.pos.y = event.clientY-p.mouseoffsety;
		p.setPos(target);
		p.callListener('mousemove', target);
		return false;
	}
	return true;
}

dPhysics.prototype.documentMouseUp = function (event)
{
	var p = document.physics;
	if (p.active_actor)
	{
		var a = p.active_actor;
		p.active_actor = null;
		a.dStatus = p.PASSIVE;
		a.acc = a.pos.sub(a.lastPos);
		a.acc.scalarMult(p.active_damper);
		a.lastPos.set(a.pos);
		a.pCounter = 0;
		p.callListener('mouseup', a);
	}
}

