
Ekina.JSPB = {};
Ekina.JSPB.Controls = {};

//Ekina.JSPB.defaultInvokeUrl = window.location.href;
Ekina.JSPB.invokeNoReturn = function(methodInfo) { 
	/// <summary>Invokes a server method with no callback</summary>
	/// <param name="methodInfo" type="Ekina.JSPB.MethodInfo">A structure describing the server method to execute</param>
	/// <returns type="Boolean">True if the method was executed, otherwise false</returns>
	methodInfo = Ekina.JSPB.Helper.parseMethodInfo(methodInfo);
	if (!methodInfo) return false;
	Ekina.JSPB.invoke(methodInfo, null);
	return true;
};
Ekina.JSPB.invoke = function(methodInfo, callback, args) {
	/// <summary>Invokes a server method</summary>
	/// <param name="methodInfo" type="Ekina.JSPB.MethodInfo">A structure describing the server method to execute</param>
	/// <param name="callback" type="Function">A callback function to fire when the server method returns data</param>
	///	<param name="args" type="Object" optional="true">Extra arguments to pass to the callback function</param>
	/// <returns type="Boolean">True if the method was executed, otherwise false</returns>
	methodInfo = Ekina.JSPB.Helper.parseMethodInfo(methodInfo);
	if (!methodInfo) return;
	var invoker = Ekina.JSPB._createInvoker(callback, args);
	Ekina.JSPB._invoke(invoker, methodInfo);
	return invoker;
};
Ekina.JSPB._invoke = function(invoker, methodInfo){invoker.invoke(methodInfo);};
Ekina.JSPB._createInvoker = function(callback, sender){return new Ekina.JSPB.Invoker(callback, sender);};
Ekina.JSPB.createHttpObject = function(){var xmlhttp = false;try{xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");}catch(ex1){try{xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");}catch(ex2){xmlhttp = false;}}if(!xmlhttp && typeof XMLHttpRequest!='undefined')xmlhttp = new XMLHttpRequest();return xmlhttp;};

Ekina.JSPB.onerror = function(e) {
	/// <summary>Event which is fired when there is a JSPB error</summary>
	alert("Error: " + e);
};

Ekina.JSPB.Helper = { };

Ekina.JSPB.Helper.parseBool = function(value) {
	if(value == null) return false;
	return value.toString() == "true";
};

Ekina.JSPB.Helper.parseMethodInfo = function(mi) {

	if (typeof mi == "string")
		return new Ekina.JSPB.MethodInfo(mi, null, null);

	if (!mi.methodName) {
		alert("Method info is not valid");
		return null;
	}
	
	if (!mi.assemblyName) mi.assemblyName = "";
	if (!mi.className) mi.className = "";
	
	var ret =  new Ekina.JSPB.MethodInfo(mi.methodName, mi.className, mi.assemblyName);
	
	if (!mi.args)
		return ret;
		
	if (!mi.args.push || !mi.args.pop) {
		alert("Argument list is invalid");
		return null;
	}
	
	for (var i = 0; i < mi.args.length; i++) {
		var o = mi.args[i];
		if (typeof o == "object")
			ret.addArgument(o.data, o.type || null, o.assembly || null);
		else
			ret.addArgument(o);
	}
	
	return ret;
};

Ekina.JSPB.Invoker = function(callback, sender) {
	this.callback = callback;
	this.xmlhttp = Ekina.JSPB.createHttpObject();
	this.timer = null;
	this.sender = sender;
	this.cancel = false;
};

Ekina.JSPB.Invoker.prototype.invoke = function(methodInfo) {
	var jspb = methodInfo.serialize();
    this.async = true;
    if(this.async){
        this._invokeAsyncServerMethod(jspb);
    }
    else{
        this._invokeServerMethod(jspb);
	    this._invokeCallback();
	}
};

Ekina.JSPB.Invoker.prototype._invokeCallback = function() {
	if (this.cancel) return;
	var ret = this.xmlhttp.responseText;
	var retObject;
	
	try {
		retObject = eval("(" + ret + ")");
	}
	catch (e) {
		alert("error evaluating: " + ret + "\n\nerror:" + e.description);
		return;
	}

	if (retObject.completed) {
		if (this.callback)
			this.callback(retObject.response, this.sender);
	}
	else {
		if (Ekina.JSPB.onerror)
			Ekina.JSPB.onerror(retObject.response, this.sender);
		
		if (window["ekinaLoaded"])
			Ekina.Events.fire(Ekina.JSPB, "onerror", retObject.response);
	}
};

Ekina.JSPB.Invoker.prototype._invokeServerMethod = function(methodInfoXml) {

	this.xmlhttp.open("POST", Ekina.JSPB.defaultInvokeUrl, false);
	this.xmlhttp.send(methodInfoXml);
};

Ekina.JSPB.Invoker.prototype._invokeAsyncServerMethod = function(methodInfoXml) {
    this.xmlhttp.open("POST", Ekina.JSPB.defaultInvokeUrl, true);
	this.xmlhttp.send(methodInfoXml);
	var realThis = this;
	this.timer = window.setInterval(function() {
		realThis.checkState();
	}, 100);
};

Ekina.JSPB.Invoker.prototype.checkState = function(index) {	
    if(this.xmlhttp.readyState==4) { 
        window.clearInterval(this.timer);
        this._invokeCallback();
    }
};
Ekina.JSPB.MethodInfo = function(methodName, className, assemblyName) {
	this.methodName = methodName;
	this.assemblyName = assemblyName == null ? "" : assemblyName;
	this.className = className == null ? "" : className;
	this.args = [];
};

Ekina.JSPB.MethodInfo.prototype.addArgument = function(data, type, assembly ){
	/// <summary>Adds an argument to the server method definition</summary>
	/// <param type="Object" name="data">The data to send</param>
	/// <param type="String" name="type" optional="true">The type of the data passed, if this is ommited, the type is infered</param>
	/// <param type="String" name="assembly" optional="true">The assembly containing the specified type</param>
	this.args[this.args.length] = new Ekina.JSPB.MethodArgument(data, type, assembly);
};

Ekina.JSPB.MethodInfo.prototype.serialize = function() {
	var retXml = "";
	retXml += "<jspb_invoke>";
	retXml += "<method>" + this.methodName + "</method>";
	retXml += "<class>" + this.className + "</class>";
	retXml += "<assembly>" + this.assemblyName + "</assembly>";
	retXml += "<args>";
	for(var i = 0; i < this.args.length; i++)
		retXml += "<arg type=\"" + this.args[i].type + "\" assembly=\"" + this.args[i].assembly + "\"><![CDATA[" + this.args[i].data + "]]></arg>";
	retXml += "</args>";
	retXml += "</jspb_invoke>";
	return retXml;
};

Ekina.JSPB.MethodArgument = function(data, type, assembly) {
	this.data = data;
	if(type == null)
		type = typeof data;
	this.type = type;
	this.assembly = assembly ? assembly : "";
};