PandoraApp = {
	_locations: [],
	_locationTimeout: null,
	_callbacks: [],
	_callCount: 0,
	_callMethod: function(/*string*/command, /*hash*/params, /*function*/callback) {
		var callbackID = this._callbacks.push(callback) - 1;
		var paramStr = "";
		for (var name in params) {
			if (params[name] != null) {
				paramStr += paramStr ? "&" : "?";
				paramStr += name + "=" + escape(params[name]);
			}
		}
		
		this._callCount++; //increment immediately so we don't get reaped while waiting for the timeout

		// we need to delay setting location for two reasons:
		// 1) setting location during page load will cause issues,
		// 2) setting location twice in a row will cause first location to be "lost"
		this._locations.push("PandoraApp://" + callbackID + "/" + command + paramStr);
		if (this._locationTimeout == null) {
			this._locationTimeout = setTimeout(function() { PandoraApp._setNextLocation() }, 1);
		}
		
		return callbackID;
	},
	_setNextLocation: function() {
		this._locationTimeout = null;
		if (this._locations.length) {
			window.location = this._locations.shift();
			if (this._locations.length) {
				this._locationTimeout = setTimeout(function() { PandoraApp._setNextLocation() }, 1);
			}
		}
	},
	_methodResponse: function(/*string*/callbackID, /*hash*/response) {
		var callback = this._callbacks[callbackID];
		delete this._callbacks[callbackID];
		if (typeof(callback) == "function") {
			// we need to delay execution a bit to prevent strange (and
			// potentially deadly) interactions with the webview host.
			var _this = this;
			setTimeout(function() {callback(response);_this._decrementCallCount();}, 1);
		} else {
			this._decrementCallCount();
		}
	},
	_decrementCallCount: function() {
		this._callCount--;
		if (this._callCount == 0) {
			window.location = "PandoraApp:/webViewIsIdle";
		}
	},
	echo: function(value, callback) {
		this._callMethod("echo", {value: value}, callback);
	},
	telephone: function(number, callback) {
		this._callMethod("telephone", {number: number}, callback);
	},
	playMovie: function(cellURL, wifiURL, callback) {
		function fixupURL(url) {
			if (url == null) return null;
			
			if (!url.match(/^https?:\/\//)) {
				if (url.charAt(0) == '/') {
					var matched = location.href.match(/^(\w*:\/\/[^\/]*)/);
				} else {
					var matched = location.href.match(/^(.*\/)[^\/]*$/);
				}
				if (matched) {
					url = matched[1] + url;
				}
			}
			return url;
		}
		cellURL = fixupURL(cellURL);
		wifiURL = fixupURL(wifiURL);
		
		this._callMethod("playMovie", {cellURL: cellURL, wifiURL: wifiURL}, callback);
	},
	sendEmail: function(fromAddress, template, callback) {
		this._callMethod("sendEmail", {fromAddress: fromAddress, template: template}, function(results) { callback(results.success) });
	},
	registerImpression: function(url) {
		this._callMethod("registerImpression", {url: url});
	},
	findLocation: function(callback) {
		this._callMethod("findLocation", {}, callback);
	},
	findLocationWithZipCode: function(callback) {
		this._callMethod("findLocation", {withZipCode:1}, callback);
	},
	createStationFromStationId: function(stationId) {
		this._callMethod("createStationFromStationId", {stationId: stationId});
	},
	openSafari: function(url) {
		this._callMethod("openSafari", {url: url});
	},
	canOpenURL: function(url, callback) {
		this._callMethod("canOpenURL", {url: url}, function(results) { callback(results.canOpenURL) });
	},
	alert: function(message) {
		this._callMethod("alert", {message: message});
	}
};
