
function MapClassSettings(divId, centerLat, centerLng, zoom, initFunc) {
	this.divId = divId;
	this.centerLat = centerLat;
	this.centerLng = centerLng;
	this.zoom = zoom;
	this.initFunc = initFunc;
};



function MapClass() {

	var self = this;
	
	var DEFAULT_ICON_HEIGHT = 30;

	var BUBBLE_MAX_WIDTH = 400;
	var BUBBLE_MAX_HEIGHT = 300;
	
	var mapSettings = null;
	var map = null;
	
	var imageOverlayIds = new Array();
	
	// holds all markers
	var markers = new Array();
	var bubbles = new Array();
	var paths = new Array();
	var overlays = new Array();
	
	
	this.loadCallback = function() {
		var options = {
			zoom: mapSettings.zoom,
			center: new google.maps.LatLng(mapSettings.centerLat, mapSettings.centerLng),
			mapTypeId: google.maps.MapTypeId.ROADMAP
		};

		map = new google.maps.Map(
			document.getElementById(mapSettings.divId),
			options
		);

		// now we can set the super class of ImageOverlay
		ImageOverlay.prototype = new google.maps.OverlayView();
		
		if(map) {
			mapSettings.initFunc();
		}

	};


/*
this.load = function(divId, engine, center_lat, center_lng, zoom, initFunc) {
		mapSettings = new MapClassSettings(divId, center_lat, center_lng, zoom, initFunc);
		self.loadCallback();
	};
*/
		
this.loadAsync = function(divId, engine, center_lat, center_lng, zoom, initFunc) {
		mapSettings = new MapClassSettings(divId, center_lat, center_lng, zoom, initFunc);
			
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.src = "http://maps.google.com/maps/api/js?sensor=false&callback=Map.loadCallback";
		document.body.appendChild(script);
};

this.unload = function() {
		// TODO?
	};
	
	
	
	
	
this.loadCurrentMediaItems = function() {
		if(map) {
			
			$.ajax({
				url: 'map/getAsJson',
				dataType: 'json',
				cache: false,
				success: function(data){
					json = data; //transport.responseText.evalJSON();
					if(json) {
						self.update(json);
					}
					else {
						alert("Error while trying to receive Json data.");
					}
				}
			});
		}
	};


this.addMarker = function(posLat, posLng, iconUrlAndSize, labelText, bubbleText) {
		var center = new google.maps.LatLng(posLat, posLng);
		var icon = null;

		if(iconUrlAndSize != null) {
			icon = new google.maps.MarkerImage(
				// url
				iconUrlAndSize[0],
				iconUrlAndSize.length > 1 ? new google.maps.Size(iconUrlAndSize[1][0], iconUrlAndSize[1][1]) : null,
				new google.maps.Point(0, 0),
				iconUrlAndSize.length > 2 ? new google.maps.Point(iconUrlAndSize[2][0], iconUrlAndSize[2][1]) : null
			);
		}

		var bubble = null;
		if(bubbleText) {
			bubble = new google.maps.InfoWindow({
			    content: bubbleText,
			    maxWidth: BUBBLE_MAX_WIDTH
			});
			bubble.isVisible = false;
			google.maps.event.addListener(bubble, 'closeclick', function() {
				bubble.isVisible = false;
			});
		}
		
		var marker = new google.maps.Marker({
			position: center,
			map: map
		});
		
		if(icon) {
			marker.setIcon(icon);
		}
		if(labelText) {
			marker.setTitle(labelText);
		}
		
		markers.push(marker);

		if(bubble) {
			google.maps.event.addListener(marker, 'click', function() {
				// open if not visible yet
				if(!bubble.isVisible) {
					bubble.open(map, marker);
					bubble.isVisible = true;
				}
				// close all other bubbles
				$.each(bubbles, function(index, b){
					if(b != bubble) {
						b.isVisible = false;
						b.close();
					}
				});
			});
			bubbles.push(bubble);
		}
		
		return marker;
};

	
	
	// pathCoords = 1.0,7.2|-2.553,-22.1|...
this.latLonPointsFromString = function(pathCoords) {
		var points = new Array();
		var i=0;
		if(pathCoords.length > 0) {
			$.each(pathCoords.split("|"), function(index, sPoint){
				p = sPoint.split(",");
				points[i++] = new google.maps.LatLng(p[0], p[1]);
			});
		}
		return points;
};


this.latLonDistance = function(point1, point2) {
	var toRad = function(d) {
		return 3.14 * d / 180.0;
	}

	var lat1 = toRad(point1.lat());
	var lat2 = toRad(point2.lat());
	var lon1 = toRad(point1.lng());
	var lon2 = toRad(point2.lng());
	
	
	var R = 6371; // km
	var dLat = lat2-lat1;
	var dLon = lon2-lon1; 
	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;
};
	
	
this.addPath = function(points) {
	pOpts = {
		clickable: false,
		geodisc: true, // render as shortests paths along the earth surface
		map: map,
		path: points,
		strokeColor: '#ff0000',
		strokeOpacity: 0.8,
		strokeWeight: 2 // pixels
//		zIndex: 999
	};
	p = new google.maps.Polyline(pOpts);
	
	paths.push(p);
};


this.addImageOverlayImpl = function(url, west, south, east, north) {
	  var overlay = new ImageOverlay(west, south, east, north, url, map);
	  overlays.push(overlay);
	  return overlay;
};

this.addImageOverlay = function(item) {
	  var srcImage = item.extended.ImageUrl;
	  return self.addImageOverlayImpl(srcImage, item.extended.BoundWest, item.extended.BoundSouth, item.extended.BoundEast, item.extended.BoundNorth);
};

this.createLabelText = function(item) {
		yearInfo = item.base.CreationYear == null ? '' : (' (' + item.base.CreationYear + ')');
		return item.base.Title + yearInfo;
};

this.getImageIconUrlAndSize = function(item, defaultUrl, defaultRatio) {
		u = item.extended.ImageIconUrl;
		hasIcon = u != null && u != '';

		result = new Array();

		return new Array(
					(hasIcon ? u : defaultUrl),
					new Array(
						// fixed height at 30 pixels
						Math.ceil((hasIcon ? item.extended.ImageRatio : defaultRatio) * DEFAULT_ICON_HEIGHT),
						DEFAULT_ICON_HEIGHT
					)
				);
};

this.wrapBubbleContent = function(title, year, content) {
		yearInfo = year == null ? '' : (' (' + year + ')');

		return '<div style="margin-bottom: 5px; white-space: nowrap; margin-right: 15px;"><b>' + title + '</b> ' + yearInfo + ' </div>'
		+ '<div style="max-height: ' + BUBBLE_MAX_HEIGHT +'px; overflow: auto;">' + content.replace(/\n/g, '<br/>') + '</div>';
};

	// pure image
this.createBubbleContentForImage = function(item) {
		return self.wrapBubbleContent(item.base.Title, item.base.CreationYear,
				'<p><a href="' + item.extended.ImageUrl + '" target="_blank"><img src="' + item.extended.ImageThumbUrl+'" title="" style="border: none;" /></a></p>'
				+ '<p>' + item.base.Description + '</p>'
			);
};

	// postcard, press, etc..
this.createBubbleContentForTextItem = function(item) {
		yearInfo = item.base.CreationYear == null ? '' : (' (' + item.base.CreationYear + ')');
		imageThumbLink = '';
		fullTextLink = '';

		hasImageUrl = item.extended.ImageUrl != null && item.extended.ImageUrl != '';
		hasThumbUrl = item.extended.ImageThumbUrl != null && item.extended.ImageThumbUrl != '';
		hasTextUrl = item.extended.TextUrl != null && item.extended.TextUrl != '';

		if(hasImageUrl && hasThumbUrl) {
			imageThumbLink = '<p><a href="' + item.extended.ImageUrl + '" target="_blank"><img src="' + item.extended.ImageThumbUrl+'" border="0" title="" /></a></p>';
		}
		else if(hasImageUrl) {
			imageThumbLink = '<p><a href="' + item.extended.ImageUrl + '" target="_blank">Bilddarstellung</a></p>';
		}
		else if(hasThumbUrl) {
			imageThumbLink = '<p><img src="' + item.extended.ImageThumbUrl+'" title="" border="0" /></p>';
		}

		if(hasTextUrl) {
			fullTextLink = '<p><a href="' + item.extended.TextUrl + '" target="_blank">Zur Textquelle</a></p>';
		}

		return self.wrapBubbleContent(item.base.Title, item.base.CreationYear,
				imageThumbLink
				+ fullTextLink
				+ '<p>' + item.base.DescriptionShort + '</p>'
			);
};

this.update = function(items) {
		if(map) {
			self.clearContent();
			$.each(items, function(index, item) {
				self.addMediaItem(item);
			});
		}
};

this.clearObjects = function(objs) {
	$.each(objs, function(index, obj) {
		obj.setMap(null);
	});
	objs.length = 0;
}
this.clearPaths = function() {
	self.clearObjects(paths);
}
this.clearOverlays = function() {
	self.clearObjects(overlays);
}
this.clearContent = function() {
		if(map) {
			// markers: remove from map and delete
			$.each(markers, function(index, marker) {
				marker.setMap(null);
			});
			markers.length = 0;
			
			// bubbles
			$.each(bubbles, function(index, bubble) {
				bubble.close();
			});
			bubbles.length = 0;
			
			// paths
			self.clearPaths();
			
			// overlays
			self.clearOverlays();
		}
};

this.addEventListener = function(type, f) {
		if(map) {
			google.maps.event.addListener(map, type, f);
		}
};

this.addMediaItem = function(item) {
		switch(item.base.TypeId) {

			case 1: // images
				//add a marker
				labelText = self.createLabelText(item);
				bubbleText = self.createBubbleContentForImage(item);
				imageIconUrlAndSize = self.getImageIconUrlAndSize(item, '/images/media_item_default_icon_image.png', 1.39);
				self.addMarker(
					item.extended.PositionLat, item.extended.PositionLng,
					imageIconUrlAndSize,
					labelText, bubbleText
				);
				break;

			case 2: // postcard
				labelText = self.createLabelText(item);
				bubbleText = self.createBubbleContentForTextItem(item);
				imageIconUrlAndSize = self.getImageIconUrlAndSize(item, '/images/media_item_default_icon_postcard.png', 1.5);
				self.addMarker(
					item.extended.PositionLat, item.extended.PositionLng,
					imageIconUrlAndSize,
					labelText, bubbleText
				);
				break;

			case 3: // press
				labelText = self.createLabelText(item);
				bubbleText = self.createBubbleContentForTextItem(item);
				imageIconUrlAndSize = self.getImageIconUrlAndSize(item, '/images/media_item_default_icon_press.png', 1.0);
				self.addMarker(
					item.extended.PositionLat, item.extended.PositionLng,
					imageIconUrlAndSize,
					labelText, bubbleText
				);
				break;

			case 6: // text
				labelText = self.createLabelText(item);
				bubbleText = self.createBubbleContentForTextItem(item);
				imageIconUrlAndSize = self.getImageIconUrlAndSize(item, '/images/media_item_default_icon_text.png', 1.0);
				self.addMarker(
					item.extended.PositionLat, item.extended.PositionLng,
					imageIconUrlAndSize,
					labelText, bubbleText
				);
				break;

			case 4: // overlay
				self.addImageOverlay(item);
				break;

			case 5: // path
				sPoints = item.extended.Coords;
				self.addPath(self.latLonPointsFromString(sPoints));
				break;


		}
		
	};

}


var Map = new MapClass();




function ImageOverlay(west, south, east, north, image, map) {

	  // Now initialize all properties.
	  var west_ = west;
	  var south_ = south;
	  var east_ = east;
	  var north_ = north;
	  
	  var bounds_ = null;
	  var image_ = image;
	  var map_ = map;
	  var self = this;
	  
	  // We define a property to hold the image's
	  // div. We'll actually create this div
	  // upon receipt of the add() method so we'll
	  // leave it null for now.
	  var div_ = null;

	  
	this.onAdd = function() {

		  // Note: an overlay's receipt of onAdd() indicates that
		  // the map's panes are now available for attaching
		  // the overlay to the map via the DOM.

		  // Create the DIV and set some basic attributes.
		  var div = document.createElement('DIV');
		  div.style.border = "none";
		  div.style.borderWidth = "0px";
		  
		  if(typeof div.style.opacity == "string") {
			  div.style.opacity = '0.5';
		  } else {
			  div.style.filter = "alpha(opacity=50)";
		  }
		  
		  div.style.position = "absolute";

		  // Create an IMG element and attach it to the DIV.
		  var img = document.createElement("img");
		  img.src = image_;
		  img.style.width = "100%";
		  img.style.height = "100%";
		  div.appendChild(img);

		  // Set the overlay's div_ property to this DIV
		  div_ = div;

		  // We add an overlay to a map via one of the map's panes.
		  // We'll add this overlay to the overlayImage pane.
		  var panes = self.getPanes();
		  panes.overlayLayer.appendChild(div);
	};


	this.draw = function() {

		  // Size and position the overlay. We use a southwest and northeast
		  // position of the overlay to peg it to the correct position and size.
		  // We need to retrieve the projection from this overlay to do this.
		  var overlayProjection = self.getProjection();

		  // Retrieve the southwest and northeast coordinates of this overlay
		  // in latlngs and convert them to pixels coordinates.
		  // We'll use these coordinates to resize the DIV.
		  var sw = overlayProjection.fromLatLngToDivPixel(bounds_.getSouthWest());
		  var ne = overlayProjection.fromLatLngToDivPixel(bounds_.getNorthEast());

		  // Resize the image's DIV to fit the indicated dimensions.
		  var div = div_;
		  div.style.left = sw.x + 'px';
		  div.style.top = ne.y + 'px';
		  div.style.width = (ne.x - sw.x) + 'px';
		  div.style.height = (sw.y - ne.y) + 'px';
	};


	this.onRemove = function() {
		  div_.parentNode.removeChild(div_);
		  div_ = null;
	};

	
	this.updateBounds = function() {
		  swBound = new google.maps.LatLng(south_, west_);
		  neBound = new google.maps.LatLng(north_, east_);
		  bounds = new google.maps.LatLngBounds(swBound, neBound);
		  bounds_ = bounds;
	}
	this.redraw = function() {
		self.updateBounds();
		self.draw();
	}
	
	this.setWest = function(v) {
		west_ = v;
	}
	this.setSouth = function(v) {
		south_ = v;
	}
	this.setEast = function(v) {
		east_ = v;
	}
	this.setNorth = function(v) {
		north_ = v;
	}

	this.updateBounds();
	this.setMap(map);

	  
	  
}





