(function ($, window) {
	// local reference to google.maps.* namespace
	var gm = null;
	
	/** 
	 * simple interface to google maps.
	 * use directly or subclass by prototype.
	 * set properties before calling .init() to configure.
	 */
	var MapHelper = {
		// selector for map element
		selectMapEl: '#mapTarget',
		// selector for detail pane
		selectMapDetailEl: '#mapPlayerDetail',
		defaultMarkerImage: 'images/layout/spriteImg/mapMarker.png',
		defaultMarkerShadowImage: 'images/layout/spriteImg/mapMarkerShadow.png',
		// startposition for the map
		startPosition: {
			lat: 51.36266741622832, 
			lng: 10.28176010383548
		},
		wasInitialized: false,
		// last opened infowindow instance
		currentInfoWindow: null,
		// start/default zoom
		defaultZoom: 6,
		minZoom: 6,
		maxZoom: 10,
		// jQuery result with map container
		mapEl: null,
		// jQuery result with detail pane
		detailEl: null,
		// google.maps.instance
		map: null,
		// google.maps.* namespace
		gm: null,
		// a map of locations and their detail data:
		// some string id => {lat, lng, title, city, img, url} 
		data: {},
		/** 
		 * a google.maps.marker for given location
		 * @param {Object} data
		 * @return google.maps.Marker
		 */
		createMarker: function (data) {
			if (!data.lng || !data.lat || !data.text) {
				return null;
			}
			var helper = this;
			var marker = data.marker = new gm.Marker({
				map: this.map,
				title: data.city,
				flat: false, 
				position: new gm.LatLng(data.lat, data.lng),
				icon: this.defaultMarkerImage,
				shadow: this.defaultMarkerShadowImage,
				shape: {
					type: 'poly',
					coord: [
						10, 0,
						21, 18,
						13, 19,
						13, 33,
						8, 33,
						8, 19,
						0, 18
					]
				}
			});
			gm.event.addListener(marker, 'click', function (overlay, coord) {
				helper.handleClick(data);
			});
			return marker;
		},
		/** 
		 * innerhtml for a map bubble
		 * @param {Object} data
		 * @return string/HTML 
		 */
		getInfoWindowHtml: function (data) {
			var html =  '<div class="mapMarkerInfo">', textClass = '';
			if (data.img) {
				textClass =  'textWithImage';
				html += '<div class="floatLeft"><img class="mapMarkerInfoImage" src="' + data.img + '" alt="" /></div>';
			}
			html += '<div class="text ' + textClass + '">';
            if (data.city)
    			html += '<span class="date"><strong>' + data.city + '</strong></span>';
			html += '<h4>' + data.title + '</h4>';
			html += '</div></div>';
			return html;
		},
		/** 
		 * loads ajax content into detail pane for given location
		 * @param {Object} data
		 * @return void
		 */
		updatePlayer: function (data) {
            if (data) {
                if (data.embed) {
                    $.getJSON(JSON_SOURCE_DETAIL_URL + data.id.slice(3),
                        function(data) {
                            $("#mapPlayerCurrentVideo").html(data.embed);
                        }
                    );
                } else {
                    $("#mapPlayerCurrentVideo").html('');
                }
                $("#mapPlayerCity").html(data.city);
                $("#mapPlayerHeadline").html(data.title);
                $("#mapPlayerContent").html(data.text);
            }
		},
		/** 
		 * displays a tooltip above the marker of a given entry
		 * @param {Object} data
		 * @return void
		 */
		showInfoWindow: function (data) {
			if (this.currentInfoWindow) {
				this.currentInfoWindow.close();
			}
			this.currentInfoWindow = new gm.InfoWindow({
				content: this.getInfoWindowHtml(data),
				position: new gm.LatLng(data.lat, data.lng) // do not attach to marker, as pixelOffset would otherwise munge window content
			});
			this.currentInfoWindow.open(this.map);
		},
		/** 
		 * scrolls map into view, updates detail pane and open info window for a given location
		 * @param {Object} data
		 * @return void
		 */
		handleClick: function (data) {
			if (!this.wasInitialized) {
				return;
			}
			if (typeof data == 'string') {
				data = this.data[data];
			}
            if (!data.text)
                return null;
			this.updatePlayer(data);
			this.showInfoWindow(data);
			$.scrollTo(this.mapEl, {
				duration: 500
			});
		},
		/** 
		 * appends markers for all entries in current data where no marker instance is found
		 * @return void
		 */
		appendMarkers: function () {
			var helper = this;
			$.each(this.data, function () {
				var entry = this;
				if (this.marker) {
					return;
				}
				helper.createMarker(this);
			});
		},
		/** 
		 * set up this map
		 * @return void
		 */
		init: function () {
			// save google namespace to local variable, save this instance for nested functions
			gm = this.gm = google.maps, helper = this;
			
			// get target elements
			this.mapEl = $(this.selectMapEl);
			this.detailEl = $(this.selectMapDetailEl);
			
			// default configuration for the map, excludes maptype, as zoom boundaries are to be applied later
			var options = {
				mapTypeControl: false,
				navigationControl: true,
				navigationControlOptions: {
					position: gm.TOP_LEFT
				},
				center: new gm.LatLng(this.startPosition.lat, this.startPosition.lng),
				zoom: this.defaultZoom
			};
			this.map = new gm.Map(this.mapEl.get(0), options);
			
			// a simple base maptype to implement zoom limiting for this map
			function LimitedZoomMapType() {};
			LimitedZoomMapType.prototype = {
				tileSize: new gm.Size(256, 256),
				getTile: function () {
					// no idea how else to retrieve the roadmap base type...
					return helper.map.mapTypes.get(gm.MapTypeId.ROADMAP).getTile.apply(this, arguments);
				},
				alt: '',
				name: '',
				maxZoom: this.maxZoom,
				minZoom: this.minZoom
			};
			// set map type
			this.map.mapTypes.set('limitedzoommap', new LimitedZoomMapType());
			this.map.setMapTypeId('limitedzoommap');

			var me = this;
			this.initMarkersCallback = gm.event.addListener(this.map, 'tilesloaded', function() {
				me.appendMarkers();
			  if(me.initMarkersCallback) {
						gm.event.removeListener(me.initMarkersCallback);
				}
				me.wasInitialized = true;
				$(me.mapEl).trigger('ready.map', [{MapHelper: me}]);
			});
		}
	};
	
	// go global
	window.MapHelper = MapHelper;
})(jQuery, window);





