/**
 * wrapped comet functionality
 * two concreate wrappers required:
 * msie and gecko
 */


/**
 * In a nutshell this class will make an ajax call to the url, 
 * then monitor the received data, 
 * calling a user-specified function when data shows up.
 */
function CometClient(url, options)
{
	this.options = $.extend(CometClient.DefaultOptions, options);
	this.options.url = url;
	
	//val's host fix for a commet
	//this.options.url = url.replace('stream-wrapper', 'stream-wrapper.php');
	
	//instantiate stream wrapper and start streaming
	this._streamWrapper = new StreamWrapper(this.options);
	this.startStream();
	
}


/**
 * defaults for comet client
 * 
 * @var Object
 */
CometClient.DefaultOptions = 
{
	checkDataFrequency: 500,
	resetConnectionFrequency: 45000,
	onUpdate: null,
	onReset: null,
	url: null
};

/**
 * start stream
 * @return {null}
 */
CometClient.prototype.startStream = function()
{
	this._streamWrapper.startStream();
}


/**
 * stop streaming
 * 
 * @return boolean
 */
CometClient.prototype.stopStream = function()
{
	return this._streamWrapper.stopStream();
};






/**
 * 
 * 
 * @param Object options
 * @return null
 */
function StreamWrapper(options)
{
	this._options = options;
	this._streamWrapper = null;
	
	if(true == $.browser.mozilla){
		this._streamWrapper = new StreamWrapperGecko(this._options);
	} else {
		//works for every browser
		this._streamWrapper = new StreamWrapperIframe(this._options);
	}
}

/**
 * initiate stream through the specific wrapper
 * 
 * @return boolean
 */
StreamWrapper.prototype.startStream = function()
{
	return this._streamWrapper.startStream();
};


/**
 * stop streaming
 * 
 * @return boolean
 */
StreamWrapper.prototype.stopStream = function()
{
	return this._streamWrapper.stopStream();
};



/**
 * iFrame stream wrapper
 * uses iFrame to load data from server
 * 
 * @param {Object} options
 * @return {StreamWrapperIframe}
 */
function StreamWrapperIframe(options)
{
	this._options = options;
	//append iframe flag to query
	this._options.url += '&iframe=on';
	
	//create hiden iframe and append it to body
	this._iframe = null
	
	var thisStr = this;
	
	//event listener for multiple items list
	window.UpdateBidDisplays = function(data){
		thisStr.iFrameUpdateItems(data);
	};
	
	//event listener for singe auction item
	window.UpdateBidlistDisplay = function(html){
		thisStr.iFrameUpdateBidList(html);
	};
	
	return;
}


/**
 * start streaming
 * set up iframe, assigne events
 * 
 * @return boolean
 */
StreamWrapperIframe.prototype.startStream = function()
{
	this._iframe = document.createElement('iframe');
	$(this._iframe).css({display:'none'});
	document.body.appendChild(this._iframe);

	var thisRef = this;
	
	//set on load even for ifreme
	//once page is loaded - reload it again
	$(this._iframe).load(function(){
		thisRef.restartIframe();	
	});
	//start streaming
	this.restartIframe();
};


/**
 * restart iframe loading
 * 
 * @return null
 */
StreamWrapperIframe.prototype.restartIframe = function()
{
	//reset iframe src, append random token to prevent ie caching
	var date = new Date();
	//start loading data
	this._iframe.src = this._options.url + '&nocache=' + date.getTime();
	return;
}



/**
 * stop listening
 * remove iframe from the body
 * 
 * @return null
 */
StreamWrapperIframe.prototype.stopStream = function()
{
	if (this._iframe) {
		this._iframe.src = '';
		document.body.removeChild(this._iframe);
	}
	return true;
}; 


/**
 * check on update of the content
 * of iframe
 * 
 * @return boolean
 */
StreamWrapperIframe.prototype.iFrameUpdateItems = function(data)
{
	if(this._options.onUpdate){ //pass data to event listeners
		this._options.onUpdate('@STT@'+data+'@END@');
	}
	return true;
};


/**
 * reload bid list for a single item on auction
 * 'html' - returned from the server as an html list
 * of div tags with users and their bids
 * 
 * pass it to event listeners
 * 
 * @return boolean
 */
StreamWrapperIframe.prototype.iFrameUpdateBidList = function(html)
{
	if(this._options.onUpdate){
		this._options.onUpdate(html);
	}
	return true;
};




/**
 * stream wrapper for gecko browsers
 * that uses XMLHttpRequest to get notifications from
 * comet server
 * 
 * this was original wrapper for
 * firefox and gecko browsers through XMLHttpRequest
 * 
 * !!NOT COMPLETE, DO NOT USE WITH OUT TESTING
 * 
 * @return null
 */
function StreamWrapperGecko(options)
{
	this._options = options;
	
	/**
	 * current running request
	 * @var Object
	 */
	this._xMLHttpRequest = null;
	
	/**
	 * stream buffer
	 * to run checks against
	 * 
	 * @var String
	 */
	this._streamBuffer = '';
	
	/**
	 * time that a last connection was made
	 */
	this._lastConnectionTime = new Date();
	
	
	/**
	 * this is a number of short requests
	 * that has ocured in a range of less
	 * than 1 seccond between each
	 * 
	 * its a protection in case there is 
	 * a server error so browser don't go
	 * craisy by trying to connect each 
	 * milisecond
	 */
	this._numberOfShortRequests = 0;
	
};

/**
 * start listening stream from server
 * set up xhr connection and listen to 
 * server responses
 * 
 * @return boolean
 */
StreamWrapperGecko.prototype.startStream = function()
{
	if(this._xMLHttpRequest && this._xMLHttpRequest.readyState >= 1){
		//there already request running
		return;
	}
	
	var thisStr = this, thisRequestTime = new Date(), 
	xhropt = {url:this._options.url, timeout:this._options.resetConnectionFrequency};
	
	xhropt.beforeSend = function(XMLHttpRequest){
		thisStr._xMLHttpRequest = XMLHttpRequest;
		thisStr._streamBuffer = '';
		
		//check on the time between requests, make sure it's over a second
		if((thisRequestTime.getTime() - thisStr._lastConnectionTime.getTime()) < 1000 ){
			thisStr._numberOfShortRequests++;
		} else {
			thisStr._numberOfShortRequests = 0;
		}
		//update last connection time with this connection time
		thisStr._lastConnectionTime = thisRequestTime;
		
		
		//if browser tries to connect to comet-wrapper
		//with in interval between requests less than 1 second
		//there is an error in connection, and we can't continue
		if(thisStr._numberOfShortRequests > 10){
			thisStr._xMLHttpRequest.abort();
			thisStr._xMLHttpRequest = null;
			throw new Error("Can't establish reliable connection to comet server.");
		}
		
		thisStr._xMLHttpRequest.onreadystatechange = function(){
			thisStr._checkStreamBuffer();
		};
	};
	
	xhropt.complete = function(){
		if(thisStr._xMLHttpRequest){ //no error ocured
			//just in case check stream buffer one more time
			thisStr._checkStreamBuffer();
			//just in case abort(close) connection
			thisStr._xMLHttpRequest.abort();
			//unset current object
			thisStr._xMLHttpRequest = null;
			//empty out stream buffer
			thisStr._streamBuffer = '';
		}
		//start new stream in a time out function
		setTimeout(function(){thisStr.startStream();}, thisStr._options.checkDataFrequency);
	};
	
	xhropt.error = function(){
		//something wrong with connection?
		thisStr._xMLHttpRequest = null;
		//no check on buffer just empty it out
		thisStr._streamBuffer = '';
		
		//try to start new stram
		setTimeout(function(){thisStr.startStream();}, thisStr._options.checkDataFrequency);
	};
	
	$.ajax(xhropt);
};

/**
 * stop listening to the stream
 * 
 * @return boolean
 */
StreamWrapperGecko.prototype.stopStream = function()
{
	if(this._xMLHttpRequest){
		this._xMLHttpRequest.abort();
		this._xMLHttpRequest = null;
	}
	return true;
};


/**
 * check streaming buffer on changes
 * called from XMLHttpRequest on state change
 * 
 * @return boolean
 */
StreamWrapperGecko.prototype._checkStreamBuffer = function(){
	//do check if XMLHttpRequest is set and text available
	if(this._xMLHttpRequest && this._xMLHttpRequest.responseText){
		if(this._streamBuffer.length != this._xMLHttpRequest.responseText.length){
			var newStreamData = this._xMLHttpRequest.responseText.replace(this._streamBuffer, '');
			this._streamBuffer = this._xMLHttpRequest.responseText;
			
			if(this._options.onUpdate){ //update listeners
				this._options.onUpdate(newStreamData);
			}
		}
	}
	return true;
};











