var remoteServerAppUrl = "service.php";


// ###########################################################################
// 
//	Das Service-Objekt verwaltet schon instanzierte APIs, bzw deren JavaScript
//	Spiegelungen (die API-Objekte).
//	Stellt Methoden für mehrfach oder Asynchrone Aufrufe zur Verfügung	
//
// ###########################################################################


function services() {
	
	this.initServices = initServices;
	
	// "public" Functions
	this.getApi = getApi;

	this.prepareCall = prepareCall;
	this.multipleCall = multipleCall;
	this.aSyncCall = aSyncCall;
	this.setTransferringLed = setTransferringLed;

	// "private" Functions
	this.serialize4php = serialize4php;	
	this.encodeToCharset = encodeToCharset;
	// Encoders
	this.encode_utf8 = encode_utf8;
	this.encode_iso_8859_1 = encode_iso_8859_1;

	
	// Properties
	this.api = {};	
	this.httpConn = undefined;
	this.charset = undefined;
	
	this.encoder = {'UTF-8':'encode_utf8', 'ISO-8859-1':'encode_iso_8859_1'};
	
	this.initServices();
	
	function initServices() {
		this.httpConn = new httpConnection(remoteServerAppUrl); // Initialitzing the Connection
		this.charset = this.httpConn.doSyncRemoteCall({'method':'getCharset'});
	}

	function getApi(serviceName) {		
		if (typeof(this.api[serviceName]) != "object") {
			this.api[serviceName] = new api(serviceName);
		}
		return this.api[serviceName];
	}		

	function prepareCall(api, method, arguments) {
		//this.switchTransferingLed("on");
		timer.startMessung("PHP-Aufruf");
		timer.startMessung(api.serviceName+"."+method);

		var result = this.httpConn.doSyncRemoteCall({'service':api.serviceName, 'method':method, 'parameter':this.serialize4php(arguments, true)});
		
		timer.stopMessung(api.serviceName+"."+method);
		timer.stopMessung("PHP-Aufruf");
		//this.switchTransferingLed("off");
	 	return result;
	}
	
	function multipleCall(api, methods, alias) {
		//this.switchTransferingLed("on");
		timer.startMessung("PHP-Aufruf");
		timer.startMessung(api.serviceName+"."+alias);

		var result = this.httpConn.doSyncRemoteCall({'service':api.serviceName,'methods':this.serialize4php(methods),'alias':alias});

		timer.stopMessung(api.serviceName+"."+alias);
		timer.stopMessung("PHP-Aufruf");
		//this.switchTransferingLed("off");
		return result;
	}
	
	function aSyncCall(api, method, arguments, object, callBackFunctionName, aSyncCallId) {
		var list = arguments.join(", ");
		//debug.print("CALL: "+api.serviceName+"/"+method+"("+list+")");
		this.httpConn.startASyncRemoteCall({'service':api.serviceName, 'method':method, 'parameter':this.serialize4php(arguments)}, object, callBackFunctionName, aSyncCallId);
	}
	
	function setTransferringLed(ledId) {
		this.httpConn.transferLedId = ledId;
	}


	function serialize4php(value, isArgumentsObj) {	
		var ser = "";
				
		
		var typ = std.getType(value);

		if (isArgumentsObj == true) typ  = "arguments";

		// alert(typ);

		if (typ == "object") {
			var part = "";
			var len = 0;
			for (var n in value) {				
				if (! (value[n] === undefined)) {
					len++;
					part+= this.serialize4php(n);                                   
					part+= this.serialize4php(value[n]);
				}
			}
			ser+="a:"+len+":{"+part+"}";
		}
		
		if (typ == "array" || typ == "arguments") {
			var part = "";
			var len = 0;
			for (var n = 0; n < value.length; n++) {
				if (! (value[n] === undefined)) {
					len++;
					part+= this.serialize4php(n);
					part+= this.serialize4php(value[n]);
				}
			}
			ser+="a:"+len+":{"+part+"}";
		}

		if (typ == "string") {
			value = this.encodeToCharset(value, this.charset);	
			ser+= "s:"+value.length+":\""+value+"\";";
		}	
		if (typ == "number") {
			if (parseInt(value) == value) ser+= "i:"+value+";";		
			else ser+= "d:"+value+";";
		}	
		if (typ == "boolean") {
			var bVal = 0;
			if (value == true) bVal = 1;
			ser+= "b:"+bVal+";";
		}	
		if (typ == "null") {
			ser+= "N;";			
		}	

		return ser;
	}
	
	function encodeToCharset(value, charset) {		
		if (this.encoder[charset] != undefined) {
			value = this[this.encoder[charset]](value);
		}
		else {
			debug.print("ACHTUNG: keinen Encoder zum Charset \""+charset+"\" gefunden, default-encoding (unicode) wird verwendet!");
		}
		return value;
	}
	

	/**
	*	UTF-8 Codierung gemäss selfhtml
	*/
	function encode_utf8(rohtext) {
		// dient der Normalisierung des Zeilenumbruchs
		rohtext = rohtext.replace(/\r\n/g,"\n");
		var utftext = "";
		for(var n=0; n<rohtext.length; n++) {
			// ermitteln des Unicodes des  aktuellen Zeichens
			var c=rohtext.charCodeAt(n);
			// alle Zeichen von 0-127 => 1byte			
			if (c<128) {
				utftext += String.fromCharCode(c);
			}
			// alle Zeichen von 127 bis 2047 => 2byte
			else if((c>127) && (c<2048)) {
				utftext += String.fromCharCode((c>>6)|192);
				utftext += String.fromCharCode((c&63)|128);
			}
			// alle Zeichen von 2048 bis 66536 => 3byte
			else {
				utftext += String.fromCharCode((c>>12)|224);
				utftext += String.fromCharCode(((c>>6)&63)|128);
				utftext += String.fromCharCode((c&63)|128);
			}
		}
		return utftext;
	}	
	/**
	*	ISO-8859-1 Codierung (nix zu tun da gleich wie unicode)
	*/
	function encode_iso_8859_1(rohtext) {
		return rohtext;
	}	
}

var services = new services();


// ###########################################################################
// 
//	Das API Objekt, Erzeugt eine Objekt dass punkto Methoden einem gegebenen
//	Web-Service entspricht.
//
// ###########################################################################

function api(serviceName)  {
	timer.startMessung("initializing API");
	timer.startMessung(serviceName);
		
	this.serviceName = serviceName;
		
	var res = services.httpConn.doSyncRemoteCall({'method':'getMethods', 'parameter':services.serialize4php([serviceName])});

	if (std.getType(res) == "array") {
		for (var n = 0; n < res.length; n++){
			var str = "this[res[n]] = function () { return services.prepareCall(this, '"+res[n]+"', arguments); }";
			eval(str);	// allows a function call like api.setData(arg1, arg2)
		}
	}
	else {
		alert("ACHTUNG: Service nicht gefunden: \""+serviceName+"\"");
	}

	timer.stopMessung(serviceName);
	timer.stopMessung("initializing API");
}


// ###########################################################################
//
// AJAX Funktionen  -  Kapselt technische Details der AJAX-Aufrufes
//	u.a. Url-Codierung, Wahl POST/GET, decodierung der Rückgabe
//
// ###########################################################################

function httpConnection(serviceUrl) {

	this.STATUS_OK = 200;
	this.STATUS_FORBIDDEN = 403

	// "public" Functions
	this.doSyncRemoteCall = doSyncRemoteCall;
	this.startASyncRemoteCall = startASyncRemoteCall;
	
	// "private" Functions
	this.startRemoteCall = startRemoteCall;
	this.startHTTP_GET = startHTTP_GET;
	this.startHTTP_POST = startHTTP_POST;
	this.createParameterList = createParameterList;
	this.interpretResponseText = interpretResponseText;
	
	this.showAccessDenied = showAccessDenied; 
	this.switchTransferingLed = switchTransferingLed;

	// Properties
	this.remoteServiceUrl = serviceUrl;
	this.maxUrlLength = 1024;
	this.debugLineCount = 50;

	this.remoteCallsActive = 0;
	this.transferLedId;
	this.ledColors = {"on":"#44FF44", "off":"#888888"};


	function doSyncRemoteCall(params) {
		//debug.print(params['method']+"("+params['parameter']+")");
	
		var ajax = this.startRemoteCall(params, false);
		var retVal;
		if (ajax.status == this.STATUS_OK) {
			retVal = this.interpretResponseText(ajax.responseText, params);
		}
		if (ajax.status == this.STATUS_FORBIDDEN) {
			this.showAccessDenied();
			retVal = false;
		}		
		return retVal;
	}

	function startASyncRemoteCall(params, object, callBackFunctionName, aSyncCallId) {
		var ajax = this.startRemoteCall(params, true);
		var httpConn = this;

		ajax.onreadystatechange = function() {
			if(ajax.readyState == 4) {
				if(ajax.status == httpConn.STATUS_OK) {
					var result = httpConn.interpretResponseText(ajax.responseText, params)
					object[callBackFunctionName](aSyncCallId, result);
				}
				
				if(ajax.status == httpConn.STATUS_FORBIDDEN) {
					this.showAccessDenied();
					object[callBackFunctionName](aSyncCallId, false);
				}
			}
		}
	}
	
	function showAccessDenied() {
		alert("ACCESS DENIED\nYou have no permission for this function");
		this.remoteCallsActive--;
		if (this.remoteCallsActive == 0) this.switchTransferingLed("off");
	
	}
	
	function startRemoteCall(params, aSyncMode) {
		this.remoteCallsActive++;
		this.switchTransferingLed("on");
	
		var ajax;
		/*@cc_on @*/
		/*@if (@_jscript_version >= 5)  {
			try {
			 	ajax = new ActiveXObject("Msxml2.XMLHTTP");
			} 
			catch (e) {
		 		try {
		  			ajax = new ActiveXObject("Microsoft.XMLHTTP");
		 		} 
		 		catch (E) {
		  			ajax = false;
		 		}
			}
		}
		@else
		ajax = false;
		@end @*/
		if (!ajax && typeof XMLHttpRequest!='undefined') {
			try {
				ajax = new XMLHttpRequest();
			} 
			catch (e) {
				ajax = false;
			}
		}
		
		
		var parameterList = this.createParameterList(params);
		if (this.remoteServiceUrl.length + parameterList.length + 1 > this.maxUrlLength) {
			this.startHTTP_POST(ajax, params, aSyncMode);
		}
		else {
			this.startHTTP_GET(ajax, parameterList, aSyncMode);
		}
		return ajax;
	}
	
	function startHTTP_GET(ajax, parameterList, aSyncMode) {
		ajax.open('GET', this.remoteServiceUrl+"?"+parameterList, aSyncMode);
		ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		ajax.send(null);	
	}
	
	function startHTTP_POST(ajax, params, aSyncMode) {
		var parameterList = this.createParameterList(params);
		ajax.open('POST', this.remoteServiceUrl, aSyncMode);
		ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		ajax.send(parameterList);	
	}
	
	function createParameterList(params) {
		var pairs = new Array();
		for (key in params) {
			pairs[pairs.length] = key.UrlEncode() + "=" + params[key].UrlEncode();
		}
		return pairs.join("&");
	}
	
	function interpretResponseText(responseText, params) {
		this.remoteCallsActive--;

		try {
			eval("var dataObj = "+responseText);
		}
		catch(e) {
			if (this.remoteCallsActive == 0) this.switchTransferingLed("off");

			var response = responseText.split("\n");
			var re = "";
			for (n = response.length - this.debugLineCount; n < response.length; n++) {
				if (n >= 0) re+= response[n]+"\n";
			}
			plist = "";
			for (key in params) {
				plist += key+" = "+params[key]+"\n";
			}
			
			var str = "Responseformat-Error!!<br>Parameter:<br>"+plist+"<br>The last "+this.debugLineCount+" Lines of the Response are:<br><br>"+responseText;
			// alert("Responseformat-Error!!\nParameter:\n"+plist+"The last "+this.debugLineCount+" Lines of the Response are:\n\n"+re);
			std.showHtmlInWindow(str);
			var dataObj = null;
		}

		if (this.remoteCallsActive == 0) this.switchTransferingLed("off");

		return dataObj;	
	}

	function switchTransferingLed(newState) {
		if (this.transferLedId != undefined) {
			var led = document.getElementById(this.transferLedId);
			if (typeof(led) == "object") {
				led.style.backgroundColor = this.ledColors[newState];
			}
		}
	}
}
