	onLoad.add(xpc_init);
	
	var xpc				= null;
	function xpc_init() {
		xpc	= XPC.getService(gw_mainXpcFunctions);
		if (typeof(xpcFunctions)!="undefined") xpc.addFunctions(xpcFunctions);
	}
	
	// Private constants
	var PARAM_STRING_SEPARATOR	= "#";
	var PARAM_ARRAY_INDICATOR	= "&";
	XPC = {
		getService : function(functions) {
			var ARGS = "_Args";
			this.addFunctions = function(info){
				var i=0;
				for(i=0;i<info.length;i++) {
					this.addFunction(info[i]);
				}
			}
			this.addFunction = function(info){
				var name, alias;
				if (info==null || typeof(info)!="object" || info.length<2) { //array
					error("addFunction expects an array with <functionName, to, paramName1, paramName2, etc.>; check for trailing comma in xpcFunctions definition");
					return;
				}
				alias = info[0];
				this[alias] = new Function('var args = new Array(), i;for(i=0;i<arguments.length;i++){args.push(arguments[i]);};	return this.call("' + alias + '", args, false);');
				this[alias+"_debug"] = new Function('var args = new Array(), i;for(i=0;i<arguments.length;i++){args.push(arguments[i]);};	return this.call("' + alias + '", args, false, null, true);');
				this[alias+ARGS] = info;
			}
			this.call = function(alias, args){
				var i=0;
				var params="";
				if (!this[alias+ARGS]) {
					if (args.length>0) {
						error("Function definition ("+alias+") did not include parameter definitions; please change or use "+alias+explicitParams);
						return;
					}
				} else {
					var paramNames = this[alias+ARGS];
					var i=0, j=2; //skip XPCname, to
					var paramName;
					var to		= paramNames[1];
					if (to == TO_ONE) j=3;	// skip guid in 2one
					
					do {
						if (j!=paramNames.length) {
							paramName = paramNames[j++];
						} else break;
	
						if (args[i]==null) args[i]="";	//TODO: test
						if (typeof(args[i])=="object") { //array
							var a_temp = args[i++];
							params += PARAM_ARRAY_INDICATOR + a_temp.length + PARAM_ARRAY_INDICATOR;
							for (tt=0; tt<a_temp.length; tt++) {
								params += encodeURIComponent(a_temp[tt]) + PARAM_STRING_SEPARATOR;
							}
						} else {
							params += encodeURIComponent(args[i++])+PARAM_STRING_SEPARATOR;
						}
						
						if (i == args.length && j==paramNames.length) break;
	
						if (j==paramNames.length && i != args.length) {
							error("Too many parameters in function call compared to definition: "+alias+"("+args+") : "+params);
							return;
						}
					} while (true);
				}
				try {
					var GUID	= "";
					var msg		= alias + PARAM_STRING_SEPARATOR + to + PARAM_STRING_SEPARATOR + params + PARAM_STRING_SEPARATOR;
					if (to == TO_ONE) {
						GUID = args[0];
					} else if (to == TO_PRESENTER) {
						GUID=ov.presenterCallGuid;
	
						if (GUID==null) {
							error("No presenter");
							return;  // DO NOT SEND IT TO EVERYONE
						}
					}
					
					log("xpc->"+msg);
					ov.sendData(GUID, msg);
					
					return true;
				} catch(err) {
					log("xpc error: "+err.name + " (" + err.message + ")");
					//errorC(err);	//ignore; connection is broken
					return false;
				}
			}
			
			this.dataReceived = function(callGuid, data, isPrivate) {
				var paramString ="";
				log("xpc<-"+data);

				var dataArray = data.substring(0, data.length - 2).split(PARAM_STRING_SEPARATOR);
				var alias	= dataArray[0];
				var to		= dataArray[1];
				for (var i=2; i<dataArray.length; i++) {
					var temp = dataArray[i];
					if (temp.substring(0,1)==PARAM_ARRAY_INDICATOR) { //array
						var ts = temp.split(PARAM_ARRAY_INDICATOR);
						var l = ts[1];
						paramString += (paramString==""?"":", ")+"[";
						for (j=0;j<l;j++,i++) {
							if (j==0) {
								paramString += "\""+decodeURIComponent(ts[2])+"\"";
							} else {
								paramString += ", \""+decodeURIComponent(dataArray[i])+"\"";
							}
						}
						paramString += "]";
					} else {
						paramString += (paramString==""?"":", ")+"\""+decodeURIComponent(temp)+"\"";
					}
				}
			
				if (paramString!="") paramString += ", ";
				try {
					var s= alias+"("+paramString+"callGuid, isPrivate);";
					if (to==TO_MODERATORS && !ov.isModerator) return;	//filter moderator messages
					eval(s);
				} catch(err) {
					//gp4 sends stuff we do not understand: errorC(err);
				}
			}
	

			////////////// getService
			if (functions) this.addFunctions(functions);
			return this;
		}
	};

