/* Centova Webmail
 * Copyright 2005, eSite Media Inc.
 *
 * Remote procedure call client class
 */

Array.prototype.serialize = function() {
	var output = "";
	var vtype = "";
	var item = "";
	for (var i=0; i<this.length; i++) {
		item = this[i];
	
		if (typeof(item)=='string') {
			vtype = 's';
		} else if (typeof(item)=='number') {
			vtype = 'i';
			item = item.toString();
		} else if (typeof(this[i])=='boolean') {
			vtype = 'b';
			item = item ? "1" : "0";
		} else if (typeof(this[i])=='object') {
			vtype = 'a';
			if (item==null) {
				vtype = 's';
				item = '';
			} else if (item && item.serialize) {
				item = item.serialize();
			} else {
				alert('cannot serialize: '+this[i]);
				continue;
			}
		} else {
			vtype = 's';
			item = item + ''; // recast to string
		}
    
		output += vtype+item.length+'|'+item;
	}
	return output;
}

function array_copy_stringable(src) {
  	var copy = new Array();
  	for (var i = 0; i < src.length; i++) {
  		if (typeof src == "string") {
  			var x = src[i].replace(/%/g, "%P");
  			copy[i] = x.replace(/,/g, "%C");
  		} else {
  			copy[i] = src[i];
  		}
  	}
  	return copy;
}

// RPC Client constructor
// url - the URL of the RPC server
// objectname - the name of this instantiated RPC object (for self-references)
function RPCClient(url,objectname,sessionid) {
	this.url = url;
	this.selfname = objectname;
	this.sessionid = sessionid;
	this.requests = 0;
	this.debug_callback = null;
	
	//this.requestlist = new Array();
	this.callbacks = new Array();
		
	var bodyelement = document.getElementsByTagName('BODY');
	this.body = bodyelement[0];

	// ghetto-threading :)
	this.threadid = Math.floor(Math.random()*65535);
	this.rpcthreads = new Array();
	this.create_thread();	
	this.caching = false;
}

RPCClient.prototype.debug = function(s) {
	if (this.debug_callback!=null) this.debug_callback('RPC',s);
}

RPCClient.prototype.debug_server = function(s) {
	if (this.debug_callback!=null) this.debug_callback('Server',s);
}

// Sets the callback method for an RPC call
RPCClient.prototype.set_callback = function(execmethod,callback_code) {
	this.callbacks[execmethod] = callback_code;
}

RPCClient.prototype._execute = function(progressive,iframemode,args) {
	var execmethod = args[0]

	this.debug('Execute: '+execmethod+'('+args.slice(1).toString()+')');

	this.requests++;
	var requestid = this.requests;
	var rnd = Math.floor(Math.random() * 1000000);

	// build the URL for this remote procedure call
	args.shift();
	args.unshift(requestid);
	args.unshift(this.callbacks[execmethod]);
	args.unshift(this.selfname);
	
	var totallength = 0;
	for (var i=0; i<args.length; i++) {
		if (args[i] && args[i].length) {
			totallength += args[i].length;
		} else if (args[i] && args[i].toString) {
			totallength += args[i].toString().length;
		}
	}
	var must_post = totallength>1000;
	//must_post = true;
	
	if (must_post) {
		progressive = true;
		iframemode = true;
	}
	
	rpc_url = this.url+'?s='+this.sessionid+'&m='+execmethod+'&h='+(progressive?1:0)
	
	if (this.caching) {
		rpc_url += '&c=1';
	} else {
		rpc_url += '&r='+requestid+'&r1='+rnd;
	}
	rpc_params = escape(args.serialize());
	if (!must_post) rpc_url += '&p='+rpc_params;
	if (iframemode && !progressive) rpc_url += '&n=1';

//	if (progressive) prompt('Launching progressive url:',rpc_url);	
	// find an available rpc "thread" and execute our RPC
	thread = this.available_thread(iframemode,must_post);
	thread.rpcbusy = true;
	//this.requestlist[requestid] = thread;
	
	if (must_post) {
		this.debug('POST: '+rpc_url+' ('+rpc_params+')');
		thread.form = this.create_thread_form(thread.id,rpc_url,rpc_params);
		thread.form.submit();
	} else {	
		this.debug('GET:  '+rpc_url);
		//layer.write('contacts_list','rpc.execute: '+rpc_url);
		thread.src = rpc_url;
	}
	
//	document.getElementById('navigation').innerHTML = rpc_url;
	
	this.debug('-- Complete\n');
}

RPCClient.prototype.create_thread_form = function(threadid,url,params) {
	var formid = 'form_' + threadid;

	var newform = document.createElement('form');
	newform.style.display = 'none';
	newform.setAttribute('id',formid);
	newform.setAttribute('name',formid);
	newform.setAttribute('target',threadid);
	newform.setAttribute('method','post');
	newform.setAttribute('action',url);
	
	var newfield = document.createElement('input');
	newfield.setAttribute('name','p');
	newfield.setAttribute('value',params);
	newfield.setAttribute('type','hidden');
	
	newform.appendChild(newfield);
	this.body.appendChild(newform);	
	
	return newform;
}

// Executes an RPC call.
// Parameter 0: RPC method name
// Parameters 1..n: (optional) RPC method parameters
RPCClient.prototype.execute = function() {
	var args = array_copy_stringable(arguments);
	this._execute(false,false,args);
}

// Executes an RPC call.
// Parameter 0: RPC method name
// Parameters 1..n: (optional) RPC method parameters
RPCClient.prototype.execute_progressive = function() {
	var args = array_copy_stringable(arguments);
	this._execute(true,true,args);
}

// Executes an RPC call.
// Parameter 0: RPC method name
// Parameters 1..n: (optional) RPC method parameters
RPCClient.prototype.execute_raw = function() {
	var args = array_copy_stringable(arguments);
	this._execute(false,true,args);
}



// Sets the state for a particular request; generally used only by the rpc "thread"
RPCClient.prototype.set_request_state = function(requestid,newstate) {
	this.requeststates[requestid] = newstate;
}


// Creates an RPC "thread" (<script> element) to perform an RPC call.
// Allows for multiple asynchronous calls.
RPCClient.prototype.create_thread = function(iframemode,postmode) {
	this.threadid++;
	threadid = "js_rpc"+this.threadid;
	
	if (iframemode) {
		newthread = document.createElement('IFRAME');
		if (!postmode) newthread.setAttribute('type','text/javascript');
		newthread.style.display = 'none';
	} else {
		newthread = document.createElement('SCRIPT');
		newthread.setAttribute('type','text/javascript');
	}
	newthread.setAttribute('id',threadid);
	newthread.setAttribute('name',threadid);
	newthread.setAttribute('rpcbusy',false);
	newthread.setAttribute('isiframe',iframemode);
	
	this.body.appendChild(newthread);
	this.rpcthreads.push(newthread);
	
	return newthread;
}

// Returns the first available RPC "thread"; creates a new one if none are
// available.
RPCClient.prototype.available_thread = function(iframemode,postmode) {
	// find a thread that isn't busy
	for (var i=0; i<this.rpcthreads.length; i++) {
		if (!this.rpcthreads[i].rpcbusy && (this.rpcthreads[i].isiframe==iframemode) ) {
			return this.rpcthreads[i];
		}
	}
	// none available? create one
	return this.create_thread(iframemode,postmode);
}

RPCClient.prototype.error = function(msg) {
	alert(msg);
}
RPCClient.prototype.authfailure = function() {
	alert('RPC failure: Your session has timed out due to inactivity.  Please login again to continue.');
//	top.window.location="/";
}
