/* batch_module.js
 * A Gears worker that is kicked off once and then runs continuously in the background 
 * in support of the Akinity application.
 * It coordinates processing a batch of conception transactions, which are input as a linked list of (relative) pseudo IDs.
 * It can transmit results to the caller either not at all, piecemeal or just the final ID.  
 * This module is called by the UI. It calls db, synthesis and meiosis modules. 
 * The module is stateless. All paramemters for every trasaction are encoded in the mv object passed in or passed back to it from a called module.
 */

function batch_module(wp) {
/* constructor
 * 
 */
	return this.run_ (wp); // comment: kick off worker
}

batch_module.prototype.run_ = 						function(wp){
	/* the code to run on the worker  
	 */
	try {
		var batch_wkr_Script = 
		'google.gears.workerPool.onmessage = ' + String(this.batchWorker) 					+';'+
		'var module 						=  "batch"; ' 									+	// module global
		'var main 							= ' +	String(this.main) 						+';'+
		'var get_batch_id 					= ' +	String(this.get_batch_id) 				+';'+
		'var get_bi_seq 					= ' +	String(this.get_bi_seq) 				+';'+
		'var get_count_bi 					= ' +	String(this.get_count_bi) 				+';'+
		'var execute_instruction 			= ' +	String(this.execute_instruction) 		+';'+
		'var insert_batch			 		= ' +	String(this.insert_batch) 				+';'+
		'var insert_batch_instructions 		= ' +	String(this.insert_batch_instructions) 	+';'+
		'var update_batch_instruction 		= ' +	String(this.update_batch_instruction) 	+';'+
		'var update_batch 					= ' +	String(this.update_batch) 				+';'+
		'var getFlickrSimTags 				= ' +	String(this.getFlickrSimTags) 			+';'+
		'var synthesis 						= ' +	String(this.synthesis) 					+';'+
		'var meiosis 						= ' +	String(this.meiosis) 					+';'+
		'var trx_caller 					= ' +	String(this.trx_caller) 				+';'+
// module shared code in mod_lib.js
		'var lib_include 					= ' +	String(lib_include) 					+';'+
		'var lib_set_mod_state 				= ' +	String(lib_set_mod_state) 				+';'+
		'var lib_debug	 					= ' + 	String(lib_debug) 						+';';

		var BatchWorkerId = wp.createWorker(batch_wkr_Script); 
		return {ID: BatchWorkerId};
	}
	catch(e){

	}
}   

batch_module.prototype.batchWorker = function(a, b, message){
	lib_include("http://ingendex.akinity.info/third_party/closure/closure/goog/base.js", main(message));
}

batch_module.prototype.main = 				function(message) {

	var wp = google.gears.workerPool;
	var db = google.gears.factory.create('beta.database');
	var mv;

	var modparent 			= message.sender;	// module static 
	var synthScript 		= message.body[1];	// module static 
	var wkrthrottle			= message.body[2];	// module static 
	var batch_instructions 	= [];				// global

	try {
		// comment: pull state from caller
		mv = lib_set_mod_state (message, modparent, synthScript, wkrthrottle);
// 		turn on debug here for incoming message only. Later turn off because debug upsets input parameters 
		lib_debug (mv.ba, mv.from.action, mv.from.status, 'batch_module returning '+mv.dest.module+'.'+mv.from.module+'.'+mv.from.action+'.'+mv.from.status)

		if (mv.dest.module !== 'batch') throw ("ba: invalid module "+mv.dest.module+"."+mv.dest.action+"."+mv.dest.status+"."+mv.dest.message+"."+mv.from.module);
		switch (mv.from.status) {
			case 'success':
				var batch = mv.ba;
				switch (mv.from.module) {
					case 'db':
						mv = lib_set_mod_state(message, modparent, synthScript, wkrthrottle);
						lib_debug (mv.ba, mv.from.action, mv.from.status, 'return from db');
						switch (mv.from.action) {
							case 'insert_batch':
								insert_batch_instructions(wp, mv);
								lib_debug (mv.ba, mv.from.action, mv.from.status, 'done insert_batch_instructions');
							break;
							case 'insert_batch_instructions':
								if (batch.execute_flag === '1') {
									execute_instruction(wp, mv, batch, 0);
//									lib_debug (mv.ba, mv.from.action, mv.from.status, 'done execute bi ' +batch.id+'.'+ batch.bi.seq+'.'+wkrthrottle);
								}
							break;
							case 'update_batch_instruction': // finished one instruction
								batch.bi.seq++;
								lib_debug (mv.ba, mv.from.action, mv.from.status, ' test bi ' +batch.id+'.'+ batch.bi.seq)
								if ((batch.bi.seq < get_count_bi(wp, mv, batch.id)) && batch.execute_flag === '1') { // is there a next one ?
									execute_instruction(wp, mv, batch, batch.bi.seq);
									lib_debug (mv.ba, mv.from.action, mv.from.status, ' executed bi ' +batch.id+'.'+ batch.bi.seq);
								}
								else update_batch (wp, mv);	// finished all instructions. set batch inactive						 					
/*
								mv.dest.module = 'UI';
								mv.from.action = 'execute_bi';
								mv.dest.status = 'success';
								trx_caller(wp, mv, null, mv.dest.status, mv.init.parent); // notify UI of new binding 
								lib_debug (mv.ba, mv.from.action, mv.from.status, 'requested update_batch_instruction '+mv.ba.bi.z_binding_id)

*/							break;
							case 'update_batch':
								lib_debug (mv.ba, mv.from.action, mv.from.status, ' updated batch ');
							break;
//							default: throw ('invalid action. Module : ' + mv.from.module + ' Action : ' + mv.from.action);
						}
					break;
					case 'UI':
						if (mv.dest.action === 'initiate') insert_batch(wp, mv);
					break;
					case 'synthesis':
						lib_debug (mv.ba, mv.from.action, mv.from.status, 'batch: update_batch_instruction '+batch.id+' new binding '+mv.sy.binding_id);		 		
						update_batch_instruction(wp, mv, mv.sy.binding_id);
					break;
					case 'meiosis':
						lib_debug (mv.ba, mv.from.action, mv.from.status, 'meiosis: update_batch_instruction '+batch.id+' new binding '+mv.me.Z.binding_id);		 		
						update_batch_instruction(wp, mv, mv.me.Z.binding_id);
					break;
//				default: throw ('invalid Module : ' + mv.from.module + ' Action : ' + mv.from.action); 
				}
//			default: throw( 'improper return status: ' + mv.from.status);		
			}
			
	// comment: handle errors from this worker
	wp.onerror = function( err ){
		lib_debug (mv.ba, mv.from.action, mv.from.status,'error in batch '+error.message);
//		trx_caller (wp, mv, err.message, 'error', mv.init.parent);
		};
	} 
	catch (e) { lib_debug (mv.ba, 'batchWorker', 'exception', e); }
	finally {
		delete wp;
		delete db;
	}
};

batch_module.prototype.get_batch_id 			= 	function(wp, mv, batch_id, active_flag) {
// retrieve single batch by its primary key - batch.id
//	lib_debug (mv.ba, mv.from.action, mv.from.status, 'get_batch_id '+batch_id);	
	var dbgb 		= google.gears.factory.create('beta.database');
	var rsgb; 
	try {
		dbgb.open(mv.cu.name);

		var select_string = 'select id, def_breadth, max_depth, version, mos_breadth, max_breadth, dbwID, sywID, mewID, wkrthrottle ';
		rsgb = dbgb.execute(select_string + ' from '+active_flag ? 'active_batch' : 'batch' +' where id = ? ', [batch_id]);
		
		if (rsgb.isValidRow()) {
			var batch = {
				id 				: rsgb.field(0),
				def_breadth		: rsgb.field(1),
				max_depth		: rsgb.field(2),
				version			: rsgb.field(3),
				mos_breadth		: rsgb.field(4),
				max_breadth		: rsgb.field(5),
				dbwID			: rsgb.field(6),
				sywID			: rsgb.field(7),
				mewID			: rsgb.field(8),
				wkrthrottle		: rsgb.field(9)
			};	
			return batch;
		 }
		 else {
		 	return false; 
		 };
	} 
	catch (e) { lib_debug (mv.ba, 'get_batch_id', 'exception', e); }
	finally {
		rsgb.close();	
		dbgb.close();		
	}
};

batch_module.prototype.get_bi_seq 				= 	function(wp, mv, batch_id, seq) {
/* get batch_instructions on ID  
 */
//	lib_debug (mv.ba, mv.from.action, mv.from.status, 'get_bi_seq '+batch_id+' ' +seq);
	
	var dbgbi 		= google.gears.factory.create('beta.database');
	var rsgbi; 

	try {
		dbgbi.open(mv.cu.name);
		var select_string = 	
		' select bi.pid, bi.z_binding_id, bi.z_description, bi.conc_type, bi.x_pid, bi.x_binding_id, bi.organelle, bi.y_pid, bi.y_binding_id, bi.breadth, bi.depth '+
		' from batch_instruction bi, active_batch ab where bi.id = ? and bi.seq = ? and ab.id = bi.id ';					
							
		rsgbi = dbgbi.execute(select_string , [batch_id, seq]);
		var batch_instruction = {
				id 				: batch_id,
				seq 			: seq,
				pid 			: rsgbi.field(0),
				z_binding_id 	: rsgbi.field(1),
				z_description 	: rsgbi.field(2),
				conc_type		: rsgbi.field(3),
				x_pid 			: rsgbi.field(4),
				x_binding_id 	: rsgbi.field(5),
				organelle 		: rsgbi.field(6),
				y_pid 			: rsgbi.field(7),
				y_binding_id 	: rsgbi.field(8),
				breadth 		: rsgbi.field(9),
				depth 			: rsgbi.field(10)};			
		return batch_instruction;
	} 
	catch (e) { lib_debug (mv.ba, 'get_bi_seq', 'exception', e); }
	finally {
		rsgbi.close();	
		dbgbi.close();
	}
};

batch_module.prototype.get_count_bi 			= 	function(wp, mv, batch_id) {
/* count batch_instructions on ID  
 */
//	lib_debug (mv.ba, mv.from.action, mv.from.status, 'get_count_bi '+batch_id);
	
	var dbgci 		= google.gears.factory.create('beta.database');
	try {
		dbgci.open(mv.cu.name);
		var select_string = 	
		' select count(*) '+
		' from batch_instruction bi, active_batch ab where bi.id = ? and ab.id = bi.id ';										
		var rsgci = dbgci.execute(select_string , [batch_id]);
		return rsgci.field(0);
	} 
	catch (e) { lib_debug (mv.ba, 'get_count_bi', 'exception', e); }
	finally {
		rsgci.close();	
		dbgci.close();
	}
};

batch_module.prototype.execute_instruction = 		function(wp, mv, batch, seq) {    
/* execute one batch_instruction indicated by seq  
 */
	try {
		var bi = get_bi_seq (wp, mv, batch.id, seq);
		mv.ba.bi = bi;

		lib_debug (mv.ba, mv.from.action, mv.from.status, 'execute_instruction '+bi.conc_type + ' '+mv.cu.name + ' '+bi.organelle+ ' '+bi.breadth+ ' '+
		batch.def_breadth+ ' '+batch.max_breadth+ ' '+batch.mos_breadth+ ' '+batch.sywID+ ' '+
		batch.wkrthrottle + ' '+bi.depth+ ' '+ batch.version+ ' '+ bi.x_binding_id+ ' '+ bi.y_binding_id+ ' '+  
		batch.max_inbred+ ' '+ batch.def_bindpar + ' '+ mv.init.parent + ' '+ mv.ba.mewID);

		if (bi.conc_type === 'me') { // meiosis
			meiosis(mv, wp, mv.cu.name, bi.organelle, bi.breadth || batch.def_breadth, bi.depth, batch.version, bi.x_binding_id, bi.y_binding_id, batch.max_breadth, batch.mos_breadth, batch.max_inbred, batch.def_bindpar, batch.sywID, batch.wkrthrottle);
		}
		else {
			synthesis(mv, wp, mv.cu.name, bi.organelle, bi.breadth, batch.def_breadth, batch.max_breadth, batch.mos_breadth, batch.sywID, batch.wkrthrottle);
		}
	} 
	catch (e) { lib_debug (mv.ba, 'execute_instruction', 'exception', e); }
};

batch_module.prototype.insert_batch = 				function(wp, mv){
/* Request DB worker to insert a batch record
 * Upon completion, it will report back to this module as 'insert_batch'
*/
	mv.dest.module 		= 'db';
	mv.from.module		= 'batch';
	mv.dest.action		= 'insert_batch';

	try {
		trx_caller (wp, mv, null, null, mv.ba.dbwID);
		lib_debug (mv.ba, mv.from.action, mv.from.status, 'batch. insert_batch. called ');
	} 
	catch (e) { lib_debug (mv.ba, 'insert_batch', 'exception', e); }
}

batch_module.prototype.insert_batch_instructions =  function(wp, mv) {
/* Request DB worker to insert a batch instruction record
 * Upon completion, it will report back to this module as 'insert_batch_instructions'
*/
	mv.dest.module 		= 'db';
	mv.dest.action		= 'insert_batch_instructions';
	lib_debug (mv.ba, mv.from.action, mv.from.status, 'batch insert_batch_instructions ' );

	try {
		trx_caller (wp, mv, null, null, mv.ba.dbwID);
		return mv.ba.bi.length;
	} 
	catch (e) { lib_debug (mv.ba, 'insert_batch_instructions', 'exception', e); }
}

batch_module.prototype.update_batch =  	function(wp, mv) {
/* Request DB worker to update a batch record to indicate batch completion
*/
	mv.dest.module 		= 'db';
	mv.from.module		= 'batch';
	mv.dest.action		= 'update_batch';

	try {
		lib_debug (mv.ba, mv.from.action, mv.from.status, 'call update_batch. batch '+mv.ba.id);
		trx_caller (wp, mv, null, null, mv.ba.dbwID);
	} 
	catch (e) { lib_debug (mv.ba, 'update_batch', 'exception', e); }
};

batch_module.prototype.update_batch_instruction =  	function(wp, mv, binding_id) {
/* Request DB worker to update a batch instruction record to redirect relevant pid to binding id.
 * and to indicate instruction completion
 * Expects just one item in array mv.ba.bi (mv.ba.bi[0])
*/
	mv.dest.module 		= 'db';
	mv.from.module		= 'batch';
	mv.dest.action		= 'update_batch_instruction';

	try {
		mv.ba.bi.z_binding_id = binding_id;
		trx_caller (wp, mv, null, null, mv.ba.dbwID);
	} 
	catch (e) { lib_debug (mv.ba, 'update_batch_instruction', 'exception', e); }
};

batch_module.prototype.getFlickrSimTags 		=	function(sourceTag){

	var apiURL 		= mv.api.flickr.URL;
	var apiMethod 	= 'flickr.tags.getRelated';
	var apiKey 		= mv.api.flickr.key;
	var apiTag 		= '&tag=' + mv.sy.organelle;
	var apiFormat 	= '&format=rest'
	var altArr 		= [];

	try {
		var request = google.gears.factory.create('beta.httprequest');
		request.open("GET", apiURL + apiMethod + apiFormat + apiKey + apiTag);
		request.onreadystatechange = function() {
		  if (request.readyState == 4) {
//		    lib_debug (mv.ba, mv.from.action, mv.from.status,'received response');
		  }
		};
		request.send();

		return altArr;
	} 
	catch (e) { lib_debug (mv.ba, 'getFlickrSimTags', 'exception', e); }
};

batch_module.prototype.synthesis = 					function(mv, wp, culture, organelle, chosenbreadth, def_breadth, max_breadth, mos_breadth, synworker, wkrthrottle){
		var create_dt 	= new Date().getTime();
		
// Generate a synthetic cTag of breadth Z_breadth from the contents of the text passed in the organelle parameter
		var action;
		var status;
//		lib_debug (mv.ba, mv.from.action, mv.from.status, 'batch. synthesis '+culture+' '+organelle+' '+chosenbreadth+' '+max_breadth+' '+mos_breadth+' '+synworker+' '+wkrthrottle+' ' )
		try {
			// validation

			if (organelle === "") throw ('Text entry field is null');			
			var breadth 		= chosenbreadth | def_breadth;
			if (breadth > max_breadth) throw ("Breadth-step error: target breadth "+ Z_breadth +" - exceeds limit "+ max_breadth);
			if (breadth < mos_breadth) throw ("definedBreadth-step error: Invalid target breadth - too low");

			mv.sy.oos 			= organelle;
			mv.sy.description 	= organelle;
			mv.sy.create_dt 	= create_dt;
			mv.sy.breadth 		= breadth <= max_breadth ? breadth : max_breadth;
			mv.sy.steps 		= mv.sy.breadth - mos_breadth;
			mv.sy.mos_breadth 	= mos_breadth;
//			mv.sy.sywID 		= synworker;
			mv.sy.lot 			= 0;
			mv.sy.skip_to_lot 	= 0;
			mv.sy.last_lot_conts= null;

			mv.from.status = 'success'
			mv.dest.module =	'synthesis';
			mv.dest.action = 	'initiate';
			mv.dest.message = 	null;
			mv.dest.ID = 		mv.ba.sywID;
			mv.init.wkrthrottle	= wkrthrottle;

			trx_caller (wp, mv, null, null, mv.dest.ID);
			lib_debug (mv.ba, mv.from.action, mv.from.status, 'called synthesis. '+culture+' '+organelle+' '+chosenbreadth+' '+max_breadth+' '+mos_breadth+' '+synworker+' '+wkrthrottle+' '+ mv.dest.ID);
			mv.dest.action = 	null;
		}
	catch (e) { lib_debug (mv.ba, 'synthesis', 'exception', e); }
	}

batch_module.prototype.meiosis = 					function(mv, wp, culture, organelle, Z_breadth, Z_depth, version, X_ID, Y_ID, maxBreadth, mos_breadth, MaxInBred, def_bind_parent, synworker, wkrthrottle){
	try {

		var binding = function () { return 	{	
				binding_id		: null,		
				description		: organelle,			// localised
				xpar			: 0,		
				version			: '0.1',	
				breadth			: Z_breadth,			// localised
				depth			: Z_depth,				// localised
				synth_flag		: null,		
				id1_reversal 	: 0,		
				bv_type			: 'text', 				// localised
				act_flag		: 0,					// localised
				bnd_val			: organelle,			// localised		
				create_dt		: new Date().getTime(),	// localised	
				conts			: null,			
				offset			: null,
				mos_breadth		: mos_breadth, 			// localised
				min_entropy		: MaxInBred,			// localised
				def_bind_parent	: def_bind_parent,		// localised
				syw_base_lot	: 0, 			
				skip_to_lot 	: 0,			
				lot				: 0, 			
				steps			: Z_breadth - mos_breadth, 	// localised	
				oos				: null, 
				dbwID			: mv.ba.dbwID, 				// localised
				sywID			: mv.ba.sywID 				// localised
			}
		};

		mv.me.Z = binding();
		mv.cu.name 				= culture;
		mv.me.X.binding_id 		= X_ID; 
		mv.me.Y.binding_id 		= Y_ID; 
		mv.me.version			= version;
//		mv.me.dbwID				= mv.ba.dbwID;

		mv.from.message			= null;

		mv.dest = {
			module: 'meiosis',
			action: 'initiate',
			message: null,
			ID: mv.ba.mewID }
			lib_debug (mv.ba, mv.from.action, mv.from.status, ' calling meiosis  : '+mv.ba.mewID+' '+mv.from.module+' '+mv.from.action+' '+mv.from.status+' '+mv.from.message);

		// call meiosis 
		trx_caller (wp, mv, null, null, mv.dest.ID);
	}
	catch (e) { lib_debug (mv.ba, 'meiosis', 'exception', e); }
}; 

batch_module.prototype.trx_caller = 				function(wp, mv, msg, status, dest_ID){

	/* Messaging function. Replicated in each module.
	 * The reply parameter is true when replying directly to a message from another module.
	 */
	try {
		mv.dest.ID = dest_ID;
		mv.from.module = 'batch';
		
		if (msg) {
			mv.from.message = msg;
			mv.dest.message = msg;
		}
		if (status) 
			mv.from.status = status;
		
		if (mv.dest.module === 'db' || mv.dest.module === 'UI') 
			wp.sendMessage([mv], mv.dest.ID);
		else if (mv.dest.module === 'synthesis' || mv.dest.module === 'meiosis') 
			wp.sendMessage([mv, mv.init.synthScript, mv.init.wkrthrottle, mv.init.parent], mv.dest.ID); // sy or me 
		
//		lib_debug (mv.ba, mv.from.action, mv.from.status, 'done trx_caller' + '.' + mv.dest.module + '.' + mv.dest.action + '.' + mv.dest.ID + '.' + mv.init.wkrthrottle + '.' + mv.init.parent);

	} 
	catch (e) { lib_debug (mv.ba, 'trx_caller', 'exception', e); }
};

 
/* Foreground functions 
 */

function set_syn (pid, z_description, organelle, breadth) {
	return {id:				null,
			pid:			pid,
			conc_type:		'sy',
			z_binding_id:	null,
			z_description:	z_description,
			x_pid:			null,
			x_binding_id:	null,
			organelle:		organelle,
			y_pid:			null,
			y_binding_id:	null,
			breadth:		breadth,
			depth:			0};
};

function set_mei (pid, z_description, x_pid, x_binding_id, y_pid, y_binding_id, breadth, depth) {
	return {id:				null,
			pid:			pid,
			conc_type:		'me',
			z_binding_id:	null,
			z_description:	z_description,
			x_pid:			x_pid,
			x_binding_id:	x_binding_id,
			organelle:		null,
			y_pid:			y_pid,
			y_binding_id:	y_binding_id,
			breadth:		breadth,
			depth:			depth};
};

function do_batch(batch){
		try {
			var mv = init_mv(); // create module variable structue and default values
			mv.ba 				= batch;
			mv.cu.name 			= operativeCulture;
			mv.from.module 		= 'UI';
			mv.from.status 		= 'success';
			mv.dest.module 		= 'batch';
			mv.dest.action 		= 'initiate';
			mv.ba.dbwID			= dbworker.ID;
			mv.ba.sywID			= synworker.ID;
			mv.ba.mewID			= meiworker.ID;	
			mv.dest.ID 			= batchworker.ID;
			
			trx_caller (wp, mv, synworker.Script, mv.ba.wkrthrottle, mv.dest.ID);
			return mv.ba.bi.length;
		}
		catch(e){
			alert ("set_batch error: " +e)	;
		}
	}

function set_batch (active_flag, execute_flag, bi) {

	return {
		id				: null,
		def_breadth		: document.getElementById("ConcDefBreadth").value,
		max_depth		: document.getElementById("ConcMaxDepth").value,
		version			: document.getElementById("ConcAkinityVers").value,
		max_inbred		: document.getElementById("ConcMaxInBred").value,
		def_bindpar		: document.getElementById("ConcDefBindPar").value,
		mos_breadth		: document.getElementById("ConcMosBreadth").value,
		max_breadth		: document.getElementById("ConcMaxBreadth").value,
		active_flag		: active_flag ? '1' :'0', 
		execute_flag	: execute_flag? '1' :'0', 
		bi				: bi,
		dbwID			: dbworker.ID,
		bawID			: batchworker.ID,
		sywID			: synworker.ID,
		mewID			: meiworker.ID,
		synthScript		: synworker.Script,
		wkrthrottle 	: document.getElementById("GearsWorkers").value
	};
}
 
function default_batch_data (culture, active_flag, execute_flag) {

	var rn = Math.random();
	var dt = new Date().getTime();
	var bi = []; 	
	
	// cutlure initialisation data
	bi[0]=  	set_syn (1, 	rn, rn, 11);						// random culture identity element
	bi[1]=  	set_syn (2, 	dt, dt, 11);						// datetime element
	bi[2]=  	set_syn (3, 	culture, culture, 11);				// name of culture
	bi[3]=  	set_syn (4, 	'random', 'random', 11);
	bi[4]=  	set_syn (5, 	'datetime', 'datetime', 11);
	bi[5]=  	set_syn (6, 	'Akinity', 'Akinity', 11);
	bi[6]=  	set_syn (7, 	'MySeed', 'MySeed', 11);
	bi[7]=  	set_syn (8, 	'culture', 'culture', 11);
	bi[8]=  	set_mei (9, 	'datetime', 2, null, 5, null, 11, 3);
	bi[9]=  	set_mei (10, 	'random', 1, null, 4, null, 11, 3);
	bi[10]= 	set_mei (11, 	'culture', 3, null, 8, null, 11, 2);
	bi[11]= 	set_mei (12, 	'MySeed', 9, null, 10, null, 11, 3);
	bi[12]= 	set_mei (13, 	culture, 11, null, 12, null, 11, 4);
	bi[13]= 	set_mei (14, 	'Akinity', 13, null, 7, null, 11, 5);

	return set_batch (active_flag, execute_flag, bi);
}


