// ///////////////////////////////////////
// Transport class
// ///////////////////////////////////////

//fields
BETransport.prototype._transportURL;
BETransport.prototype._requests;  //array of pending ajax requests
BETransport.prototype._keepAliveSec;
BETransport.prototype._keepAliveExecuter;
BETransport.prototype._keepAliveEnvelope;

//constructor
function BETransport(transportURL, keepAliveSec){
    this._transportURL = transportURL;
    this._requests = new Array();
    this._keepAliveSec = keepAliveSec;
}

//methods
BETransport.prototype.getTransportURL = function(){
    return this._transportURL;
}

BETransport.prototype.transmitRequest = function(channelId, action, objMessage, receiveCallback){    
	//dispose of finished requests
	while (this._requests.length > 0 && this._requests[0].isFinished()){
		this._requests.shift();
	}
	
	//check for periodical executer for keepalives
	if (this._keepAliveExecuter == null && channelId == 'framework' && action == 'login'){	
		var eventListener = this.transmitKeepalive.bindAsEventListener(this);
		this._keepAliveExecuter = new PeriodicalExecuter(eventListener, this._keepAliveSec);
	}

	//todo include sessionData object so checksum can be calculated
	var requestEnvelope = new BEEnvelope(channelId, action, objMessage, receiveCallback);
	this._requests.push (requestEnvelope);
	requestEnvelope.sendMessage(this._transportURL);
}

BETransport.prototype.transmitKeepalive = function(event){
	//send the keepalive message
	if (this._keepAliveEnvelope == null){
		var msgIgnore = {ignorejson: "1"};
		this._keepAliveEnvelope = new BEEnvelope("framework", "keepalive", msgIgnore , null);	
	}
	this._keepAliveEnvelope.sendMessage(this._transportURL);	
}

//
//Javascript Envelope class
BEEnvelope.prototype._channelId;
BEEnvelope.prototype._action;
BEEnvelope.prototype._callback;
BEEnvelope.prototype._xmlMessage;
BEEnvelope.prototype._isFinished;
BEEnvelope.prototype._ajaxRequest;

function BEEnvelope(channelId, action, objMessage, receiveCallback){
	this._channelId = channelId;
	this._action = action;
	this._callback = receiveCallback;
	this._isFinished = false;
	this._ajaxRequest;
	
	var jsonMessage =  Object.toJSON(objMessage);	
	this._xmlMessage = this.toXML(channelId, action, jsonMessage);
	
}

BEEnvelope.prototype.isFinished = function(){
	return (this._isFinished);
}

BEEnvelope.prototype.toXML = function (channelId, action, myJsonMarkup){
	//concatentate to xml document, ready for transmission
	//todo: calculate checksum
	return  "<msg channelid='" + channelId + "' dir='request' action='" + action + "' checksum='123' > \n" +
				"<![CDATA["  + myJsonMarkup + "]]>\n" + 
		 	"</msg>";
}

BEEnvelope.prototype.sendMessage = function (transportURL){
	//sends xml message to server and logs callback response
	var thisEnvelope = this;
	thisEnvelope._ajaxRequest = new Ajax.Request(transportURL,
				  	{
				    	method:'post',
	  					parameters: {sData: thisEnvelope._xmlMessage},
				    	onSuccess: thisEnvelope.onSuccess,
				    	onFailure: thisEnvelope.onFail,
				    	envelope: thisEnvelope      //workaround to 'this' object being incorrect on ajax callback
				    });	
				    
				    
}

BEEnvelope.prototype.onFail = function (response){
	//send response (fail) //soap error; cannot contact server, etc.
	var thisEnvelope = response.request.options.envelope;
	responseMessage = {
		"result": "fail",
		"message": "There was a communication or server error."
	};

	if (thisEnvelope._callback){
		thisEnvelope._callback(responseMessage);
	}
	thisEnvelope._isFinished = true;
}

BEEnvelope.prototype.onSuccess = function (response){
	//send response (success)	
	var returnXML = response.responseText.toString();
	var thisEnvelope = response.request.options.envelope;
	var responseMessage;
	
	var channelId = thisEnvelope.extractAttribute(returnXML, "channelid");
	var action = thisEnvelope.extractAttribute(returnXML, "action");
	var direction = thisEnvelope.extractAttribute(returnXML, "dir");
	
	//ensure channel, message, direction, etc. as expected
	if (channelId == thisEnvelope._channelId && action == thisEnvelope._action && "response" == direction ){
		var jsonResponse = thisEnvelope.extractJsonMarkup(returnXML);
		responseMessage = jsonResponse.evalJSON(true); //convert to JSON, sanitizing if needed
	}
	else{
		responseMessage = {
			"result": "invalid",
			"message": "The server response had an invalid channel id, action, or direction."
		};
	}
	
	if (thisEnvelope._callback){
		thisEnvelope._callback(responseMessage);
	}
	
	thisEnvelope._isFinished = true;
}

BEEnvelope.prototype.extractAttribute = function (xmlMarkup, attributeName){
	//send response (success)
	var sReturn = "";
	
	var matchAttrib = " " + attributeName; //prepend space for more accurate search
	var quote = '"';
	
	var parts = xmlMarkup.split(">"); //cut off everything after the element body
	if (parts.length > 1){
		var sTag = parts[1].replace(/ =/g, "=");
		//remove spaces around '=' symbols so they become split correctly
		while (sTag.search(" =") > -1 || sTag.search ("= ") > -1 ){
			sTag = sTag.replace(/ =/g, "=");
			sTag = sTag.replace(/= /g, "=");
		}
		var attribArray = sTag.split(" ");
		for (var i = 0; i < attribArray.length; i++){
			var sLine = attribArray[i].strip();
			if (sLine.search(attributeName) > -1){
				//found the attribute - extract the value
				var lineParts = sLine.split("=");
				sReturn = lineParts[1].strip();
				sReturn = sReturn.replace(quote, "");
				sReturn = sReturn.replace(quote, "");
				break;
			}
		}
	}
	
	return sReturn;
}

BEEnvelope.prototype.extractJsonMarkup = function (xmlMarkup){
	//parses json markup from response
	var sReturn = "";
	var parts = xmlMarkup.split("<![CDATA[");
	if (parts.length == 2){
		var sLine = parts[1]; //json part
		parts = sLine.split("]]>")
		if (parts.length == 2){
			sReturn = parts[0];
		}
	}
	
	return sReturn;
}


