app = {
	
	init: function ()
	{
		app.Base.init();
		app.Facebook.init();
		app.Map.init();
		app.MapDBComms.getAllMemories();		
	}
	
}

app.Base = {
	
	init: function ()
	{
		//	Initial setup for page
		// 	Location search focus
		$("#locationSearch").focus(function()
		{
			$(this).val("");
		});
	},
	
	onInfoWindowOpen: function ()
	{
		$("#blackout").fadeIn();
		$("#infoWindow").fadeIn();
		//	Attach even for blackout click to close
		$("#blackout").click(function ()
		{
			app.Base.onInfoWindowClose();
		});
	},
	
	onInfoWindowClose: function ()
	{
		$("#blackout").fadeOut();
		$("#infoWindow").fadeOut();
	},
	
	onSearchLocation: function (addressString)
	{
		app.Map.geocoder.getLatLng(addressString, app.Base.onGeocodeAddressResponse);
		//	Track
		pageTracker._trackPageview("/locationsearch/" + addressString);
	},
	
	onGeocodeAddressResponse: function (foundLatLng)
	{
		if (!foundLatLng)
		{
			//	If location was not found TODO
		}
		else
		{
			//	Address found
			//	Close info window if not hidden
			app.Map.closeMarkerGuyInfoWindow();
			//	Go there, zoom
			//app.Map.map.panTo(foundLatLng);//setCenter(foundLatLng, 15);
			app.Map.map.setCenter(foundLatLng, 13);
			//app.Map.map.setZoom(5);
			//	Update current latlng
			app.Map.currentLatLng = foundLatLng;
			//	Update location of add guy
			app.Map.moveAddGuyToCurrentLatLng();
		}
	}
	
}

app.Facebook = {
	
	api_key: "142d7b5a37583a1a29c0e33d6f021e0c",
	receiver: "/xd_receiver.htm",
	api: null,
	userInfo: null,
	isLoggedIn: false,
	
	init: function ()
	{
		FB.init(app.Facebook.api_key, app.Facebook.receiver);
		FB.ensureInit(function()
		{ 
			FB.Facebook.init(app.Facebook.api_key, app.Facebook.receiver);			
			app.Facebook.api = FB.Facebook.apiClient;
		});
	},
	
	onConnected: function ()
	{
		//	Login status
		app.Facebook.isLoggedIn = true;
		//	Store reference to user info so we can get it when needed from elsewhere in the app
		app.Facebook.api.users_getInfo(app.Facebook.api.get_session().uid, ['first_name', 'last_name'], function(request)
		{
			app.Facebook.userInfo = request[0];
			$("#loginStatus").hide();
			$("#login").append("<div id='logging' style='margin-top: 8px;'><p>Logging into Facebook, won't be a moment ...</p></div>");

			//	Is logged in, update login status on page
			$.get("/requests/get_login_status_content/is_logged_in_status", app.Facebook.onFacebookLoginStatusResponse);
			
			//	Call django login and store in django db if new user, pass firstname lastname and store
			var postVars = 	"firstname=" + app.Facebook.userInfo.first_name + 
							"&lastname=" + app.Facebook.userInfo.last_name;
			$.ajax({
				type: "POST",
				url: "/requests/on_facebook_login/",
				data: postVars,
				success: app.Facebook.onLoginResponse
				//	TODO - fail
			});
			
		});
	},
	
	onLoginResponse: function (response)
	{
		//	No feedback
		//      On login, track
                pageTracker._trackPageview("/facebook/login");
	},
		
	onFacebookLoginStatusResponse: function (content)
	{
		//	Only delay fade in if logged in, else just show login button now
		if (app.Facebook.isLoggedIn)
		{
			//	Insert name into copy
			content = content.replace("[name]", app.Facebook.userInfo.first_name + " " + app.Facebook.userInfo.last_name);
			window.setTimeout(function()
			{
				$("#logging").hide();
				$("#loginStatus").fadeIn();
			}, 2500);
		}
		//	Inject this content into the page
		$("#loginStatus").html(content);
		FB.XFBML.Host.parseDomTree();
	},
		
	publish: function (memoryString, memoryURL)
	{
		var comment_data = {"memoryString":$("#memoryRef").html(), "memoryURL":$("#memory_link_to").val()};
		FB.Connect.showFeedDialog('129902438623', comment_data, null, null, null, FB.RequireConnect.require, FB.RequireConnect.promptConnect, null, null); 
		//	Track
                pageTracker._trackPageview("/memory/" + $("#memoryID").val() + "/fbshare");
	}
	
}

app.MapDBComms = {
	
	parsedMemories: null,
	
	getAllMemories: function ()
	{
		$.get("/requests/get_all_memories/", app.MapDBComms.onGetAllMemoriesResponse);
	},
	
	onGetAllMemoriesResponse: function (json_memories)
	{
		//	TODO - failed response
		app.MapDBComms.parsedMemories = JSON.parse(json_memories);
		//	If there are any memories to parse
		if (app.MapDBComms.parsedMemories[0])
		{
			for (memory_index in app.MapDBComms.parsedMemories)
			{
				//	If we have been linked to a specific marker, we will have populated a div with it's id
				//	Compare with the currently looped marker id and open if the same
				var openNow = $("#show_tag").html() == app.MapDBComms.parsedMemories[memory_index].pk ? true : false;
				app.Map.addMemoryMarkerWithData(app.MapDBComms.parsedMemories[memory_index], openNow);
			}
		}
	},
	
	getMemoryWithID: function (id)
	{
		$.get("/requests/get_memory/" + id, app.MapDBComms.onGetMemoryResponse);
	},
	
	onGetMemoryResponse: function (json_memory)
	{
		//	TODO - failed response
		//	TODO - if memory not found
		var parsedMemory = JSON.parse(json_memory);
		app.Map.addMemoryMarkerWithData(parsedMemory[0], true);
	}
	
}

app.Map = {
	
	map: null,
	geocoder: null,
	streetviewClient: null,
	currentLatLng: null,
	markerGuy: null,
	markerGuyIsClickable: false,
	streetviewOverlay: null,
	addMemoryPanorama: null,
	currentLocationHasStreetview: false,
	memoryWasAdded: false,
	
	init: function ()
	{
		//	Generate the map
		if (GBrowserIsCompatible())
		{
			app.Map.map = new GMap2(document.getElementById("map"));
			//	Listen for fully loaded
			GEvent.addListener(app.Map.map, "load", app.Map.onMapLoaded);
			//	Setup
			app.Map.map.setMapType(G_PHYSICAL_MAP);
			app.Map.map.setCenter(new GLatLng(42.032974332441405, -42.01171875), 3);
			//	Controls
			app.Map.map.addControl( new GLargeMapControl3D(), new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(11, 50)) );
			app.Map.map.addControl( new GMapTypeControl(), new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(15, 20)) );
			app.Map.map.addMapType(G_PHYSICAL_MAP);
			app.Map.geocoder = new GClientGeocoder();
			app.Map.streetviewClient = new GStreetviewClient();	
		}
		//	Else TODO
	},
	
	onMapLoaded: function ()
	{
		//	Listen for map move
		GEvent.addListener(app.Map.map, "zoomend", app.Map.onMapZoom);
		//	Drop guy onto map
		app.Map.initAddGuy();
	},
	
	onMapZoom: function ()
	{
		//	Close info window if not hidden
		app.Map.closeMarkerGuyInfoWindow();
		app.Map.currentLatLng = app.Map.map.getCenter();
		app.Map.moveAddGuyToCurrentLatLng();
	},
	
	addMemoryMarkerWithData: function (memoryData, openNow)
	{
		var marker = new MemoryMarker();
		marker.init(memoryData);
		if (openNow) marker.open();
	},
	
	initAddGuy: function ()
	{
		//	The user wants to drop the guy onto the map
		var guyIcon = new GIcon(G_DEFAULT_ICON);
		guyIcon.image = "http://maps.gstatic.com/mapfiles/cb/man_arrow-0.png";
		// guyIcon.transparent = "http://maps.gstatic.com/mapfiles/cb/man-pick.png";
		guyIcon.imageMap = [
		      26,13, 30,14, 32,28, 27,28, 28,36, 18,35, 18,27, 16,26,
		      16,20, 16,14, 19,13, 22,8
		   ];
		guyIcon.shadowSize = new GSize(0, 0);
		guyIcon.iconSize = new GSize(49, 52);
		guyIcon.iconAnchor = new GPoint(25, 35);  // near base of guy's feet
		guyIcon.infoWindowAnchor = new GPoint(25, 5);  // top of guy's head

		//	Drop at center of map
		var markerGuyPoint = new GLatLng(28.92163128242129, -71.89453125);
		app.Map.markerGuy = new GMarker(markerGuyPoint, {icon: guyIcon, draggable: true});
		app.Map.map.addOverlay(app.Map.markerGuy);
		//	Save this new central location as current location
		app.Map.currentLatLng = markerGuyPoint;
		//	Enable streetview overlay
		app.Map.streetviewOverlay = new GStreetviewOverlay();
	    app.Map.map.addOverlay( app.Map.streetviewOverlay );
		
		//	Open addguy info straight away, first time in app, then replace by add window thereafter
		app.Map.openAddGuyForInstruction();	
	},
	
	openAddGuyForInstruction: function ()
	{
		//	This doesn't need to happen if someone is linked to here
		//	This opens when someone first visits the site
		GDownloadUrl("/requests/get_info_window_content/welcome", function(data) {
			var content = data;
			app.Map.markerGuy.openInfoWindowHtml(content);
		});
		//	Init events
		app.Map.initMarkerGuyEvents();
	},
	
	initMarkerGuyEvents: function ()
	{
		//	Listen for click
		GEvent.addListener(app.Map.markerGuy, "click", app.Map.onGuyClick);
		//	Listen for info window fully open
		GEvent.addListener(app.Map.markerGuy, "infowindowopen", app.Map.onGuyInfoMarkerOpen);
		
		//	Listen for drag guy start/end
		GEvent.addListener(app.Map.markerGuy, "drag", app.Map.onDragGuyStart);
		GEvent.addListener(app.Map.markerGuy, "dragend", app.Map.onDragGuyEnd);		
	},
	
	moveAddGuyToCurrentLatLng: function ()
	{
		app.Map.markerGuy.setLatLng(app.Map.currentLatLng);
	},
	
	onGuyClick: function ()
	{
		app.Map.markerGuyIsClickable = true;
		//	Open info window with add memory content template
		//	Decide which template, based on if we can fetch streetview
		app.Map.streetviewClient.getNearestPanorama(app.Map.currentLatLng, app.Map.onGetNearestPanoramaResponse);
		//	Track
		pageTracker._trackPageview("/addmemoryattempt");
	},

	onGetNearestPanoramaResponse: function (response)
	{
		var templateString;
		currentLocationPanoramaResponseCode = response.code;
		//	Check response
		switch (response.code)
		{
			case 500:
				//	Server error - TODO
				templateString = "add_memory_500";
				//	TODO - dont want to be able to submit a memory at this point
				break;
				
			case 600:
				//	No pano
				templateString = "add_memory_600";
				app.Map.currentLocationHasStreetview = false;
				break;
				
			case 200:
				//	Pano found
				templateString = "add_memory_base";
				app.Map.currentLocationHasStreetview = true;
				break;
		}
		//	Now get correct template
		GDownloadUrl("/requests/get_info_window_content/" + templateString, function(data) {
			var content = data;
			app.Map.markerGuy.openInfoWindowHtml(content);
		});
		//	Always listen for close		
		GEvent.addListener(app.Map.map.getInfoWindow(), "closeclick", app.Map.onAddGuyInfoWindowClose);
	},
	
	onGuyInfoMarkerOpen: function ()
	{
		//	Only init further if marker is clickable(was clicked) to prevent action on the initial welcome screen
		if (app.Map.markerGuyIsClickable)
		{
			//	Add memory window is open
			//	If this was 200 response when checking panorama availability, attach panorama
			if (app.Map.currentLocationHasStreetview)
			{
				var panoramaOptions = { latlng: app.Map.currentLatLng };
				app.Map.addMemoryPanorama = new GStreetviewPanorama(document.getElementById("addMemoryStreetview"), panoramaOptions);
				GEvent.addListener(app.Map.addMemoryPanorama, "error", app.Map.handleNoFlash);
				//	Listen for initializes event when and if the user walks via streetview
				GEvent.addListener(app.Map.addMemoryPanorama, "initialized", app.Map.onStreetviewWalked);
			}		
			//	If the user has logged in via facebook, use name as default value in username input box
			if (app.Facebook.isLoggedIn && app.Facebook.userInfo != null) $("#username").val(app.Facebook.userInfo.first_name + " " + app.Facebook.userInfo.last_name);
			//	Get their name
			//	Init events for this window
			app.AddMemoryWindow.init();
		}
	},
	
	onStreetviewWalked: function (newLocation)
	{
		//	User has walked using streetview
		var newPoint = newLocation.latlng;
		//	Update guy marker with new location
		app.Map.currentLatLng = newPoint;
		app.Map.moveAddGuyToCurrentLatLng();
	},
	
	onAddGuyInfoWindowClose: function ()
	{
		if (app.Map.addMemoryPanorama != null)
		{
			app.Map.addMemoryPanorama.remove();
			app.Map.addMemoryPanorama = null;
		}
	},
	
	onDragGuyStart: function ()
	{
		//	Close info window if not hidden
		app.Map.closeMarkerGuyInfoWindow();
	},
	
	onDragGuyEnd: function ()
	{
		//	Update current position with where the guy has been dropped
		app.Map.currentLatLng = app.Map.markerGuy.getLatLng();
		app.Map.map.panTo(app.Map.currentLatLng);
	},
	
	closeMarkerGuyInfoWindow: function ()
	{
		if (!app.Map.map.getInfoWindow().isHidden()) app.Map.markerGuy.closeInfoWindow();
	},

	removeAddGuyOverlay: function ()
	{
		app.Map.map.removeOverlay(app.Map.markerGuy);
	},
	
	addAddGuyOverlay: function ()
	{
		app.Map.map.addOverlay(app.Map.markerGuy);
	},
	
	handleNoFlash: function (errorCode)
	{
		if (errorCode == FLASH_UNAVAILABLE)
		{
			alert("Opps, looks like ou don't have flash installed. You can still use the site, but you won't be able to see any memories at ground level.");
	    	return;
		}
	},
	
	onSubmitNewMemory: function ()
	{
		//	If validation succeeds
		if (app.AddMemoryWindow.validateNewMemory())
		{
			//	If all ok, 
			//	Disable button
			$("#submitMemoryButton").attr("disabled", "disabled");
			$("#submitMemoryButton").attr("value", "Saving ...");

			//	Create postvars to insert into db
			//	Insert to db via ajax			
			var params = { username:$("#username").attr("value"), memory:$("#newMemoryString").attr("value"), lat:app.Map.currentLatLng.lat(), lng:app.Map.currentLatLng.lng() };
					
			//	If has streetview
			if (app.Map.currentLocationHasStreetview)
			{
				params.has_street_view = "true";
				params.yaw = app.Map.addMemoryPanorama.getPOV().yaw;
				params.pitch = app.Map.addMemoryPanorama.getPOV().pitch;
				params.zoom = app.Map.addMemoryPanorama.getPOV().zoom;
			}
			else
			{
				params.has_street_view = "false";
				params.yaw = 0
				params.pitch = 0;
				params.zoom = 0;
			}

			var postVars = jQuery.param(params);

			//	Request to save
			$.ajax({
				type: "POST",
				url: "/requests/save_new_memory/",
				data: postVars,
				success: function (added_pk)
				{
					//	New memory saved
					app.Map.onSubmitNewMemorySuccessWithNewID(added_pk);
				}
				//	TODO - fail
			});
		}
	},
	
	onSubmitNewMemorySuccessWithNewID: function (id)
	{
		//	Done saving new memory
		//	Now need to update this memory to save new bit.ly hash
		//	Check to see if the api is available TODO
		//	Setup callback function on shorten url on memory save
		BitlyCB.onURLShortenResponse = function (urlData)
		{
			var result;
			for (var r in urlData.results)
			{
				result = urlData.results[r];
				result['longUrl'] = r;
				break;
			}
			shortenedURL = result['shortUrl'];
			
			var hash = BitlyClient.extractBitlyHash(shortenedURL);
			//	Insert this new url hash into the db
			var postVars = 	"hash=" + hash;
			//
			$.ajax({
				type: "POST",
				url: "/requests/update_memory/" + id,
				data: postVars,
				success: function ()
				{
					//	Memory updated with bit.ly hash
					//	Close add window
					app.Map.markerGuy.closeInfoWindow();
					//	Reference that we've just added a new memory, to open a thank you template
					app.Map.memoryWasAdded = true;
					//	Reload this new marker in with id and add as new marker on map
					app.MapDBComms.getMemoryWithID(id);
				}
				//	TODO - fail
			});	
		}
		//	Now use the new pk to create a new bit.ly link from this url
		//	Query api to shorten this url to ensure it is only built once.
		BitlyClient.call('shorten', {'longUrl':'http://www.thisiswhere.co.uk/memory/' + id, 'history':'1'}, 'BitlyCB.onURLShortenResponse');
		//	Track
		pageTracker._trackPageview("/addmemorysuccess");
	}
	
}

app.AddMemoryWindow = {
	
	defaultMemoryString: "This is where ",
	
	init: function ()
	{
		//	Listen for focus on message and name
		$("#newMemoryString").focus(function()
		{
			app.AddMemoryWindow.onMemoryStringFocus(this);
		});
		$("#username").focus(function()
		{
			app.AddMemoryWindow.onUsernameFocus(this);
		});
		//	Listen for change in memory chars
		$("#newMemoryString").keyup(function()
		{
			app.AddMemoryWindow.onMemoryStringChange(this);
		});
	},
	
	onMemoryStringFocus: function (target)
	{
		//	Only clear focus if default string
		//if ($(target).attr("value") == app.AddMemoryWindow.defaultMemoryString) $(target).attr("value", "");
		if ($("#username").attr("value") == "") $("#username").attr("value", "Anonymous");
	},
	
	onUsernameFocus: function (target)
	{
		//	Reset memory string back to default if empty
		if ($("#newMemoryString").attr("value") == "") $("#newMemoryString").attr("value", app.AddMemoryWindow.defaultMemoryString);
	},
	
	onMemoryStringChange: function (target)
	{
		var newAmt = 200 - $(target).val().length;
		$("#charsRemaining").html(newAmt);
		//	If in the red
		if (newAmt < 0)
		{
			$("#lblRemaining").css("color", "red");
			$("#remainingWarning").html(". It's good to be a little more specific.");
		}
		else
		{
			$("#lblRemaining").css("color", "#999999");
			$("#remainingWarning").html("");
		}
	},
	
	validateNewMemory: function ()
	{
		//	Validate add memory form and return outcome
		var success = false;
		//	Memory
		if ($("#newMemoryString").val() == "" || $("#newMemoryString").val() == app.AddMemoryWindow.defaultMemoryString || parseInt($("#charsRemaining").html()) < 0)
		{
			app.AddMemoryWindow.highlightInvalidFormField("#newMemoryString");
		}
		else
		{
			//	Memory string ok
			app.AddMemoryWindow.restoreFormField("#newMemoryString");
			//	Check name
			if ($("#username").val() == "")
			{
				app.AddMemoryWindow.highlightInvalidFormField("#username");
			}
			else
			{
				//	All ok
				app.AddMemoryWindow.restoreFormField("#username");
				success = true;
			}
		}
		//	Result
		return success;
	},
	
	highlightInvalidFormField: function (target)
	{
		$(target).css("border-color", "red");
		$(target).effect("highlight", { color: "#FFC9C9" }, 1000);
	},
	
	restoreFormField: function (target)
	{
		$(target).css("border-color", "black");
	}
	
}

//	Encapsulated marker class(object)
function MemoryMarker ()
{
	var point;
	var marker;
	var data;
	var has_street_view;
	var panorama;
	var createdPan = false;
	this.init = init;
	this.open = open;		
	
	function init (memoryData)
	{
		data = memoryData;
		//	New point for the marker
		point = new GLatLng(data.fields.lat, data.fields.lng);
		//	Marker
		marker = new GMarker(point);
		//	Attach to map
		app.Map.map.addOverlay(marker);
		//	Listen for click
		GEvent.addListener(marker, "click", onMemoryMarkerClick);
		//	Listen for info window fully open
		GEvent.addListener(marker, "infowindowopen", onMarkerInfoWindowOpen);
	}
	
	function onMemoryMarkerClick ()
	{
		open();
	}
	
	function open ()
	{
		//	Before we open the memory, get the content template depending on if we will be showing streetview
		has_street_view = data.fields.has_street_view;
		var templateString = has_street_view ? "memory_marker_sv" : "memory_marker_base";
		//	Load up template
		GDownloadUrl("/requests/get_info_window_content/" + templateString, function(templateData) {
			var content = templateData;
			//	Make sure the sv id is set if streetview
			if (has_street_view) content = content.replace("[sv_id]", "sv_" + data.pk);
			content = content.replace("[user]", data.fields.username);
			//	Format date
			var date = data.fields.date_tagged.substr(0, 10);
			var s = date.split("-");
			s = s.reverse();
			date = s.join("-");
			content = content.replace("[date_tagged]", date);
			content = content.replace("[memoryString]", data.fields.memory);
			content = content.replace("[memoryURL]", "http://bit.ly/" + data.fields.url_hash);
			//	Bit of a long way around, but if we have just added a new memory, we need to inject a thank you into the template
			if (app.Map.memoryWasAdded)
			{
				var thanksStr = '<div class="bottom-border">' + 
									'<h2 class="window-header">All done. Pretty easy huh?</h2>' +
								'</div>' + 
								'<div class="bottom-border">' +
									'<p>Your memory has been added successfully and is live now! You can link to this memory directly or share on Facebook or Twitter at the bottom of this window.</p>' +
								'</div>';
				content = content.replace("[thanks]", thanksStr);
			}
			else
			{
				//	Remove the tag holder
				content = content.replace("[thanks]", "");
			}
			//	If memory was added, redraw guy marker
			if (app.Map.memoryWasAdded)
			{
				app.Map.removeAddGuyOverlay();
				app.Map.addAddGuyOverlay();
			} 
			//	Reset added status
			app.Map.memoryWasAdded = false;
			marker.openInfoWindowHtml(content);
		});
	}
	
	function onMarkerInfoWindowOpen ()
	{
		if (has_street_view)
		{
			//	Attach panorama
			var markerPov = {yaw: data.fields.yaw, pitch: data.fields.pitch, zoom: data.fields.zoom};
			panoramaOptions = { latlng: point, pov: markerPov };
			panorama = new GStreetviewPanorama(document.getElementById("sv_" + data.pk), panoramaOptions);
			GEvent.addListener(panorama, "error", handleNoFlash);
		}
		//	Attach click event to link input
		$("#memory_link_to").focus(function()
		{
			this.select();
		});
		//	Listen for close		
		GEvent.addListener(app.Map.map.getInfoWindow(), "closeclick", onMarkerInfoWindowClose);
		//	Setup retweet button	
		initMemoryRetweetButton();
		//	Inject mem id into hidden val
		$("#memoryID").val(data.pk);
		//	Track memory open
		pageTracker._trackPageview("/memory/" + data.pk + "/open");
	}
	
	function initMemoryRetweetButton ()
	{
		//	Check to see if the api is available TODO
		
		var shortenedURL = null;
		var totalClicks = 0;
		
		//	Callback on url stats response
		BitlyCB.onBitURLStatsResponse = function (statData)
		{
			if (statData.results.clicks)
			{
				totalClicks = statData.results.clicks;
			}
			$("#totalHits").html(totalClicks);
		}
				
		//	Inject constructed twitter url to dom
		$("#retweet").attr("href", "http://twitter.com/home?status=" + encodeURIComponent($("#memoryRef").html() + " http://bit.ly/" + data.fields.url_hash));
		//	Query for url stats based on bit.ly hash
		BitlyClient.stats(data.fields.url_hash, 'BitlyCB.onBitURLStatsResponse');

		//	Add click event so we can track it
		$("#retweet").click(function ()
		{
			pageTracker._trackPageview("/memory/" + data.pk + "/retweet");
		});
	}
	
	function onMarkerInfoWindowClose ()
	{
		if (panorama != null)
		{
			panorama.remove();
			panorama = null;
		}
		$("#content_" + data.pk).html("");
	}

	function handleNoFlash (errorCode)
	{
		if (errorCode == FLASH_UNAVAILABLE)
		{
			alert("Opps, looks like ou don't have flash installed. You can still use the site, but you won't be able to see any memories at ground level.");
	    	return;
		}
	}
	
};

$(document).ready(function()
{
	//	Kick off the app
	app.init();
});
