Sm2Poi.Map = {
	$el: null,
	$tabs: null,
	
	gmap: null,
	
	evtBindings: {},
	
	poiCache: {},
	
	settings: {
		center: [35.522167, -82.532043]
	},
	
	initialize: function(options) {
		Sm2Poi.Map.$el = $('.sm2_poi_map:first');
		Sm2Poi.Map.$tabs = $('#sm2_poi_map_sidebar');
		
		// Save settings
		Sm2Poi.Map.settings = $.extend(Sm2Poi.Map.settings, options);

		// Init jQuery UI Tabs
		Sm2Poi.Map.$tabs.tabs({
			selected:-1,
			select: function(event, ui) {
				Sm2Poi.Map.triggerEvent('tab_change', $(ui.tab).attr('href').substr(1));
			}
		});

		// Init GoogleMap
		google.load("maps", "2", {callback: Sm2Poi.Map.setup});
	},
	
	setup: function(center) {
		// Initialize Google Map
		Sm2Poi.Map.gmap = new google.maps.Map2(document.getElementById('map'));

		var center = new google.maps.LatLng(35.303526, -82.257625);
		//var zoom = (Sm2Poi.Map.settings.center[2] || 1) * 1;
		Sm2Poi.Map.gmap.setCenter(center, 11);

		Sm2Poi.Map.gmap.addControl(new google.maps.LargeMapControl());
		Sm2Poi.Map.gmap.addControl(new google.maps.MapTypeControl());
		Sm2Poi.Map.gmap.enableScrollWheelZoom();
		
		Sm2Poi.Map.triggerEvent('map_ready', window.location.hash.substr(1));
	},
	
	/**
	 * Tells jQuery UI to open the specified tab
	 * @param {Object} tabId
	 */
	openTab: function(tabId) {
		Sm2Poi.Map.$tabs.tabs('select', '#'+tabId);
	},
	
	/**
	 * Create a marker for use on the Google Maps map.
	 * @param {Number} lat The latitude value of the marker coordinates
	 * @param {Number} lon The longitude value of the marker coordinates
	 * @param {Function} onClickMarker Function to call when the marker is clicked
	 * @param {Number} mapIconId The ID of the map icon to associate with this marker
	 * @return {Marker} A new Marker instance to be put on the map
	 */
	createMarker: function(lat, lon, onClickMarker, mapIconId) {
		// Set the coordinates for the marker
		var point = new google.maps.LatLng(lat, lon);
		
		// If an icon ID is given, assign the icon to the marker
		var icon  = null;
		if (mapIconId*1) {
			var d = mapIconData[mapIconId];
			icon = new GIcon(G_DEFAULT_ICON);
			icon.iconSize = new GSize(d.imageWidth, d.imageHeight);
			icon.iconAnchor = new GPoint(d.mapAnchorX, d.mapAnchorY);
			icon.infoWindowAnchor = new GPoint(d.infoWindowAnchorX*1, d.infoWindowAnchorY*1);
			icon.image = '/sitemanager2/poi/mapicon.php?id='+mapIconId+'&type=image';
		}
		
		// Create the marker object
		var marker = icon ? new google.maps.Marker(point, icon) : new google.maps.Marker(point);
		
		// Listen for clicks on the marker
		GEvent.addListener(marker, 'click', onClickMarker);
		
		return marker;
	},
	
	addListener: function(evt, fn) {
		if (!Sm2Poi.Map.evtBindings[evt]) Sm2Poi.Map.evtBindings[evt] = [];
		Sm2Poi.Map.evtBindings[evt].push(fn);
	},
	triggerEvent: function(evt, data) {
		if (Sm2Poi.Map.evtBindings[evt]) {
			$.each(Sm2Poi.Map.evtBindings[evt], function() {
				this(data);
			});
		}
	},
	
	/**
	 * Loads a set of POIs from the cache (or remote database, if not in cache)
	 * @param {Array} ids List of POI IDs to return data for
	 * @param {Function} callback Function to call with full POI data:
	 *                            function callback(data, ids)
	 *                            data={1:{...}, 2:{...}, ...}
	 *                            ids=[1,2,...]
	 */
	getPois: function(ids, callback) {
		var f = [];
		var d = {};
		
		// Loop through the given IDs and figure out which ones we need to
		// fetch data for
		$.each(ids, function() {
			if (!Sm2Poi.Map.poiCache[this]) f.push(this);
			else d[this] = Sm2Poi.Map.poiCache[this].data;
		});
		
		// Fetch data for unloaded POIs
		if (f.length > 0) {
			$.getJSON(Sm2Poi.ajaxUrl, {act:'get', ids:f.join(',')}, function(poiData) {
				$.each(poiData, function() {
					Sm2Poi.Map.poiCache[this.id] = {data:this};
					d[this.id] = this;
				});
				callback(d, ids);
			});
		}
		// All POIs already in cache
		else {
			callback(d, ids);
		}
	}
};

var MapLocations = {
	$resultTemplate: null,
	$infowinTemplate: null,
	iconTemplate: null,
	
	$tab: null,
	$form: null,
	$results: null,
	
	cache:{},
	lastResults: [],
	
	setup: function(load) {
		MapLocations.$tab = $('#locations').removeClass('hidden');
		MapLocations.$form = MapLocations.$tab.find('.categories:first');
		MapLocations.$results = MapLocations.$tab.find('.results:first');
		
		// Listen for click on "update map" button"
		MapLocations.$tab.find('button.updatemap').click(function(){ MapLocations.search(); });
		
		// Import the result row
		MapLocations.$resultTemplate = MapLocations.$results.find('.poi').removeClass('hidden').remove();
		console.debug(MapLocations.$resultTemplate);
		MapLocations.$infowinTemplate = MapLocations.$tab.find('.infowin').removeClass('invisible').remove();
		
		Sm2Poi.Map.addListener('tab_change', MapLocations.onTabChange);
		
		MapLocations.$results.height(235);
		
		
		// If no initial tab is selected, or this is specified as the initial tab, open this tab
		if (!load) load = 'pois';
		if (load.indexOf('poi') === 0) {
			var params = load.split('/');
			switch (params[0]) {
				case 'poi':
					break;
				case 'pois':
					// If category params are passed in, set them and search
					if (params[1]) {
						var ids = params[1].split(',');
						$('#locations .categories input.catCB').each(function() {
							this.checked = $.inArray(this.value, ids) >= 0;
						});
						MapLocations.search(function() {
							// If a specific POI is specified, highlight it.
							if (params[2]) {
								MapLocations.map(params[2]);
							}
						});
					}
					break;
			}
			Sm2Poi.Map.openTab('locations');
		}
	},
	
	search: function(callback) {
		MapLocations.$results.removeClass('empty').addClass('loading').find('.poi').remove().end().find('.city').remove();
		var ids = MapLocations.getCheckedCategories();
		
		// Save the link to this search in the URL
		//MapLocations.saveLink();
		
		// Request data from the server
		$.getJSON(Sm2Poi.ajaxUrl, {act:'filterByCategory', ids:ids.join(','), map:1}, function(data) {
			MapLocations.handleSearchResults(data, callback);
		});
	},
	
	/**
	 * Takes IDs returned from the server and displays them
	 * @param {Array} ids List of IDs to load
	 * @param {Function} callback Function to call when all POIs have been loaded
	 */
	handleSearchResults: function(ids, callback) {
		if (!ids  ||  !parseInt(ids[0])) {
			MapLocations.displayResults({}, []);
			if (callback) callback();
		}
		else {
			Sm2Poi.Map.getPois(ids, function(pois, ids) {
				MapLocations.displayResults(pois, ids);
				if (callback) { callback(); }
			});
		}
	},
	
	displayResults: function(pois, ids) {
		$.each(ids, function() {
			if (!pois[this]) console.error('NO DATA FOR ID', this);
		});
		for (var k in pois) {
			if ($.inArray(k, ids) < 0) console.warn('NO ID FOR DATA', k, pois[k]);
		}
		var results = [];
		var markers = [];
		
		MapLocations.lastResults = ids;
		
		$.each(ids, function() {
			// If this ID is new, build marker and result row for it
			
			//if (!MapLocations.cache[this]) {
				MapLocations.cache[this] = {
					marker: MapLocations.buildMapIcon(pois[this]),
					result: MapLocations.buildResult(pois[this])
				};
			//}
			
			results.push(MapLocations.cache[this].result);
			
			if (MapLocations.cache[this].marker) markers.push(MapLocations.cache[this].marker);
		});
		
		$.each(results, function() {
			console.warn('CITY: '+this.data('city'));	 
		});
		
		MapLocations.showResults(results);
		MapLocations.showMarkers(markers);
	},
	
	buildResult: function(data) {
		var mapable = data.lat*1 && data.lon*1;
		
		// Set map icon source
		var mapIconSrc = '/sitemanager2/poi/images/genericMapIcon.png';
		if (parseInt(data.mapIconId)) mapIconSrc = '/sitemanager2/poi/mapicon.php?id='+data.mapIconId+'&type=legend';
		
		$row = MapLocations.$resultTemplate.clone()
			.find('img.mapicon').attr('src', mapIconSrc).end()
			.find('.data_title').html(data.title).end()
			.find('.data_state').html(data.state).end()
			.find('a.map').attr('href', "javascript:MapLocations.map("+data.id+")").end()
			.find('a.info').attr('href', '/links/'+data.id).click(function() { MapLocations.saveLink(); }).end()
			;
			
		// If addressable, show address
		if (data.city  &&  data.state) $row.find('address').html(data.city+', '+data.state);
		else $row.find('address').remove();

			
		// If mappable, show link
		if (mapable) $row.addClass('mappable');
		else $row.find('img.mapicon').remove();
		
		$row.data('city', data.city);
		return $row;
	},
	
	buildMapIcon: function(data) {
		var marker = null;
		if (data.lat*1  &&  data.lon*1) {
			var fn = function() {
				MapLocations.map(this._sm2Id);
			}
			marker = Sm2Poi.Map.createMarker(data.lat, data.lon, fn, data.mapIconId);
			marker._sm2Id = data.id;
			marker.title = data.title;
		}
		
		return marker;
	},
	
	buildInfoWindowHtml: function(data) {
		var html = '<div class="infowin">';
		
		html += '<div class="title">' + data.title + '</div>';
		//if(data.thumbnail) html += '<img class="mapThumb" src="/sitemanager2/files/'+data.thumbnail+'" />'; 
		if(data.gallery.thumbnail.url) html += '<img class="mapThumb" src="'+data.gallery.thumbnail.url+'" />'; 
		html += '<address>'+data.streetAddress+'<br />'+data.city+', '+data.state+' '+data.zip+'</address>';
		
		if (data.phone) html += '<div><b>Phone:</b> ' + data.phone + '</div>';

		if (data.url) html += '<div><a href="' + data.url + '" target="_blank">' + data.url + '</a></div>';
		
		html += '<div class="links">';
		html += 	'<a href="/links/'+data.id+'" onclick="MapLocations.saveLink()"><img src="/sitemanager2/poi/images/icon_info.png" alt="[>]"/> More Info</a>';
		html +=     '<br />';
		
		var inIt = Sm2Poi.Itinerary.inItinerary(data.id);
		var cl = inIt ? 'in' : 'out';
		var ht = inIt ? 'Remove from My Itinerary' : 'Add to My Itinerary';
		
		if (Sm2Poi.Itinerary.inItinerary(data.id))
			html += '<a rel="'+data.id+'" class="toggleItineraryLink" href="" onclick="Sm2Poi.Itinerary.toggleLink('+data.id+'); return false;"><img src="/sitemanager2/poi/images/icon_itineraryRem.png" alt="[-]"/> Remove from My Itinerary</a>';
		else
			html += '<a rel="'+data.id+'" class="toggleItineraryLink" href="" onclick="Sm2Poi.Itinerary.toggleLink('+data.id+'); return false;"><img src="/sitemanager2/poi/images/icon_itineraryAdd.png" alt="[+]"/> Add to My Itinerary</a>';
		html += '</div>';
		html += '<p>&nbsp;</p>';
		html += '<p>&nbsp;</p>';
		html += '<p>&nbsp;</p>';
		html += '</div>';
		return html;
	},
	
	showMarkers: function(arr) {
		Sm2Poi.Map.gmap.clearOverlays();
		$.each(arr, function() { Sm2Poi.Map.gmap.addOverlay(this); });
	},
	
	showResults: function(arr) {
		var lastCity = '';

		MapLocations.$results.removeClass('loading');
		if (arr.length == 0) MapLocations.$results.addClass('empty');
		else MapLocations.$results.removeClass('empty');
		
		$.each(arr, function() {

			var city = this.data('city');			
			if (city && city.toLowerCase() != lastCity) {		
				lastCity = city.toLowerCase();
				MapLocations.$results.append('<li class="city">'+city+'</li>');
			}
			MapLocations.$results.append(this);
		});
	},
	
	map: function(id) {
		// Save what marker was clicked on so it can be re-selected on page refresh
		//MapLocations.saveLink('/'+id);
		
		// Open the info window
		MapLocations.cache[id].marker.openInfoWindow(MapLocations.buildInfoWindowHtml(Sm2Poi.Map.poiCache[id].data),{maxWidth:400});
	},
	
	onTabChange: function(tab) {
		if (tab != 'locations') {
			// Hide all previous markers
			$.each(MapLocations.lastResults, function() {
				var m = MapLocations.cache[this].marker;
				if (m) {
					m.closeInfoWindow();
					m.hide();
				}
			});
		}
		else {
			// Show the previous markers
			$.each(MapLocations.lastResults, function() {
				var m = MapLocations.cache[this].marker;
				if (m) m.show();
			});
			// Save link into URL
			//MapLocations.saveLink();
		}
	},
	
	getCheckedCategories: function() {
		var result = MapLocations.$form.find('input.catCB:checked').map(function() { return this.value; });
		return $.makeArray(result);
	},
	
	saveLink: function(extra) {
		var cats = MapLocations.getCheckedCategories();
		window.location.replace('#poi' + (cats.length>0 ? 's/'+cats.join(',') : '') + (extra || ''));
	}
};
Sm2Poi.Map.addListener('map_ready', MapLocations.setup);

var MapItinerary = {
	$resultTemplate: null,
	$infowinTemplate: null,
	
	$tab: null,
	$results: null,
	
	poiFocus:0,
	
	cache:{},
	
	setup: function(load) {
		MapItinerary.$tab = $('#myItinerary').removeClass('hidden');
		MapItinerary.$results = MapItinerary.$tab.find('.results:first');
		
		Sm2Poi.Map.addListener('tab_change', MapItinerary.onTabChange);

		// Import the result row and info window templates
		MapItinerary.$resultTemplate = MapItinerary.$results.find('.poi').removeClass('hidden').remove();
		MapItinerary.$infowinTemplate = MapItinerary.$tab.find('.infowin').removeClass('invisible').remove();
		
		// If this is specified as the initial tab, open this tab
		// If no initial tab is selected, or this is specified as the initial tab, open this tab
		if (load.indexOf('itinerary') === 0) {
			var params = load.split('/');
			if (params[0]=='itinerary'  &&  params[1]) {
				MapItinerary.poiFocus = params[1];
			}
			Sm2Poi.Map.openTab('myItinerary');
		}
	},
	
	onTabChange: function(tab) {
		if (tab == 'myItinerary') {
			MapItinerary.show();
			// Save link into URL
			//MapItinerary.saveLink();
		}
		else {
			MapItinerary.hide();
		}
	},
	
	show: function() {
		MapItinerary.$results.removeClass('empty').addClass('loading');
		
		Sm2Poi.Map.getPois(Sm2Poi.Itinerary.get(), function(pois, ids) {
			if (ids.length > 0) {
				MapItinerary.displayItinerary(pois, ids);
			}
			else {
				MapItinerary.$results.addClass('empty');
			}
			
			MapItinerary.$results.removeClass('loading');
		});
	},
	
	hide: function() {
		$.each(MapItinerary.cache, function() { if (this.marker) Sm2Poi.Map.gmap.removeOverlay(this.marker); });
		MapItinerary.$results.find('.poi').remove();
	},
	
	remove: function(id) {
		Sm2Poi.Itinerary.rem(id);
		var d = MapItinerary.cache[id];
		if (d.marker) Sm2Poi.Map.gmap.removeOverlay(d.marker);
		d.result.remove();
		MapItinerary.cache[id] = null;
		if (MapItinerary.$results.find('li.poi').length == 0) MapItinerary.$results.addClass('empty');
	},
	
	displayItinerary: function(pois, ids) {
		$.each(ids, function() {
			if (!MapItinerary.cache[this]) MapItinerary.cache[this] = {marker:0, result:0};
			
			// Add marker to map
			var marker = MapItinerary.buildMarker(pois[this]);
			if (marker) Sm2Poi.Map.gmap.addOverlay(marker);
			MapItinerary.cache[this].marker = marker;
			
			// Add result to result list
			var result = MapItinerary.buildResult(pois[this]);
			MapItinerary.$results.append(result);
			MapItinerary.cache[this].result = result;
			
			// Autofocus if given
			if (MapItinerary.poiFocus) {
				MapItinerary.map(MapItinerary.poiFocus);
				MapItinerary.poiFocus = 0;
			}
		});
	},
	
	buildMarker: function(data) {
		var marker = null;
		if (data.lat*1  &&  data.lon*1) {
			var fn = function() {
				MapItinerary.map(this._sm2Id);
			}
			marker = Sm2Poi.Map.createMarker(data.lat, data.lon, fn, data.mapIconId);
			marker._sm2Id = data.id;
		}
		return marker;
	},
	
	buildResult: function(data) {
		var mapable = data.lat*1 && data.lon*1;
		
		// Set map icon source
		var mapIconSrc = '/sitemanager2/poi/images/genericMapIcon.png';
		if (parseInt(data.mapIconId)) mapIconSrc = '/sitemanager2/poi/mapicon.php?id='+data.mapIconId+'&type=legend';
		
		
		$row = MapLocations.$resultTemplate.clone()
			.find('img.mapicon').attr('src', mapIconSrc).end()
			.find('.data_title').html(data.title).end()
			.find('.data_state').html(data.state).end()
			.find('a.map').attr('href', "javascript:MapItinerary.map("+data.id+")").end()
			.find('a.info').attr('href', '/links/'+data.id).click(function() { MapItinerary.saveLink(); }).end()
			;
			
		// If addressable, show address
		if (data.city  &&  data.state) $row.find('address').html(data.city+', '+data.state);
		else $row.find('address').remove();

			
		// If mappable, show link
		if (mapable) $row.addClass('mappable');
		else $row.find('img.mapicon').remove();
			
		return $row;
	},
	
	map: function(id) {
		// Save what marker was clicked on so it can be re-selected on page refresh
		//MapItinerary.saveLink('/'+id);
		
		// Open the info window
		MapItinerary.cache[id].marker.openInfoWindow(MapItinerary.buildInfoWindowHtml(Sm2Poi.Map.poiCache[id].data));
	},
	
	buildInfoWindowHtml: function(data) {
		var html = '<div class="infowin">';
		
		html += '<div class="title">' + data.title + '</div>';
		if(data.thumbnail) html += '<img class="mapThumb" src="/sitemanager2/files/'+data.thumbnail+'" />'; 
		html += '<address>'+data.streetAddress+'<br />'+data.city+', '+data.state+' '+data.zip+'</address>';
		
		if (data.phone) html += '<div><b>Phone:</b> ' + data.phone + '</div>';

		if (data.email) html += '<div><b>Email:</b> ' + data.email + '</div>';
		
		html += '<div class="links">';
		html += 	'<a href="/links/'+data.id+'" onclick="MapItinerary.saveLin()"><img src="/sitemanager2/poi/images/icon_info.png" alt="[>]"/> More Info</a>';
		html += 	'<br />';
		html += 	'<a rel="'+data.id+'" href="" onclick="MapItinerary.remove('+data.id+'); return false;"><img src="/sitemanager2/poi/images/icon_itineraryRem.png" alt="[-]"/> Remove from My Itinerary</a>';
		html += '</div>';
		
		html += '</div>';
		return html;
	},
	
	saveLink: function(extra) {
		window.location.replace('#itinerary' + (extra || ''));
	}
};
Sm2Poi.Map.addListener('map_ready', MapItinerary.setup);

$(function() {
	Sm2Poi.Map.initialize(_initVars);
});
