dojo.provide("tests.MockXMLHttpRequest");

/* summary:
 *   MockXMLHttpRequest implements the XMLHttpRequest interface to allow testing 
 *   without having to run a server. It can work with dojo.io.bind and
 *   dojo.hostenv.getText by using the setMockXMLHttpRequest() function to 
 *   set dojo.hostenv.getXmlhttpObject use a MockXMLHTTPRequest.
 *   tests.MockXMLHttpRequest.restore() restores the original one.
 */
 
/*
examples:
	var responseText = "this is a test!";
	tests.MockXMLHttpRequest.set(responseText, 1000); //simulate a 1 sec response time
	dojo.hostenv.getText('urn:this_is_ignored', 
		function(data) { 
			jum.assertTrue(data == responseText);
		});	
	tests.MockXMLHttpRequest.restore(); 

	tests.MockXMLHttpRequest.set(function(xhr) { return xhr.data; }, 100);  
	var postData = "test post data";
	dojo.io.bind({
		url: "urn:ignore", 
		method :"post",
		postContent :  postData,
		mimetype: "text/plain" ,		
		handle: function(type, data, evt) {
			if(type == "load"){
				jum.assertTrue(data == postData);
			}
		}
	}); 
	tests.MockXMLHttpRequest.restore(); 
*/

dojo.require('dojo.event.*');

tests.MockXMLHttpRequest = function(responseText, /* milliseconds */ responseTime, responseXml) {
	if (typeof responseText == "function") {
		this._responseFunc = responseText; 
		this.responseText= null;
	} else {
		this.responseText=responseText;
		this._responseFunc = null;
	}	 
	this._responseHeaders = '';
	this.responseXml= responseXml; //note: this isn't used by BrowserIO
	this._delay = responseTime;
	this.readyState = 0; 
	this.onreadystatechange = null;
	this._timeout = null;
	this._timeoutFunc = null;
	this._aborted = false;

	this.open = function(method, url, async) { 
		this._async = async;
		this.readyState = 1; 
		this.url = url;
	};

	this._finish  = function() {
	    if (this._aborted)
	       return;
		this.readyState = 4;
		this.status = 200;
		this.statusText = "OK"; 
		if (this._responseFunc)
			this.responseText = this._responseFunc(this);
		if (this.onreadystatechange)
			 this.onreadystatechange(this);
	 };
		 
	this.send = function(data) { 
	    this._aborted = false;
		this.data = data;
		if (this._async !== undefined && !this._async)	{
			this._finish();
		 } else {
			this.readyState = 2;
			var self = this;
			this._timeout = setTimeout( function() { 
				self._timeout = null; self._finish(); 
			}, this._delay);

		}
	};

	this.abort = function() {
	    this._aborted = true;
		this.readyState = 0;
		if (this._timeout) {
			clearTimeout(this._timeout);
			this._timeout = null;
		}
	};

	this.getAllResponseHeaders = function() { return this._responseHeaders; }
	this.getResponseHeader = function(header) { return ""; }
	this.setRequestHeader = function(header, value) {  }

};

tests.MockXMLHttpRequest._originalXmlhttpObjectGetter = dojo.hostenv.getXmlhttpObject;

tests.MockXMLHttpRequest.set = function(responseTextOrFunc, /* milliseconds */responseTime,
	responseXml) {
	var oldFunc = dojo.hostenv.getXmlhttpObject;
	dojo.hostenv.getXmlhttpObject  = function() {
		return new tests.MockXMLHttpRequest(responseTextOrFunc, 
			responseTime, responseXml); 
	}
	return oldFunc;
}

tests.MockXMLHttpRequest.restore = function() {
	dojo.hostenv.getXmlhttpObject = tests.MockXMLHttpRequest._originalXmlhttpObjectGetter;
}

tests.MockXMLHttpRequest.wrap = function(scope, func, responseTextOrFunc, responseTime, responseXml) {
	 var disconnectHack = false;  
	 //disconnect is broken!: hasAdvice is doesn't find the advice 
	 //because "__0" != "__0$joinpoint$method"
     var aroundFunc = function(invocation) {
     	if (!disconnectHack)
        	tests.MockXMLHttpRequest.set(responseTextOrFunc, responseTime, responseXml);
        try {
            var result = invocation.proceed();
        } finally { 
            var ret = dojo.event.disconnect("around", scope, func, aroundFunc);
            if (!disconnectHack)
            	tests.MockXMLHttpRequest.restore();
            disconnectHack = true;
        }
        return result;
    };
    dojo.event.connect("around", scope, func, aroundFunc);
}

if (dojo.hostenv.name_ == 'rhino') {
	var _orginalRhinoIOBind = null;
	var _currentMockRequests = {};

	function _rhinoIOBind(kwArgs) {			
		var tid = java.lang.Thread.currentThread().toString();
		if (!_currentMockRequests[tid]) 
			return _orginalRhinoIOBind.call(this, kwArgs);
	
		var async = kwArgs["sync"] ? false : true;
		var http = dojo.hostenv.getXmlhttpObject(kwArgs);
		//dojo.debug('get mock ' + tid + ' ' + http.responseText);
			
		var query = "";
		if (kwArgs.postContent) 
			query = kwArgs.postContent
		query += dojo.io.argsFromMap(kwArgs["content"] || {},
							kwArgs.encoding);
		
		http.onreadystatechange = function() {
			if (http.readyState != 4)
				return;
			
			var ret;
			if (kwArgs.method.toLowerCase() == "head"){
				// TODO: return the headers
			}else{
				var text = http.responseText;
				if(kwArgs.mimetype == "text/javascript"){
					try{
						ret = dj_eval(text);
					}catch(e){
						dojo.debug(e);
						dojo.debug(text);
						ret = null;
					}
				}else if(kwArgs.mimetype == "text/json"){
					try{
						ret = dj_eval("("+text+")");
					}catch(e){
						dojo.debug(e);
						dojo.debug(text);
						ret = false;
					}
				}else{
					ret = text;
				}
				(kwArgs.load || kwArgs.handle)("load", ret, kwArgs);
			}
		}
		http.open('ignored', 'ignored', async);
		http.send(query);
		kwArgs.abort = function() { http.abort(); };
	}
	
	dojo.hostenv.getXmlhttpObject  = function() {
		var tid = java.lang.Thread.currentThread().toString();
		var mockRequest = _currentMockRequests[tid];
		if (mockRequest) { 
		 	return new MockXMLHTTPRequest(mockRequest[0],mockRequest[1],
		 			mockRequest[2]);
		}else{
			return null; //hostenv doesn't define getXmlhttpObject
		} 
	}

	var _setMockXMLHttpRequest = tests.MockXMLHttpRequest.set;
	tests.MockXMLHttpRequest.set = function(responseTextOrFunc, /* milliseconds */responseTime,
		responseXml) {
		if (!_orginalRhinoIOBind) {
			_orginalRhinoIOBind = dojo.io.RhinoHTTPTransport.bind;
			dojo.io.RhinoHTTPTransport.bind = _rhinoIOBind;
		}
		var tid = java.lang.Thread.currentThread().toString();
		//dojo.debug( 'set mock ' + tid + ' ' + responseTextOrFunc); 
		_currentMockRequests[tid] = arguments;
		_setMockXMLHttpRequest.apply(dj_global, arguments);
	}

	var _restoreXMLHttpRequest = tests.MockXMLHttpRequest.restore;
	tests.MockXMLHttpRequest.restore = function() {
		var tid = java.lang.Thread.currentThread().toString();
		if (_currentMockRequests[tid]) {
			//dojo.debug( 'delete mock ' + tid); 
			delete _currentMockRequests[tid];
		}
		_restoreXMLHttpRequest();
	}
} 



