/* Oggetto che gestisce lavori in background simulando (molto limitatamente) il 
comportamento dei thread*/

/**
 *  Funzionamento: si imposta una funzione che verrà chiamata con certe modalità in maniera asincrona.
 *  La signature della funzione deve essere:
 *  
 *  	function fun(oggettoDiStato, BackgroundWorker){
 *  		...
 *  	}
 *  
 *  - oggettoDiStato è un oggetto che viene mantenuto fra una chiamata e l'altra. Si può usare a piacimento
 *  - BackgroundWorker è l'istanza che controlla la stessa funzione.
 *  
 *  
 *  La funzione si aggancia con il metodo BackgroundWorker.setWorker(funName, FunObject, StateObject)
 *  
 *  E' importante che la funzione sia breve e non bloccante. 
 *  
 *  Usi: animazioni, lunghi task...
 */

function bind(fun, obj) {
	if (obj !== null) {
		return function() {
			fun.apply(obj);
		};
	} else {
		return fun;
	}
}

function BackgroundWorker() {

	this.workerFun = null;
	this.workerFunObject = null;
	this.stateObject = null;
	this.isThreadLike = true;
	this.interval = -1;
	this.startedAt = null;
	this.isStopped = true;
	this.timeoutStartTime = null;
	this.callBack = callBack;
	this.getMillis = getMillis;

	function getMillis(DateObj) {
		return DateObj.getMilliseconds()
		+ 1000
		* (DateObj.getSeconds() + 60 * (DateObj.getMinutes() + 60 * (DateObj
				.getHours())));
	}

	function callBack() {
		if (!this.isStopped) {

			/*Prima esegue la funzione*/
			var fn = this.workerFun;

			if ((this.workerFunObject !== undefined)&&(this.workerFunObject !== null)){
				fn.apply(this.workerFunObject, [this.stateObject, this]);
			}else{			
				fn(this.stateObject, this); // <-------- Chiamata alla funzione
			}
			/*Reimposta la chiamata*/

			if (!this.isThreadLike) {
				var time = new Date();
				var curTime = getMillis(time);
				var oldTime = getMillis(this.timeoutStartTime);
				var delta = curTime - oldTime;
				this.timeoutStartTime = new Date();
				if (delta < this.interval) {
					var remain = this.interval - delta;
					setTimeout(bind(this.callBack, this), this.interval);
				} else {
					setTimeout(bind(this.callBack, this), this.interval);
				}
			} else {
				setTimeout(bind(this.callBack, this), 1);
			}
		}
	}

}

BackgroundWorker.prototype.getMillisFromStart = function() {
	var curTime = new Date();
	curTime = this.getMillis(curTime);
	var oldMillis = this.getMillis(this.startedAt);
	return curTime - oldMillis;
};

/**
 *  funName - funzione che verrà chiamata in maniera ricorrente
 *  FunObject - oggetto di cui la funzione è membro (opzionale, mettere null in caso non c'è)
 *  StateObject - oggetto di stato (anche questo opzionale, mettere null se non serve).
 */
BackgroundWorker.prototype.setWorker = function(funName, FunObject, StateObject) {
	this.workerFun = funName;
	this.workerFunObject = FunObject;
	this.stateObject = StateObject;

};

/**
 *  Comincia a chiamare la funzione a intervalli il più breve possibili (con setTimeout a 1 millisec).
 */
BackgroundWorker.prototype.start = function() {
	if (!this.isStopped){
		this.resetTimer();
		return;
	}
	this.isStopped = false;
	this.startedAt = new Date();
	setTimeout(bind(this.callBack, this), 1);
};

/**
 *  Chiama la funzione a intervalli come minimo lunghi interval millisecondi
 */
BackgroundWorker.prototype.startTimed = function(interval) {
	if (!this.isStopped){
		this.resetTimer();
		return;
	}
	this.isThreadLike = false;
	this.interval = interval;
	this.startedAt = new Date();
	this.isStopped = false;
	this.timeoutStartTime = new Date();
	setTimeout(bind(this.callBack, this), interval);
};

BackgroundWorker.prototype.resetTimer = function(){
	this.startedAt = new Date();
}

/**
 *  Blocca la chiamata della funzione.
 */
BackgroundWorker.prototype.stop = function() {
	this.isStopped = true;
};


/**
 * Funziona in maniera simile al Monitor. Solo che non interrompe le funzioni, ma permette di specificare
 * le callback da richiamare quando si sblocca.
 * 
 */
function Synchronizer(){
	
	this.objectLocked = false;
	this.objectQueue = new Array();
	
}



/**
 * blocca l'oggetto. 
 * @return torna true se effettivamente si è ottenuto il lock, altrimenti false. 
 * 
 * Ricordarsi di usare una delle signal
 */
Synchronizer.prototype.lock = function(){
	if (this.objectLocked)
		return false;
	this.objectLocked = true;
};

/**
 * Se l'oggetto è bloccato, tornerà false, altrimenti true. Nel caso di blocco metterà
 * la funzione callback nella coda interna e la chiamerà sullo sblocco. SE LA WAIT RITORNA
 * TRUE, AQUISIRA' IL LOCK. SE TORNA FALSE LO AQUISIRA' AL MOMENTO DELLA CHIAMATA DELLA CALLBACK.
 * 
 * @param callback
 * La callback che sarà chiamata quando l'oggetto si sbloccherà. Quando la funzione torna
 * false, la callback viene inserita in una coda interna, dalla quale poi saranno chiamate 
 * le funzioni.
 * 
 * @param callObj (opzionale) l'oggetto di cui è membro la funzione
 * @param arg (opzionale) argomento da passare alla funzione
 * 
 * LA FUNZIONE DEVE ESSERE DEL TIPO:
 * 
 * 	function funzione(oggettoDiLock, arg)
 * 
 * dove oggetto di lock è l'istanza dell'oggetto di lock che si sta utilizzando
 * Se la funzione è membro di un oggetto, metterlo in callObj, altrimenti null
 *   
 */
Synchronizer.prototype.wait = function(callback, callObj, arg){
	if (!this.objectLocked){
		this.objectLocked = true;
		return true;
	}
	if ((callObj !== undefined) && (callObj !== null)){
		var fun = {'fun':bind(callback, callObj), 'arg': arg };
	}else{
		var fun = {'fun':callback, 'arg':arg};
	}
	this.objectQueue.push(fun);
	return false;	
};

/**
 *  Sveglia la prima funzione bloccata e gli fornisce il lock 
 */
Synchronizer.prototype.signal = function(){	
	if (this.objectQueue.length > 0){
		var fun = this.objectQueue.pop();
			fun['fun'](this, fun['arg']); //<---
	}else{
		this.objectLocked = false;
	}
		
};





