/**
 * CLASS: JsonRequest
 */
X.createClass('X.json.Request',
	// Constructor
	function Request(id, xhr, options)
	{
		this.id = id;
		this.xhr = xhr;
		this.options = options;
		this.aborted = false;
	
		return this;
	}
);

/**
 * CLASS: RequestEvent
 */
X.createClass('X.json.RequestEvent',
	function RequestEvent(type, target, textStatus, request)
	{
		this.base(type, target);
		this.textStatus = textStatus;
		this.request = request;
	
		return this;
	},
	// Prototype Members
	null,
	// Static Members
	null,
	// Base Class
	X.Event
);

/**
 * SINGLETON: JsonRequestManager
 */
X.createSingleton('X.json.RequestManager',
	// Constructor
	function JsonRequestManager()
	{
		this.base();
		
		this.requests = {};
		this.cache = {};
		
		return this;
	},
	// Prototype Members
	{
		/*
		 * options.url
		 * options.cache
		 * options.abort
		 * options.success
		 * options.error
		 * options.timeout
		 * ex: X.json.RequestManager.call('getUsers', { url: '/gkc/getUsers.jsp', cache: false, abort: true, success: function(data){}, error: function(msg){}, timeout: 20000 });
		 */
		call: function(id, options)
		{
			if (!options || !options.url)
			{
				throw new Error('Missing or invalid options parameter.');
			}
	
			// Set defaults
			options = jQuery.extend({},
			{
				cache: true,
				abort: true,
				type: 'GET',
				data: null,
				success: function(data, textStatus) {},
				error: function(textStatus, msg) {},
				timeout: 20000 //ms
			}, options);
	
			// If data is already return cached data
			if (options.cache && this.cache[options.url])
			{
			    options.success(this.cache[options.url]);
				return;
			}
	
			if (options.abort)
			{
				this.abort(id);
			}
	
			if (!this.requests[id])
			{
				this.requests[id] = {};
			}
	
			this.requests[id][options.url] = new X.json.Request(
				id,
				jQuery.ajax(
				{
					success: this._onSuccessHandler.delegate(this, id, options.url),
					error: this._onErrorHandler.delegate(this, id, options.url),
					cache: options.cache,
					dataType: 'text',
					type: options.type,
					data: options.data,
					url: options.url,
					timeout: options.timeout
				}),
				options
			);
	
			return this;
		},
	
		abort: function(id)
		{
			var requests = this.requests[id];
			if (!requests) { return; }
	
			var request;
			for (var url in requests)
			{
				request = requests[url];
				if (!request.aborted) { break; }
			}
			if (!request) { return; }
	
			request.aborted = true;
	
			var success = false;
			try
			{
				if (typeof(request.xhr.abort) === 'function')
				{
					request.xhr.abort();
					success = true;
				}
				if (typeof(request.xhr.close) === 'function')
				{
					request.xhr.close();
					success = true;
				}
			}
			catch (ex)
			{
			}
			finally
			{
				var textStatus = (success)? 'success' : 'failed';
				this.dispatchEvent(new X.json.RequestEvent('abort', this, textStatus, request));
			}
	
			return this;
		},
	
		abortAll: function()
		{
			for (var id in this.requests)
			{
				this.abort(id);
			}
	
			return this;
		},
	
		_onSuccessHandler: function(data, textStatus, id, url)
		{
			var request = this.requests[id][url];
			
			try
			{
				eval("data = " + data);
			}
			catch (ex)
			{
				this._onErrorHandler(null, 'error', ex, id, url)
				return;
			}
			// Cache the result
			if (request.options.cache)
			{
				this.cache[request.options.url] = data || {};
			}
		
			request.options.success(data);
		},
	
		_onErrorHandler: function(xhr, textStatus, ex, id, url)
		{
			var request = this.requests[id][url];
	
			if (request.aborted) { return; }
	
			var msg = (ex)? ex.message : (textStatus === 'timeout')? 'Timeout! The server is experiencing slower than normal response times.' : 'Unknown Error';
	
			request.options.error(textStatus, msg);
		}
	},
	// Base Class
	X.EventDispatcher
);