function dynObj() {
	this.p = [ 0, 0 ]; // position
	this.v = [ 0, 0 ]; // speed
	this.a = [ 0, 0 ]; // acceleration
	this.m = 0; // mass
	this.boundingBox = [ 0, 0, 0, 0 ]; // bounding box
	this.index = -1;
	this.govFun = null;
}

function dynEvent() {
	this.eventType = null;
	this.startTime = 0;
	this.objectsTouched = []; // indexs of involved objects
	this.markForRemoval = false;
	this.isDerived = false; // Derived events, coming from others..
}

function dynEng() {

	this.dynObjsMap = new Object(); // Map of objects
	this.eventsQueue = [];
	this.numObjs = 0;
	this.lastTime = 0;
}
/**
 * Creates a new object and adds it to the manager
 * 
 * @return new dynamics object created
 */
dynEng.prototype.addObj = function() {
	var newObj = new dynObj();
	newObj.index = this.numObjs;
	this.numObjs++;
	this.dynObjsMap[this.numObjs] = newObj;
	return newObj;
};

/**
 * Adds an event to the event processing queue
 * 
 * @param evt
 * @return
 */
dynEng.prototype.setEvent = function(evt) {
	var i, sevt, inserted;
	inserted = false;
	for (i = 0; i < this.eventsQueue.length; i++) {
		sevt = this.eventsQueue[i];
		if (evt.startTime < sevt.startTime) {
			this.eventsQueue.unshift(evt);
			inserted = true;
			break;
		}
	}
	if (!inserted) {
		this.eventsQueue.push(evt);
	}
	this.purgeEvents();
};

/**
 * calculates objects trajectories and collisions till a specified time, will
 * also create events and push them in the event queue
 * 
 * @param targetTime
 * @return
 */
dynEng.prototype.calc = function(targetTime) {
	
	var closestCalculationTime = targetTime; //This'll be reduced everytime an event is created
	var lastInterval = targetTime - this.lastTime; 
	var currObj, px, py, vx, vy; 
	for (var k in this.dynObjsMap){
		currObj = this.dynObjsMap[k];
		px = currObj.p[0] + 0.5 * currObj.a[0] * Math.pow(lastInterval, 2) + currObj.v[0] * lastInterval;
		py = currObj.p[1] + 0.5 * currObj.a[1] * Math.pow(lastInterval, 2) + currObj.v[1] * lastInterval;
		vx = currObj.v[0] + currObj.a[0] * lastInterval;
		vy = currObj.v[1] + currObj.a[1] * lastInterval;
		currObj.p = [px, py];
		currObj.v = [vx, vy];
	}	
	this.lastTime = targetTime;
};

dynEng.prototype.calcEvent = function(event){
	
	if (event.eventType === "acc"){ //Accellerate
		var dax, day, tObj;		
		if (tObj === null){
			console.log("Error...");
		}else{
			var obj = this.dynObjsMap[tObj];
			obj.a[0] = event.dax + obj.a[0];
			obj.a[1] = event.day + obj.a[1];	
		}
	}else{
		console.log("Event error... unknown event.");
	}
};


dynEng.prototype.calcFuns = function(currTime){
	var currObj, currFun;
	for (var k in this.dynObjsMap){
		currObj = this.dynObjsMap[k];
		currFun = currObj.govFun;
		if (currFun!=null){
			currFun(currObj, currTime);
		}
	}	
};

/**
 * does the simulation for the specified timeLapse since the last calculation
 * 
 * @param timeLapse
 * @return
 */
dynEng.prototype.solveLapse = function(timeLapse) {
	var currObj, currEvnt;
	var curTime = this.lastTime + timeLapse;
	//calls a behavioural function
	this.calcFuns(curTime);
	// applies all the events (and solves the derived ones)
	while (this.eventsQueue.length > 0) {
		if (this.eventsQueue[0].startTime < curTime) {
			currEvnt = this.eventsQueue.pop();
		
			// Solves all the objects until the event time
			this.calc(currEvnt.startTime);
			// Then the event
			this.calcEvent(currEvnt);
			// If it was the last event
			if (this.eventsQueue.length == 0) {
				this.calc(curTime); // This may generate other events
			}
		} else {
			break; // The events are too far in the future
		}
	}

	this.calc(curTime); // This may generate other events
};

/**
 * Handles events and removes derived events that won't ever take place
 * 
 * @return
 */
dynEng.prototype.purgeEvents = function() {
	var i, j, k, z, fEvt, sEvt, objsA, objsB, currA, currB;
	var removeSome = false;
	var removeArr = [];
	for (i = 0; i < (this.eventsQueue.length - 1); i++) {
		fEvt = this.eventsQueue[i];
		objsA = fEvt.objectsTouched;
		for (j = 0; j < objsA.length; j++) {
			var currA = objsA[j]; // element of this event
			for (k = i + 1; k < this.eventsQueue.length; k++) {
				sEvt = this.eventsQueue[k];
				if ((sEvt.isDerived) && (sEvt.objectsTouched.length > 0)) {
					objsB = sEvt.objectsTouched;
					for (z = 0; z < objsB.length; z++) {
						currB = objsB[z];
						if (currB === currA) {
							sEvt.markForRemoval = true;
							removeSome = true;
							removeArr.push(k);
							break;
						}
					}
				}
			}
			if (removeSome) {
				removeSome = false;
				for (k = 0; k < removeArr.length; k++) {
					this.eventsQueue.splice(removeArr[k], 1);
				}
				removeArr = [];
			}
		}
	}
};
