ClientAPI = {};
ClientAPI.BaseURL = location.protocol + '//' + location.hostname + '/admin/plugin/';

ClientAPI.Cache = {};
ClientAPI.Promise = function() {
	this._ready = false;
	this._queue = [];
}
ClientAPI.Promise.prototype = {
	_dispatch: function() {
		while (this._queue.length) {
			try {
				var f = this._queue.shift();
				f.method.apply(f.self, [ this._value, this._request.status, this._request ]);
			} catch (e) {
				if (window.console)
					window.console.log(e, e.stack);
			}
		}
	},
	setRequest: function(r) {
		this._request = r;
	},
	setValue: function(v) {
		this._value = v;
		this._ready = true;
		this._dispatch();
	},
	getValue: function(f, pThis) {
		if (arguments.length == 0)
		{
			return this._ready ? this._value : null;
		}
		if (!pThis) pThis = this;
		if (this._ready)
			f.apply(pThis, [ this._value, this._request.status, this._request ]);
		else
			this._queue.push({ method: f, self: pThis });
	}
};

ClientAPI.emptyCache = function() {
	ClientAPI.Cache = {};
}

ClientAPI._ajax = function(plugin, endpoint, options) {
	if (options.type && options.type != 'GET')
	{
		ClientAPI.Cache = {};
	}
	
	if (!endpoint)
		endpoint = "";
		
	if (ClientAPI.Cache[plugin] && ClientAPI.Cache[plugin][endpoint])
		return ClientAPI.Cache[plugin][endpoint];
	
	var P = new ClientAPI.Promise;
	if (!options.type || options.type == 'GET') {
		if (!ClientAPI.Cache[plugin])
			ClientAPI.Cache[plugin] = {};
		ClientAPI.Cache[plugin][endpoint] = P;
	}
	
	if (plugin != '__literal__')
		options.url = ClientAPI.BaseURL + plugin + '/' + endpoint;
	else
		options.url = endpoint;
		
	options.complete = function(req) {
		if (req.status == 204 || (req.status >= 300 && req.status < 400))
			P.setValue(null); // There is no content
		else
			P.setValue(JSON.parse(req.responseText));
		
		var cch = req.getResponseHeader('Cache-Control');
		if (cch && /no-store/.test(cch)) {
			// Cache-Control: ..., no-store, ...
			// We treat this as a flag that the response isn't to be used again.
			if (ClientAPI.Cache[plugin])
				delete ClientAPI.Cache[plugin][endpoint];
		}
	}
	P.setRequest($.ajax(options));
	return P;
}

ClientAPI._regex 	= /^\/admin\/plugin\/([^/]+)\/(.*)/;
ClientAPI._regex2	= /^\/plugin\/([^/]+)\/(.*)/;
ClientAPI._regexURL = /^http(s)?:\/\/[a-zA-Z0-9.]+\/(admin\/)?plugin\//;

ClientAPI._encodeForm = function(form)
{
	var d = {'_top_': {}};
	function makeNode(name, value, parent, parentName) {
		if (!parent) { parent = d; parentName = '_top_'; };
	
		name = name.replace(/\[([^\]]+)\]/g, '.$1').replace(/\[\]/g, '.__push__');
		var items = name.split('.');
		if (items.length == 1) {
			if (name == '__push__') {
				if (!(parent[parentName] instanceof Array))
					parent[parentName] = [];
				parent[parentName].push(value);
			}
			else
			{
				if (!parent[parentName])
					parent[parentName] = {};
				parent[parentName][name] = value;
			}
		} else {
			if (!parent[parentName])
				parent[parentName] = {};
			var nn = items.shift();
			makeNode(items.join('.'), value, parent[parentName], nn);
		}
	}
	
	$(":input[name]", form).each(function() {
		if (this.type == 'checkbox' || this.type == 'radio')
		{
			if (!this.checked) return;
		}
		
		if (this.nodeName.toLowerCase() == 'select' && this.multiple)
		{
			var items = [];
			for (var i = 0; i < this.options.length; i++) {
				if (this.options[i].selected) items.push(this.options[i].value);
			}
			if (items.length)
			{
				for (var i = 0; i < items.length; i++)
					makeNode(this.name, items[i]);
			}
		}
		else
		{
			makeNode(this.name, this.value);
		}
	});
	
	return d._top_;
}

ClientAPI._get = function(plugin, endpoint) {
	return ClientAPI._ajax(plugin, endpoint, { });
}
ClientAPI._post = function(plugin, endpoint, data) {
	// If 'data' is an HTML <form> element, read it and turn it into a JS object instead, handling '[...]' refs and such.
	if (data.nodeName && data.nodeName.toLowerCase() == 'form' && data.ownerDocument == document)
	{
		data = ClientAPI._encodeForm(data);
	}
		
	return ClientAPI._ajax(plugin, endpoint, {
		type: 'POST',
		contentType: 'application/json',
		data: JSON.stringify(data),
		processData: false
	});
}
ClientAPI._move = function(plugin, endpoint, target) {
	return ClientAPI._ajax(plugin, endpoint, {
		type: 'MOVE',
		headers: {
			Destination: target
		}
	});
}

ClientAPI._put = function(plugin, endpoint, data) {
	if (data.nodeName && data.nodeName.toLowerCase() == 'form' && data.ownerDocument == document)
	{
		data = ClientAPI._encodeForm(data);
	}

	return ClientAPI._ajax(plugin, endpoint, {
		type: 'PUT',
		contentType: 'application/json',
		data: JSON.stringify(data),
		processData: false
	});
}
ClientAPI._remove = function(plugin, endpoint) {
	return ClientAPI._ajax(plugin, endpoint, { type: 'DELETE' });
}
ClientAPI._search = function(plugin, endpoint, data) {
	return ClientAPI._ajax(plugin, endpoint, {
		type: 'SEARCH',
		contentType: 'application/json',
		data: JSON.stringify(data),
		processData: false
	});
}

var q = (function() {
	var n = [ 'get', 'post', 'put', 'remove', 'search', 'move' ];

	var _genRequestType = function(rt) {
		return function() {
			// console.log(arguments);
			if (ClientAPI._regexURL.test(arguments[0])) {
				// URL was passed...
				var args = [].slice.apply(arguments, []);
				args.unshift("__literal__");
				return rt.apply(this, args);
			}
			
			if (ClientAPI._regex.test(arguments[0]) || ClientAPI._regex2.test(arguments[0])) {
				var m = ClientAPI._regex.exec(arguments[0]);
				if (!m) m = ClientAPI._regex2.exec(arguments[0]);
				// console.log("Path was specified for request: " + arguments[0] + " -- " + m[1] + " / " + m[2]);				
				var ap = [ m[1], m[2] ];
				for (var i = 1; i < arguments.length; i++)
					ap.push(arguments[i]);
				return rt.apply(this, ap);
			}	
			return rt.apply(this, arguments);
		}
	}
	
	while (n.length) {
		var p = n.shift();
		// console.log(p, ClientAPI['_' + p]);
		ClientAPI[p] = _genRequestType(ClientAPI['_' + p]);
	}
});

q();

(function() {
	if (!/^\/admin/.test(location.pathname)) {
		var P = document.getElementsByTagName("base")[0].getAttribute("href");
		if (P.charAt(P.length - 1) != '/') P += '/';
		P += "plugin/";
		ClientAPI.BaseURL = P;
	}
})();


