JSONProxyDeferred = Class.create();

JSONProxyDeferred.InvalidResponse = new Error('Invalid response from server.');
JSONProxyDeferred.MalformedResponse = new Error('Malformed response from server.');

JSONProxyDeferred.prototype = 
{
	registered: [],
	
	initialize: function (URI, namespace)
	{
		if (URI)
		{
			this.setURI(URI);
		}
		if (namespace)
		{
			this.namespace = namespace;
		}
		this.errorHandlers = new Array();
	},
	
	
	error: function (errBack)
	{
		this.errorHandlers.push(errBack);
	},
	
	
	callErrbacks: function (e)
	{
		this.errorHandlers.each(function (eb)
		{
			eb(e);
		});
	},
 	
	
	setURI: function (URI)
	{
		this.URI = URI;
	},
	
	
	register: function (methods)
	{
		if (typeof methods == "string")
		{
			this._register(methods);
			return;
		}
		methods = $A(methods);
		methods.each(this._register.bind(this));
	},
	
	
	_register: function (method_name)
	{
		if (typeof method_name != "string")
		{
			throw new Error('method_name must be of type string');
		}
		
		var me = this;
		this[method_name] = function()
		{
			var m = method_name;
			if (me.namespace)
			{
				m = me.namespace+'.'+method_name;
			}
			return me.call(m, $A(arguments));
		};
		
		this.registered.push(method_name);
	},
	
	
	reflect: function ()
	{
		var d = this.call('reflect', [this.namespace]);
		d.addCallback(this.register.bind(this));
	},
	
	
	call: function (method_name, params)
	{
		if (jQuery)
		{
			jQuery('body').css('cursor', 'wait');
		}
		
		var request = {
			method: method_name,
			params: params
		};
		
		var d = new Deferred;
		var reqObj = new Ajax.Request(
		this.URI,
		{
			method: 'post',
			parameters: 'request='+Object.toJSON(request),
			onComplete: function (req)
			{
				if (!req.responseText)
				{
					d.errback(JSONProxyDeferred.InvalidResponse);
					return false;
				}
				
				var response = req.responseText.parseJSON();
				
				if (!response)
				{
					d.errback(JSONProxyDeferred.MalformedResponse);
					return false;
				}
				
				if (response.error)
				{
					d.errback(response.error);
					return false;
				}
				
				d.callback(response.result);
			}
		});
		return d;
	}
};


var Deferred = Class.create();
Deferred.prototype = 
{
	initialize: function ()
	{
		this.callbacks = new Array;
		this.result = null;
		this.fresh = true;
		
		this.errbacks = new Array;
		this.error = null;
		this.dead = false;
	},
	
	callback: function (data)
	{
		if (jQuery)
		{
			jQuery('body').css('cursor', 'auto');
		}
		
		this.fresh = false;
		
		var result = data;
		for (var i=0; i<this.callbacks.length; i++)
		{
			if (!this.dead)
			{
				var clbk = this.callbacks[i];
				result = clbk(result);
			}
		}
		this.result = result;
	},
	
	
	addCallback: function (clbk)
	{
		if (this.dead)
		{
			throw new Error('This deferred is dead: '+this.error.getMessage());
		}
		
		this.callbacks.push(clbk);
		
		if (!this.fresh)
		{
			clbk(this.result);
		}
	},
	
	
	errback: function (e)
	{
		if (jQuery)
		{
			jQuery('body').css('cursor', 'auto');
		}
		
		this.dead = true;
		this.error = e;
		
		for (var i=0; i<this.errbacks.length; i++)
		{
			var clbk = this.errbacks[i];
			clbk(e);
		}
	},
	
	
	addErrback: function (clbk)
	{
		this.errbacks.push(clbk);
		
		if (this.dead)
		{
			clbk(this.error);
		}
	}
};

