function external_interface_receive(data){
	ns11mmPresentation.streetview().receive(data);
};

(function($){
	jQuery.fn.presentationStreetview = function(options){
		var $psv = jQuery(this);

		var options = jQuery.extend({
			lat:40.711645,
			lng:-74.010446,
			yaw:281.18,
			pitch:-23.35,
			overlay_file_name:"../assets/overlay.swf"
		},options);
		
		var pov_cache;
		var loc_cache;
		var initialized = false;
		
		var client;
		var panorama;
		
		var panorama_no_event_timer = null;
		
		var $streetview = $psv.children(".streetview_wrapper").children(".streetview");
		var $overlay = $psv.children(".overlay_wrapper").children(".overlay");
		
		function initialize(sv_opt){
			load_streetview(sv_opt);
			load_overlay(sv_opt);
		};
		$psv.init = function(sv_opt){
			initialize(sv_opt);
		};
		
		function load_streetview(options){
			try{
				if (jQuery.browser.firefox() && jQuery.browser.mac()) {
					if (jQuery.browser.version.string().substr(0, 3) == "2.0") {
						$streetview.attr("style", "width: 962px; height: 577px; z-index: 1; position: absolute; margin-top: 140px;");
					};
				};
				
				client = new GStreetviewClient();
				
				options.lat = 40.711645;
				options.lng = -74.010446;
				options.yaw = 281.18;
				options.pitch = -23.35;
				
				pov_cache = {};
				pov_cache.yaw = options.yaw;
				pov_cache.pitch = options.pitch;
				
				loc_cache = {};
				loc_cache.lat = options.lat;
				loc_cache.lng = options.lng;
				
				var loc = new GLatLng(options.lat,options.lng);
				var pov = {yaw: options.yaw, pitch: options.pitch};
				var panoramaOptions = {latlng:loc , pov:pov};

				panorama = new GStreetviewPanorama($streetview.get(0), panoramaOptions);
				
				GEvent.addListener(panorama, "error", _handleNoFlash);
				GEvent.addListener(panorama, "initialized", _streetviewChangeCompleteHandler);
				
				$psv.trigger("streetviewInitialized");
			} catch(e){
				var data = {
					errorMessage:"[ERROR @ load_streetview]",
					error:e
				};
				ns11mmPresentation.trigger("streetviewError",data);
			};
		};
		
		var streetview_loaded = false;
		$psv.bind("streetviewInitialized",_streetviewInitializedHandler);
		function _streetviewInitializedHandler(e){
			
			ns11mmPresentationLoader.trigger("loadStatusEvent",{pct:5,msg:"Streetview Loading"});
			if(initialized == false){
				streetview_loaded = true;
				if(overlay_loaded == true){
					completeInitialization();
				};
			};
		};
		
		function _streetviewChangeCompleteHandler(init){
			//console.log("streetviewChangeComplete");
			clearTimeout(panorama_no_event_timer);
			var data = { action:"streetviewChangedHandler" };
			send(data);
		};
		
		function load_overlay(sv_opt){
			try{
				var flashvars = {};
				
				flashvars.baseUrl = sv_opt.baseUrl;
				flashvars.mapsAPIKey = sv_opt.mapsAPIKey;
				flashvars.showMenu = sv_opt.showMenu;
				
				var params = {
					wmode:"transparent",
					allowscriptaccess:"sameDomain"
				};
				var attributes = {
					id:$overlay.attr("id")
				};
				swfobject.embedSWF(options.overlay_file_name, $overlay.attr("id"), "100%", "100%", "10.0.0", "/assets/expressInstall.swf", flashvars, params, attributes);
			} catch(e) {
				var data = {
					errorMessage:"[ERROR @ load_overlay]",
					error:e
				};
				ns11mmPresentation.trigger("streetviewError",data);
			};
		};
		
		var overlay_loaded = false;
		$psv.bind("overlayInitialized",function(e){
			_overlayInitializedHandler(e);
		}); function _overlayInitializedHandler(e){
			overlay_loaded = true;
			ns11mmPresentationLoader.trigger("loadStatusEvent",{pct:15,msg:"Overlay Loading"});
			if(streetview_loaded == true && initialized == false){
				completeInitialization();
			};
		};
		
		function completeInitialization(){
			initialized = true;
			ns11mmPresentationLoader.trigger("loadStatusEvent",{pct:10,msg:"Streetview Loaded"});
			ns11mmPresentationLoader.trigger("loadStatusEvent",{pct:20,msg:"Streetview And Overlay Loaded"});
			ns11mmPresentation.trigger("streetviewLoaded");
		};
		
		function getOverlay(){
			if (navigator.appName.indexOf("Microsoft") != -1){
				return window[$overlay.attr("id")];
		    } else {
		        return document[$overlay.attr("id")];
		    };
		};
		
		function send(data){
			try {
				//console.log("[COMM] send -----------------------------");
				//console.log(data);
				getOverlay().external_interface_receive(data);
			} catch(e) {
				var data = {
					errorMessage:"[COMM] ERROR @ send",
					error:e,
					msg:data
				};
				ns11mmPresentation.trigger("streetviewError",data);
			};
		};
		$psv.receive = function(data){
			receive(data);
		};
		function receive(msg){
			try{
				//console.log("[COMM] receive -----------------------------");
				//console.log(msg);
				switch(msg.action){
					case "streamSelected":
						receiveStreamSelected(msg);
						break;
					case "mediaItemViewChange":
						mediaItemViewChange(msg);
						break;
					case "menuLoaded":
						$psv.trigger("overlayInitialized");
						break;
					case "setMenuStreetview":
						ns11mmPresentationLoader.trigger("loadStatusEvent",{pct:5,msg:"Overlay Menu Loaded"});
						changeLocationAndPOV(msg);
						break;
					case "triggerSearchQuery":
						triggerSearchQueryHandler(msg);
						break;
					case "mediaCuepointHit":
						mediaCuepointHitHandler(msg);
						break;
				};
				
			} catch(e) {
				var data = {
					errorMessage:"[COMM] ERROR @ receive",
					error:e,
					msg:msg
				};
				ns11mmPresentation.trigger("streetviewError",data);
			};
		};
		
		function sendStreamSelected(stream){
			var data = {
				action:"streamSelected",
				stream:stream
			};
			send(data);
		};
		
		function receiveStreamSelected(data){
			ns11mmPresentation.trigger("streetviewStreamSelected",data);
		};
		
		function mediaItemViewChange(data){
			changeLocationAndPOV(data);
			ns11mmPresentation.trigger("streetviewMediaItemViewChange",data);
		};
		
		function triggerSearchQueryHandler(data){
			ns11mmPresentation.trigger("triggerSearchQuery",data);
		};
		
		function mediaCuepointHitHandler(data){
			changeLocationAndPOV(data);
			ns11mmPresentation.trigger("mediaKeyframeHit",data);
		};
		
		function changeLocationAndPOV(data){
			if (data["lat"] != null && data["lng"] != null && data["yaw"] != null && data["pitch"] != null && data["streetview"] != null) 
			{
				// if location distance is less than 10 meters, skip the setting the panorama. we cannot be sure the player takes it as new
				var dist_threshold = 0.010;
				var dist = distHaversine(parseFloat(loc_cache.lat), parseFloat(loc_cache.lng), parseFloat(data["lat"]), parseFloat(data["lng"]));
				
				if (data["streetview"] == true && dist > dist_threshold)
				{
					loc_cache.lat = data["lat"];
					loc_cache.lng = data["lng"];
					pov_cache.yaw = data["yaw"];
					pov_cache.pitch = data["pitch"];
					//console.log("setting new location (distance: " + dist + ")");
					client.getNearestPanorama(new GLatLng(data["lat"], data["lng"]), streetview_callback);					
				}
				else {
					//console.log("skipped setting new location (distance: " + dist + ")");
					_streetviewChangeCompleteHandler(false);
				}
			} else {
				//incomplete packet received. throw error.
			};
		};
		
		/*
		 * calculate the distance between two geo locations give in signed decimal degrees
		 * http://www.movable-type.co.uk/scripts/latlong.html
		 */
		Number.prototype.toRad = function() { // extend Number to convert degrees to radians
		  return this * Math.PI / 180;
		};
		
		function distHaversine(lat1, lon1, lat2, lon2) {
			var R = 6371; // earth's mean radius in km
			var dLat = (lat2-lat1).toRad();
			var dLon = (lon2-lon1).toRad();
			lat1 = lat1.toRad(), lat2 = lat2.toRad();

			var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
					Math.cos(lat1) * Math.cos(lat2) * 
					Math.sin(dLon/2) * Math.sin(dLon/2);
			var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
			var d = R * c;
			return d;
		};
		
		function streetview_callback(panoData)
		{
			panorama_no_event_timer = setTimeout(_streetviewChangeCompleteHandler, 2000);
			if (panoData.code == 200){
				var ui = {code:panoData.code};
				lat = panoData.location.lat;
				lng = panoData.location.lng;
				panorama.setLocationAndPOV(new GLatLng(lat, lng), pov_cache);
			} else {
				var ui = {code:panoData.code};
				return false;
			};
		}
		
		function _handleNoFlash(errorCode)
		{
			//console.log("handleNoFlash " + errorCode);
			var data = {};
			switch(errorCode) {
				case 600:
					data.errorMessage = "[ERROR @ getNearestPanorama] no nearby panorama";
					break;
				case 603:
					data.errorMessage = "[ERROR @ streetview component] Flash is not available";
					break;
			};
			ns11mmPresentation.trigger("streetviewError",data);
			return;
		};
		
		$psv.update = function(stream){
			sendStreamSelected(stream);
			return;
		};
		
		$psv.cancel = function(){
			//do something here!
			return;
		};
		
		return $psv;
	}	
})(ns11mmPresentation);