/**
 * Global Arena maps
 */
window.ArenaMap = (function($){

	var MAP_VISIBLE = 'visible',
		MAP_FULLSCREEN = 'fullscreen',
		MAP_SETTINGS = 'settings',
		MAP_GOTO = 'goto',
		MAP_DETAILS = 'details',
		MAP_CLOSEINFO = 'closeinfo',
		MAP_BALLOON = 'balloon',
		MAP_IMAGE = 'image',
		MAP_MARKER = 'marker',
		MAP_OFFICE = 'office',
		MAP_REALESTATE = 'realestate',
        MAP_BUSINESSPARTNER = 'businesspartner',
		MAP_POLY = 'polygon',
		MAP_GROUP = 'group',
		MAP_POLY_COLOR = '#2e9ed8',

		MAP_ZOOMED_OUT = 'out',
		MAP_ZOOMED_IN = 'in',

		MAP_ZOOM_COUNTRIES = 6,
		MAP_DEBUG = true;

	google.load("maps", "2");
	google.load("earth", "1");


	function Map(element, gui) {
		this.gui = gui;
		this.initMap(element);

		ArenaMap.Markers.initialize();

		this.manager = new ArenaMap.Manager(this.map, {
			clickHandler: this.handleGroupClick.bind(this),
			hoverHandler: this.handleMarkerHover.bind(this),
			leaveHandler: this.handleMarkerLeave.bind(this)
		});
		
		this.jsonLoader = XMLHttp.getInstance("JSONLoader");
		this.htmlLoader = XMLHttp.getInstance('HTMLLoader');
		this.xmlLoader = XMLHttp.getInstance('XMLLoader');

		this.mapData = [];
		
		var dataURL = 'ajax/markers.js',
			links = $('head link[rel=map-goto]'),
			data = $('head link[rel=map-data]'),
			kml = $('head link[rel=map-kml]'),
			rss = $('head link[rel=map-rss]');

		if(links[0]) {
			this.handleClick(links[0], 'map-goto');
		} else if(data[0]){
			dataURL = data.attr('href');
			this.loadJSONData(dataURL);
		} else if(rss[0]){
			dataURL = rss.attr('href');
			this.loadRSSData(dataURL);
		} else if(kml[0]) {
			dataURL = kml.attr('href');
			this.loadKMLData(dataURL);
		}
	}

	Map.prototype = {
		initMap:function(element) {
			this.map = new GMap2(element);
			this.map.addMapType(G_PHYSICAL_MAP);
			this.map.addMapType(G_SATELLITE_3D_MAP);
			this.map.setCenter(new GLatLng(52.369174, 4.899130), 7);
			this.map.setMapType(G_PHYSICAL_MAP);
			
			this.mapTypes = this.map.getMapTypes();
			this.mapControl = new GLargeMapControl();
			this.typeControl = new GMapTypeControl();

			$(document).addRelation(/(^|\s)map-/, this.handleClick, this);

			this.mapSettings = $('#map-settings');
			this.mapSettings.click(this.handleSettingsClick.bind(this));
		
			GEvent.addListener(this.map, 'zoomend', this.handleZoom.bind(this));
		},

		loadJSONData:function(url, post) {
			if(post) {
				this.jsonLoader.sendAndLoad(post, url, this.addMapData, this);
			} else {
				this.jsonLoader.load(url, this.addMapData, this);
			}
		},

		loadRSSData:function(url) {
			this.xmlLoader.load(url, this.parseRSS, this);
		},

		parseRSS:function(rss) {
			var data = ArenaMap.Parsers.RSS.parse(rss);
			this.addMapData(data);
		},

		loadKMLData:function(url) {
			this.xmlLoader.load(url, this.parseKML, this);
		},

		parseKML:function(xml) {
			var data = ArenaMap.Parsers.KML.parse(xml);
			this.addMapData(data, true);
		},

		addMapData:function(response) {
			var locations = response.markers;
			
			if(!locations || locations.length == 0) {
				MAP_DEBUG && console.log('response contained no markers!', response);
				return;
			}

			this.removeMarkers();
			this.parseLocations(locations);			
			this.applyJsonOptions(response.options || {});
			this.mapData.push(response);

			this.applyMapSettings();

			if(locations.length == 1) {
				var markers = this.manager.getOverlays();
				this.displayDetails(markers[0]);
			}
		},

		parseLocations:function(locations) {
			for(var i=0; i<locations.length; i++) {
				try {
					
					var markers = this.createMarkers(locations[i]);
					for(var j=0; j<markers.length; j++) {
						this.addMarker(markers[j]);
					}
				
				} catch (e) {
					MAP_DEBUG && console.log(e, locations[i]);
				}
			}
		},

		createMarkers:function(item, nested) {
			var markers = [];
			var point = null;
			
			if(item.lat && item.lng) {
				var lat = parseFloat(item.lat);
				var lng = parseFloat(item.lng);
				if(Math.abs(lat) > 180 || Math.abs(lng) > 180) {
					window.console && console.log('Error in map coordinates of marker "' + item.label + '" data: ', item);
					return markers;
				}

				point = new GLatLng(item.lat, item.lng);
			}

			if(item.polygon) {
				item.coords = ArenaMap.Parsers.KML.parseCoordinates(item.polygon);
				var polyMarker = new ArenaMap.Markers.PolygonMarker(point, item);
				markers.push(polyMarker)

				if(point) {
					markers.push(new ArenaMap.Markers.Office(point, item));
				} else if(item.details){
					polyMarker.setType(MAP_MARKER);
				}
			} else if(point) {
                var marker = new ArenaMap.Markers.Marker(point, item);
                if (item.type) {
                    marker.setType(item.type);
                }
				markers.push(marker);
			}			
		
			return markers;
		},

		applyJsonOptions:function(options) {
			if(options.mapType) {
				var types = this.mapTypes;
				for(var i=0; i<types.length; i++) {
					if(types[i] == options.mapType) {
						this.map.setMapType(options.mapType);
					}
				}
			}

			if(options.lat && options.lng) {
				var zoom = options.zoom || 6;
				var point = new GLatLng(options.lat, options.lng);
				this.map.setCenter(point, zoom);
			} else {
				this.showAll();
			}
		},

		applyMapSettings:function() {
			var inputs = this.mapSettings.find('input');
			var reg = /map-toggletype/i;
			for(var i=0; i<inputs.length; i++) {
				var input = inputs[i];
				if(reg.test(input.name)) {
					this.manager.setTypeEnabled(input.value, input.checked);
				}
			}

			this.manager.update();
		},

		addMarker:function(marker) {
			var groupable = true;
			var type = marker.type;
			if(type == MAP_OFFICE || type == MAP_POLY) {
				groupable = false;
			}
			
			this.manager.addOverlay(marker, groupable);

			var clickHandler = this.handleMarkerClick.bind(this),
				overHandler = this.handleMarkerHover.bind(this),
				outHandler = this.handleMarkerLeave.bind(this);

			marker.click(clickHandler);
			marker.hover(overHandler, outHandler);
		},
		
		removeMarker:function(marker) { 
			this.manager.removeOverlay(marker); 
		},

		removeMarkers:function() {
			this.manager.clearOverlays();
			this.infoWindow = null;
			this.tooltip = null;
			this.activeMarker = null;
		},

		displayDetails:function(marker) {
			try {
				this.infoWindow && this.infoWindow.hide();
				this.activeMarker && this.activeMarker.show(true);
			} catch (e) { 
				MAP_DEBUG && console.log(e);
			}

			this.activeMarker = marker;
			
			if(marker) {
				var self = this;
				if(marker.data.description) {
					this.displayInfoWindow(marker, marker.data.description);
				} else if(marker.data.details) {
					this.htmlLoader.load(marker.data.details, function(html) {
						self.displayInfoWindow(marker, html);
					});
				}
			}
		},

		displayInfoWindow: function(marker, summary, latlng) {
			var point = latlng || marker.getLatLng();

			if(!this.infoWindow) {
				this.infoWindow = new ArenaMap.Markers.InfoWindow(point);
				this.manager.addToMap(this.infoWindow);
			}

			this.gui && this.gui.toggleMap(MAP_VISIBLE);

			var win = this.infoWindow;
			win.setLatLng(point);
			win.write(summary);
			win.show();
			win.redraw();

			var bounds = this.infoWindow.getBounds(),
				clat = (bounds.getNorthEast().lat() + bounds.getSouthWest().lat()) /2;
				clng = (bounds.getNorthEast().lng() + bounds.getSouthWest().lng()) /2;
			
			this.map.panTo(new GLatLng(clat,clng));
		},

		displayTooltip:function(marker, latlng, message) {
			if(!this.tooltip) {
				this.tooltip = new ArenaMap.Markers.Tooltip(latlng || marker.getLatLng());
				this.manager.addToMap(this.tooltip);
			}

			this.tooltip.open(marker, latlng, message);
		},

		hideTooltip:function() {
			this.tooltip.hide();
		},

		handleClick:function(link, rel) {
			var type = /map-([^$|\s]+)/.exec(rel)[1];
			var href = link.getAttribute('href');
			switch (type) {
				
				// move the map to the given coordinates and zoom level
				case MAP_GOTO:
					try {
						this.displayDetails(false);
						this.gui && this.gui.toggleMap(MAP_VISIBLE);
						this.gui && this.gui.scrollTo(0);

						var lat = /lat=([^&#\$]+)/.exec(href)[1];
						var lng = /lo?ng=([^&#\$]+)/.exec(href)[1];
						var zoom = parseInt(/zoom=([^&#\$]+)/.exec(href)[1], 10);
						this.map.setCenter(new GLatLng(lat, lng), zoom);
						return true;
					} catch (e) {
						MAP_DEBUG && console.log(e);
					}
				break;
				
				// load map data via a json file, removes existing markers
				case MAP_DETAILS:
					try {
						this.gui && this.gui.toggleMap(MAP_VISIBLE);
						this.gui && this.gui.scrollTo(0);
						this.loadJSONData(href);
						return true;
					} catch(e) {
						MAP_DEBUG && console.log(e);
					}
				break;

				// go full screen
				case MAP_FULLSCREEN:
					this.gui && this.gui.toggleMap(MAP_FULLSCREEN);
					$(link).toggleClass('active');
					return true;
				break;
				
				// close info balloon of an active marker
				case MAP_CLOSEINFO:
					this.displayDetails(false);
					return true;
				break;

				// close info balloon of an active marker
				case MAP_SETTINGS:
					this.mapSettings.toggleClass('active');
					$(link).toggleClass('active');
					return true;
				break;
			}
		},

		handleSettingsClick:function(e) {
			var input = $(e.target).closest('input');
			if(input.length) {
				var name = input[0].name,
					value = input[0].value,
					checked = input[0].checked;

				if(/map-toggletype/i.test(name)) {
					this.manager.setTypeEnabled(value, checked);
					this.manager.update();
				}
			}
		},

		handleMarkerClick:function(marker) {
			var type = marker.type,
				data = marker.data,
				point = marker.getLatLng();

			switch (type) {
				case MAP_MARKER:
				case MAP_OFFICE:
				case MAP_REALESTATE:
                case MAP_BUSINESSPARTNER:
					if(data.details) {
						var self = this;
						this.htmlLoader.load(data.details, function(html) {
							self.displayInfoWindow(marker, html, point);
						});
					} else if(data.description){
						this.displayInfoWindow(marker, description, point);
					}
				break;
				case MAP_GROUP:
					this.handleGroupClick(marker);
				break;
				case MAP_POLY:
					var markers = data.markers;
					if(typeof markers == 'string') {
						this.loadJSONData(markers);
					} else if(markers) {
						this.addMapData(data);
					}
				break;
				default:
			}
		},

		handleGroupClick:function(marker) {
			this.hideTooltip();
			var point = marker.getLatLng(),
				zoom = this.map.getZoom();

			this.map.setCenter(point);
			this.map.setZoom(zoom + 2);
		},

		handleMarkerHover:function(marker) {
			var point = marker.getLatLng();
			this.displayTooltip(marker, point);
		},

		handleMarkerLeave:function() {
			this.hideTooltip();
		},

		handleZoom:function(from, to) {
			if(to < from) {
				this.checkRegionalZoom();
			}
		},

		checkRegionalZoom:function() {
			var bounds = this.manager.getBounds();
			var mapBounds = this.map.getBounds();

			if(mapBounds.containsBounds(bounds)) {
				var index = this.mapData.length -2;
				if(index >= 0) {
					this.mapData.length -= 1;
					var parent = this.mapData.pop();
					this.addMapData(parent);
				}
			}
		},

		showAll:function() {
			var bounds = this.manager.getBounds();
			
			var clat = (bounds.getNorthEast().lat() + bounds.getSouthWest().lat()) /2;
			var clng = (bounds.getNorthEast().lng() + bounds.getSouthWest().lng()) /2;
			var zoom = this.map.getBoundsZoomLevel(bounds);
			
			this.map.setZoom(zoom);
			this.map.setCenter(new GLatLng(clat,clng));
		},

		toggleControls:function(toggle) {
			if(toggle) {
				this.map.addControl(this.mapControl);
				this.map.addControl(this.typeControl);
				this.mapSettings.show();
			} else {
				this.map.removeControl(this.mapControl);
				this.map.removeControl(this.typeControl);
				this.mapSettings.hide();
			}
		},

		checkResize:function () {
			this.map.checkResize();
		}
	};


	/**
	 * Expose
	 */
	return {
		Map: Map
	};

})(jQuery)
