function ToolTip(marker, html, width, gmapInstance) {
	this.html_ = html;
	this.width_ = (width ? width + 'px' : 'auto');
	this.marker_ = marker;
	this.gmapInstance = gmapInstance;
}

ToolTip.prototype = new GOverlay();

ToolTip.prototype.initialize = function(map) {
	var div = document.createElement('div');
	div.style.display = 'none';
	this.gmapInstance.map.getPane(G_MAP_FLOAT_PANE).appendChild(div);
	this.map_ = map;
	this.container_ = div;
}

ToolTip.prototype.remove = function() {
	this.container_.parentNode.removeChild(this.container_);
}

ToolTip.prototype.copy = function() {
	return new ToolTip(this.html_);
}

ToolTip.prototype.redraw = function(force) {
	if(!force) return;

	var pixelLocation = this.map_.fromLatLngToDivPixel(this.marker_.getPoint());
	this.container_.innerHTML = this.html_;
	this.container_.className = 'mapToolTip';
	this.container_.style.left = pixelLocation.x + 10 + 'px';
	this.container_.style.top  = pixelLocation.y - 20 + 'px';

	//this.container_.style.whiteSpace = 'nowrap';
	if(this.width_ != 'auto') this.container_.style.overflow = 'hidden';

	this.container_.style.display = 'block';
}

GMarker.prototype.ToolTipInstance = null;
GMarker.prototype.openToolTip = function(content, gmapInstance) {
	// nicht zeigen wenn ein custom InfoWindow da ist
	if(this.ToolTipInstance == null) {
		this.ToolTipInstance = new ToolTip(this, content, null, gmapInstance);
	}
}

GMarker.prototype.closeToolTip = function() {
	// nicht zeigen wenn ein custom InfoWindow da ist
	if(this.ToolTipInstance != null) {
		this.ToolTipInstance = null;
	}
}

var CB_GoogleMap = Class.create();
CB_GoogleMap.prototype = {
	'initialize': function(mapElement, options) {
		options = options || {};

		// save options
		this.options = options;

		// Wenn Browser für GoogleMap geeignet ist, dann Map aufbauen
		if (GBrowserIsCompatible()) {
			if(options.startZoom != undefined) {
				startZoom = options.startZoom;
			}

			if(options.scriptURI != undefined) {
				this.scriptURI = options.scriptURI;
			}

			if(options.resourcePath != undefined) {
				this.resourcePath = options.resourcePath;
			}

			if(options.startLat != undefined) {
				startLat = options.startLat;
			}

			if(options.startLng != undefined) {
				startLng = options.startLng;
			}

			if(options.controlZoom == 'small') {
				var zoomControl = new GSmallMapControl();
			} else if(options.controlZoom == 'nocontrol') {
				// do nothing
			} else {
				var zoomControl = new GLargeMapControl();
			}

			if(options.controlType == 'nocontrol') {
				// do nothing
			} else {
				var typeControl = new GMapTypeControl();
			}

			// Map mit Controls definieren
			var map = new GMap2(mapElement);
			this.map = map;

			// add zoom control
			if(zoomControl instanceof Object) {
				this.map.addControl(zoomControl);
			}

			// add type control
			if(typeControl instanceof Object) {
				this.map.addControl(typeControl);
			}

			var point = new GLatLng(startLat, startLng);
			this.map.setCenter(point, startZoom);

			// desclare variables, so that they are available in the object scope
			this.mainMarkerPoint;
			this.mainMarker;

			// enable scroll wheel zoom :-)
			if(options.enableScrollwheelZoom == true) {
				this.map.enableScrollWheelZoom();
			}

			// i want a pointer to get latlng of the point i've clicked
			/*
			GEvent.addListener(this.map, 'click', function(overlay, latlng) {
				console.log(latlng.lat());
				console.log(latlng.lng());
				console.log(this.map.getCenter());
				console.log(this.map.getZoom());
			}.bind(this));
			*/
	    } else {
	    	mapElement.innerHTML = 'Bitte aktivieren Sie JavaScript, um die Karte zu sehen.';
	    }
	},

	'moveListeners' : function() {
		// bind zoom event to the the event listener function
		GEvent.addListener(this.map, 'movestart', function() {
			this.previousZoomLevel = this.map.getZoom();
		}.bind(this));

		// bind zoom event to the the event listener function
		GEvent.addListener(this.map, 'moveend', function() {
			this.updateMarkers();
		}.bind(this));
	},

	'setMarkerOnMap' : function(lat, lng, options) {
		// set default values
		options = options || {};

		if(options.marker == undefined) {
			options.marker = "mm_20_blue.png";
		}

		if(options.markerWidth == undefined) {
			options.markerWidth = 12;
		}
		if(options.markerHeight == undefined) {
			options.markerHeight = 20;
		}

		// check icon, if set
		// if(options.icon != undefined) {
			// Create our "tiny" marker icon
			var icon = new GIcon();
			icon.image = this.resourcePath + options.marker;
			icon.shadow = this.resourcePath + "shadow/mm_20_shadow.png";
			icon.iconSize = new GSize(options.markerWidth, options.markerHeight);
			icon.shadowSize = new GSize(22, 20);
			icon.iconAnchor = new GPoint(Math.round(options.markerWidth / 2), options.markerHeight);
			icon.infoWindowAnchor = new GPoint(5, 1);

			var marker = new GMarker(new GLatLng(lat, lng), icon);
		// }

		if(!(marker instanceof GMarker)) {
			var marker = new GMarker(new GLatLng(lat, lng));
		}

		return marker;
	},

	// setzt einen Marker auf die googlemap dieses Objekts
	'addMarker': function(lat, lng, name, description, options) {
		// set default values
		options = options || {};

		var marker = this.setMarkerOnMap(lat, lng, options);

		// if this is the main icon, than add onClickHandler
		if(options.showInfoBox == true) {
			GEvent.addListener(marker, 'click', function() {
				this.map.savePosition();
				marker.openInfoWindowHtml('<b>' + name + '</b><br/>' + description);

				GEvent.addListener(this.map, 'infowindowclose', this.map.returnToSavedPosition);
			}.bind(this));

			GEvent.addListener(marker, 'mouseover',
				function() {
					marker.openToolTip(name, this);
					this.map.addOverlay(marker.ToolTipInstance);
				}.bind(this)
			);

			GEvent.addListener(marker, 'mouseout',
				function() {
					this.map.removeOverlay(marker.ToolTipInstance);
					marker.closeToolTip();
				}.bind(this)
			);
		}

		this.map.addOverlay(marker);
		return marker;
	},

	'addHostelMarker' : function(item) {
		var marker = this.setMarkerOnMap(item.lat, item.lng);

		GEvent.addListener(marker, 'mouseover',
			function() {
				marker.openToolTip(item.hostelName, this);
				this.map.addOverlay(marker.ToolTipInstance);
			}.bind(this)
		);

		GEvent.addListener(marker, 'mouseout',
			function() {
				this.map.removeOverlay(marker.ToolTipInstance);
				marker.closeToolTip();
			}.bind(this)
		);

		// evaluate the info templates and send them as an array to the map
		GEvent.addListener(marker, 'click', function() {
			var tabs = [
				new GInfoWindowTab('Description', this.templates.hostelDescription.evaluate(item)),
				new GInfoWindowTab('Contact', this.templates.hostelContact.evaluate(item))
			];
			marker.openInfoWindowTabsHtml(tabs, {maxWidth: '300', maxHeight: '160'});
		}.bind(this));

		this.map.addOverlay(marker);
		return marker;
	},

	'updateMarkers' : function() {
		var zoomLevel = this.map.getZoom();

		var bounds = this.map.getBounds();
		var neBoundary = bounds.getNorthEast();
		var swBoundary = bounds.getSouthWest();

		var url = this.scriptURI + '?&ne=' + neBoundary.toUrlValue() + '&sw=' + swBoundary.toUrlValue();

		// handle the continent request
		if(zoomLevel >= 0 && zoomLevel <= 3) {
			url = this.resourcePath + 'data/continents.json';
		}

		// handle the continent request
		if(zoomLevel > 3 && zoomLevel <= 5) {
			url = this.resourcePath + 'data/countries.json';
		}

		// handle the continent request
		if(zoomLevel > 5 && zoomLevel <= 9) {
			url = this.resourcePath + 'data/cities.json';
		}

		// handle the continent request
		if(zoomLevel > 9 && zoomLevel <= 17) {
			url = url + '&get=hostels';
		}

		new Ajax.Request( url,
			{
				method : 'get',
				onSuccess: function(response) {
					this.insertPoints(response);
				}.bind(this)
			}
		);
	},

	'insertPoints' : function(response) {
		var responseText = response.responseText;

		// if the zoom level before the update differs from the level after the update, clear point cache and remove all overlays (i.e. markers)
		var zoomLevel = this.map.getZoom();
		if(this.previousZoomLevel != zoomLevel) {
			// build up a point cache
			this.points = {
				'global'     : {},
				'hostels'    : {}
			}
			this.map.clearOverlays();
		}

		// pois are JSON encoded arrays of points
		var elements = responseText.evalJSON();
		elements.each(function(item) {
			if(zoomLevel <= 9) {
				// check whether the poi is in the cache
				if(!this.points.global[item.id]) {
					// cache the poi
					this.points.global[item.id] = item;

					// add specific marker for other levels than the hostels
					this.addMarker(item.lat, item.lng, item.title, item.description, {showInfoBox : true});
				}
			} else {
				// check whether the poi is in the cache
				if(!this.points.hostels[item.hostelId]) {
					// put poi in currents level cache
					this.points.hostels[item.hostelId] = item;

					// add specific marker for a hostel
					this.addHostelMarker(item);
				}
			}
		}.bind(this));
	},

	'onAjaxError': function(request) {

	}
}
