var JSJaCHBC_MAX_HOLD = 1;
var JSJACHBC_MAX_WAIT = 300; 

var JSJaCHttpBindingConnection = Class.create();
Object.extend(Object.extend(JSJaCHttpBindingConnection.prototype, JSJaCConnection.prototype), {
	initialize: function(oDbg, id) {
		this.baseInitialize(oDbg, id);
	
        this.reqnum = 0;
		this._hold = JSJaCHBC_MAX_HOLD;
		this._inactivity = 0;
		this._min_polling = 0;
		this._wait = JSJACHBC_MAX_WAIT;  
	},
	
	close: function() {
		this._connected = false;

		this.hold = -1; /* Never find a slot */
		if (this._timeout)
			clearTimeout(this._timeout); // remove timer
			
		clearInterval(this.intervalId);    		
	},
	
	_setHold: function(hold)  {
		if (!hold || isNaN(hold))
			return -1;
		if (hold < 0)
			hold = 0;
		else if (hold > JSJaCHBC_MAX_HOLD)
			hold = JSJaCHBC_MAX_HOLD;
		this._hold = hold;
		return this._hold;
	},
	
	_getFreeSlot: function() {
		for (var i=0; i<this._hold+1; i++)
			if (typeof(this._req[i]) == 'undefined' || this._req[i].readyState == 4)
				return i;
		return -1; // nothing found
	},
	
	isPolling: function() {
	   return (this._hold == 0)
	},
	
	setPollInterval: function(timerval) {
		if (!timerval || isNaN(timerval)) {
			this.oDbg.log("Invalid timerval: " + timerval,1);
			return -1;
		}
		if (!this.isPolling()) {
			_timerval: 2000;
			return -1;
		}
		if (this._min_polling && timerval < this._min_polling*1000)
			this._timerval = this._min_polling*1000;
		else if (this._inactivity && timerval > this._inactivity*1000)
			this._timerval = this._inactivity*1000;
		else
			this._timerval = timerval;
		return this._timerval;
	},
	
	_setupRequest: function(async, req, opts) {
		var options = {
			asynchronous: async,
			postBody: req,
            method: 'post'
		}
		
		Object.extend(options, opts || {});
		
		// Remove the '.transport' by-and-by
		return new Ajax.Request(this.http_base + '/' + this.reqnum++, options).transport;
	},
	
	_getRequestString: function(xml) {
		this._rid++;
			
		var reqstr = "<body rid='"+this._rid+"' sid='"+this._sid+"' xmlns='http://jabber.org/protocol/httpbind' ";
		if (JSJaC_HAVEKEYS) {
			reqstr += "key='"+this._keys.getKey()+"' ";
			if (this._keys.lastKey()) {
				this._keys = new JSJaCKeys(hex_sha1,this.oDbg);
				reqstr += "newkey='"+this._keys.getKey()+"' ";
			}
		}
		if (xml) {
			reqstr += ">" + xml + "</body>";
		} else {
			reqstr += "/>"; 
		}
		 
		return reqstr;
	},
	
	_prepareResponse: function(req) {
		if (!this.connected()) {
		   this.oDbg.log("Not connected", 1);
			return null;
		}
	
		if (typeof(req) == 'undefined' || !req) {
		   this.oDbg.log("No request", 1);
			return null;
		}
	
		/* handle error */
		try {	// When things go bad, they go really bad
			if (req.status != 200 && req.status != 0) {
				this.oDbg.log("invalid response:\n" + req.responseText,1);
				clearTimeout(this._timeout); // remove timer
				this._connected = false;
				this.oDbg.log("Disconnected.",1);
				this.handleEvent('ondisconnect');
	   			this.handleEvent('onerror',JSJaCError(req.status,'cancel','service-unavailable'));
	   			/*
				if (req.status < 500)
					this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
				else
					this.handleEvent('onerror',JSJaCError('503','cancel','service-unavailable'));
				*/
				return null;
			}
		} catch (e) {
			this.oDbg.log("Whoa:\n" + e,1);
			clearTimeout(this._timeout); // remove timer
			this._connected = false;
			this.oDbg.log("Disconnected.",1);
			this.handleEvent('ondisconnect');
   			this.handleEvent('onerror',JSJaCError(null,'cancel','service-unavailable'));
 			return null;
		}
	
		if (!req.responseXML) {
		   this.oDbg.log("no responseXML", 1);
			return null;
		}
	
		// Check for errors from the server
		var body = req.responseXML.firstChild;
		if (body.getAttribute("type") == "terminate") {
			this.oDbg.log("invalid response:\n" + req.responseText,1);
			clearTimeout(this._timeout); // remove timer
			this._connected = false;
			this.oDbg.log("Disconnected.",1);
			this.handleEvent('ondisconnect');
			this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
			return null;
		}
	
		return req.responseXML;
	},
	
	_connSuccess: function(transport) {
	   this.showMsg("Connected");
		this.oDbg.log("connSuccess", 2);
		if (!transport.responseXML || !transport.responseXML.firstChild) {
			this._connError();
			return;
		}
		
		var body = transport.responseXML.firstChild;
	
		// get session ID
		this._sid = body.getAttribute('sid');
		this.oDbg.log("got sid: "+this._sid,2);
	
		// get attributes from response body
		if (body.getAttribute('polling'))
			this.min_polling = body.getAttribute('polling');
	
		if (body.getAttribute('inactivity'))
			this.inactivity = body.getAttribute('inactivity');
		
		if (body.getAttribute('requests'))
			this._setHold(body.getAttribute('requests'));
	
		// must be done after response attributes have been collected
		this.setPollInterval(this.__timerval);
	
		this._connected = true;
	
		/* wait for initial stream response to extract streamid needed
		 * for digest auth
		 */
		// extract stream id used for non-SASL authentication
		if (body.getAttribute('authid')) {
			this.streamid = body.getAttribute('authid');
			this.oDbg.log("got streamid: "+this.streamid,2);
		} else {
			this._timeout = setTimeout(this._sendEmpty.bind(this),this.getPollInterval());
			return;
		}
		
		if (this.register)
			this._doReg();
		else
			this._doAuth();
		
		/* start sending from queue for not polling connections */
	 	if (!this.isPolling())
	 	  this._sendQueue();  // Force a second connection
/*     		this.intervalId = setInterval(this._sendQueue.bind(this),100);   */
	},
	
	_connError: function(transport) {
	   this.showMsg("Connect Error");
		this.oDbg.log("connError", 2);
		this._handleResponse(transport);
		this._sendQueue();
//   		this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
		return false;
	},
	
	_connException: function(transport, e) {
	   this.showMsg("Connect Exception");
		this.oDbg.log("connException", 2, e);
		this._handleResponse(transport);
		this._sendQueue();
//   		this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
		return false;
	},
	
	connect: function(http_base,server,domain,username,resource,pass,timerval,register,portno,secure) {
		// initial request to get sid and streamid
	
		this.http_base = http_base || '/';
		this.server = server || 'localhost';
		this.domain = domain;
		this.username = username;
		this.resource = resource;
		this.pass = pass;
		this.register = register;
		this.__timerval = timerval;
		if (portno)
			this.portno = portno;
		else
			this.portno = '5222';
		if (secure)
			this.secure = secure;
		else
			this.secure = false;
		var route = "xmpp:"+this.server+":"+this.portno;
		
		this.oDbg.log("http_base: " + this.http_base + "\nserver:" + server,2);
	
		this._rid  = Math.round( 100000.5 + ( ( (900000.49999) - (100000.5) ) * Math.random() ) );
	
		var reqstr = '';
		if (JSJaC_HAVEKEYS) {
			this._keys = new JSJaCKeys(hex_sha1,this.oDbg); // generate first set of keys
			key = this._keys.getKey();
			reqstr += "<body hold='"+this._hold+"' xmlns='http://jabber.org/protocol/httpbind' to='"+this.domain+"' wait='"+this._wait+"' rid='"+this._rid+"' newkey='"+key+"' secure='"+this.secure+"' route='"+route+"'/>";
		} else
			reqstr += "<body hold='"+this._hold+"' xmlns='http://jabber.org/protocol/httpbind' to='"+this.domain+"' wait='"+this._wait+"' rid='"+this._rid+"' secure='"+this.secure+"' route='"+route+"'/>";
	
		var slot = this._getFreeSlot();
		this.oDbg.log(reqstr,4);
		var successCB = this._connSuccess.bind(this);
		var errorCB = this._connError.bind(this);
		var exceptionCB = this._connException.bind(this);
		
	   this.showMsg("Connect");
		this._req[slot] = this._setupRequest(true, reqstr, {
		   onSuccess: successCB,
		   onFailure: errorCB,
		   onException: exceptionCB
		});
	},
	
	_getStreamID: function(slot) {
	
		this.oDbg.log(this._req[slot].responseText,4);
	
		if (!this._req[slot].responseXML || !this._req[slot].responseXML.firstChild) {
			this.handleEvent('onerror',JSJaCError('500','cancel','service-unavailable'));
			return;
		}
		var body = this._req[slot].responseXML.firstChild;
	
		// extract stream id used for non-SASL authentication
		if (body.getAttribute('authid')) {
			this.streamid = body.getAttribute('authid');
			this.oDbg.log("got streamid: "+this.streamid,2);
		} else {
			this._timeout = setTimeout(this._sendEmpty.bind(this),this.getPollInterval());
			return;
		}
		
		this._timeout = setTimeout(this._process.bind(this),this.getPollInterval());

		if (this.register)
			this._doReg();
		else
			this._doAuth();
	},
	
	
	_disconnSuccess: function(transport) {   
		this.oDbg.log("Disconnected: "+transport.responseText,2);
		this._connected = false;
		this.handleEvent('ondisconnect');
	},
	
	_disconnError: function() {
		return false;
	},
	
	disconnect: function() {    	
		if (!this.connected())
			return;
		
		if (this._timeout)
			clearTimeout(this._timeout); // remove timer
		this._rid++;
		
		var reqstr = "<body type='terminate' xmlns='http://jabber.org/protocol/httpbind' sid='"+this._sid+"' rid='"+this._rid+"'";
		if (JSJaC_HAVEKEYS) {
			reqstr += " key='"+this._keys.getKey()+"'";
		}
		reqstr += "><presence type='unavailable' xmlns='jabber:client'/></body>"
	 
		var successCB = this._disconnSuccess.bind(this);
		var errorCB = this._disconnError.bind(this);
		
		var xmlhttp = this._setupRequest(true, reqstr, {
		   onSuccess: successCB,
		   onFailure: errorCB
		});
	}
});
