/**
 * Copyright Franson Technology AB, Sweden, 2009
 * http://franson.com, http://gpsgate.com
 * <p>
 * low-level methods to interact with GpsGate Server WS
 * (The service API proxies depend on this)
 * </p>
 * author Fredrik Blomqvist
 *
 * @module GpsGate
 *
 */

var GpsGate = GpsGate || {};
/**
 * namespace
 * @class GpsGate.Server
 * @static
 */
GpsGate.Server = GpsGate.Server || {};


/**
 * todo: rename getGpsGateServerRelativePath?
 * todo: create method to create absolute path from relative?
 * todo: deprecate? (only used in BT and webmap)
 * @method getServerName
 * @return {string} server name url (including protocol and port number)
 */
GpsGate.Server.getServerName = function()
{
	var wl = window.location;
	var path = wl.href;

	// drop two dir levels from the end (our relative installation/setup depth)
	// todo: create regexp instead(?)
	var idx = path.lastIndexOf('/');
	if (idx != -1)
	{
		path = path.substring(0, idx);

		var proto = wl.protocol + '//';
		idx = path.lastIndexOf('/');
		if (idx != (path.lastIndexOf(proto) + proto.length)) // make sure we don't go as far as the protocol
		{
			path = path.substring(0, idx);
		}
	}
	return path;
};

/**
 * same as Franson.Util.getUniqueId but added here to avoid dependency
 * @type integer
 * @private
 */
GpsGate.Server._getCallId = MochiKit.Base.counter(); // todo: or make this lazy intialized to reduce script startup race with Mochi?

// support native JSON serialization (FF 3.1, IE8, Chrome, Safari) (and possibly if injecting json(2).js in the page)
// (we assume MochiKit.Base.serializeJSON already has a Date formatter registered that converts to ISO stamps)
// (todo: could rather add this kind of code to MochiKit I guess?)
GpsGate.Server._toJSON = (typeof(JSON) != 'undefined' && typeof(JSON.stringify) == 'function') ? JSON.stringify : MochiKit.Base.serializeJSON;
//GpsGate.Server._toJSON = MochiKit.Base.serializeJSON;
// todo: _parseJSON also (currently we "cheat" with dates but we can acchieve same effect since parse supports a post-processor function) (todo: profile speed in this case)


/**
 * standard WS-call method (used by the Service proxies)
 * todo: allow choosing between GET/POST calls?
 * @method call
 * @param {string} url
 * @param {string} method
 * @param {object} [params]
 * @return {Deferred}
 */
GpsGate.Server.call = function(url, method, params)
{
	params = params || {};

	var d = MochiKit.Async.doXHR(url, {
		'method': 'POST',
		'headers': {
			'Content-Type': 'application/json; charset=utf-8',
			'X-JSON-RPC': method
		},
		'sendContent': GpsGate.Server._toJSON({
			'id': GpsGate.Server._getCallId(),
			'method': method,
			'params': params
		})
	});

/*	// same as above but using GET
	// (could use loadJSONDoc also but that uses a slightly less efficient pre eval-check)
	// (now this is same as doSimpleXMLHttpRequest)
	var d = MochiKit.Async.doXHR(url + '/' + method, {
		'method': 'GET',
		'headers': {
			'Content-Type': 'application/json; charset=utf-8',
			'X-JSON-RPC': method
		},
		// ! note that this currently relies on a patched queryString that
		// formats DateTimes in same way as our json-spec (ISO-stamp) (isn't that REST "standard"?)
		// (todo: create a queryStringJSON that uses the same logic as Mochi.serializeJSON?)
		'queryString': params // todo: do we need an id? (or other no-cache stuff?)
	});
*/

	return d.addCallback(
		function(request)
		{
			// eval JSON and forward possible JayRock error
			var response = eval('(' + request.responseText + ')'); // todo: use JSON.parse if exists (needs special case for Dates though)

			if (typeof(response.error) != 'undefined')
			{
				var error = response.error;

				var e = new Error('GpsGate.Server.call("' + method + '"):' + error.message); // todo: can('t) we set the .name property?
				e.nativeError = error;

				throw e;
			}
			return response.result;
		}
	);
};


/**
 * container for hooks to the XSS calls
 * @private
 * @type object
 */
GpsGate.Server._callback = {};


/**
 * same as above but using JSONP/XSS calling
 * todo: could use Mochi.DOM for some stuff here but better to keep dependencies down(?)
 * todo: would actually be possible to make this cancellable by using a
 * middle man as callback, but not sure it's worth it.
 * todo: see http://trac.mochikit.com/ticket/258
 * todo: not fully tested..
 * @private // don't expose yet
 */
GpsGate.Server.callJSONP = function(url, method, params)
{
	var id = GpsGate.Server._getCallId();
	var callId = '_' + id;
	var nodeId = 'GpsGateServer_JSONP' + callId;

	function cleanup()
	{
		delete GpsGate.Server._callback[callId];
		MochiKit.DOM.removeElement(nodeId); // or would it be better to just keep a ref to the actual node? (should be faster atleast)
	}

	var d = new MochiKit.Async.Deferred(
		function canceller(def)
		{
			// is it safe to just remove everything?
			// shold we need to keep a placeholder? (test in different browsers)
			// GpsGate.Server._callback[callId] = MochiKit.Base.noop;
			cleanup();
		}
	);

	// add to pool
	GpsGate.Server._callback[callId] = function(response)
	{
		cleanup();

		if (typeof(response.error) != 'undefined')
		{
			d.errback(new Error('GpsGate.Server.callJSONP("' + method + '"): ' + response.error.message));
		}
		else
		{
			d.callback(response.result);
		}
	};

	document.getElementsByTagName('head')[0].appendChild(
		MochiKit.DOM.createDOM('script', {
			'type': 'text/javascript',
			'id': nodeId,
			'src': (
				url + '/' + method +
				'?jsonp=GpsGate.Server._callback.' + callId +
				'&' + MochiKit.Base.queryString(params) +
				'&noCache=' + (new Date()).getTime().toString().substr(5) + id
			)
		})
	);

	return d;
};


// convenience
GpsGate.Server.callAuth = function(url, method, params)
{
	params = params || {};
	params.appId = Context.Session.ApplicationId;
	return GpsGate.Server.call(url, method, params);
};
