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;
	}
}

GLatLngBounds.prototype.extendByRatio = function(ratio) {
         // initialize bounds to be the same as original
         var largerBounds = new GLatLngBounds(
             this.getSouthWest(), this.getNorthEast());
         // get lat, lng of north east and south west
         var northEastLat = this.getNorthEast().lat();
         var northEastLng = this.getNorthEast().lng();
         var southWestLat = this.getSouthWest().lat();
         var southWestLng = this.getSouthWest().lng();
         var diffLat = northEastLat - southWestLat;
         var diffLng = northEastLng - southWestLng;
         // multiply with ratio
         northEastLat += diffLat * ratio;
         southWestLat -= diffLat * ratio;
         northEastLng += diffLng * ratio;
         southWestLng -= diffLng * ratio;
         // extend north east
         largerBounds.extend(new GLatLng(northEastLat, northEastLng));
         // extend south west
         largerBounds.extend(new GLatLng(southWestLat, southWestLng));
         return largerBounds;
     } 

var Gomio_GoogleMap = Class.create();
Gomio_GoogleMap.prototype = {
	'initialize': function(mapElement, options) {
		options = options || {};
		// Wenn Browser für GoogleMap geeignet ist, dann Map aufbauen
		if (GBrowserIsCompatible()) {
		
			if(options.scriptURI != undefined) {
				this.scriptURI = options.scriptURI;
			}
			
			if(options.resourcePath != undefined) {
				this.resourcePath = options.resourcePath;
			}

			var startLat = parseFloat($('startLat').innerHTML);
			if(options.startLat != undefined) {
				startLat = options.startLat;
			}
			var startZoom = parseFloat($('startZoom').innerHTML);
			if(options.startZoom != undefined) {
				startZoom = options.startZoom;
			}
			var startLng = parseFloat($('startLng').innerHTML);
			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);
			}
			
            if(!Element.empty($('maxlng')) &&
	            !Element.empty($('minlng'))&&
	            !Element.empty($('maxlat'))&&
	            !Element.empty($('minlat'))
	            )
	          {
	            
		            var maxlng = parseFloat($('maxlng').innerHTML);
	                var minlng = parseFloat($('minlng').innerHTML);
	                var maxlat = parseFloat($('maxlat').innerHTML);
	                var minlat = parseFloat($('minlat').innerHTML);
	       
	            
	            var bounds = new GLatLngBounds(new GLatLng(minlat,minlng),new GLatLng(maxlat,maxlng));
	            
	            var largerBounds = bounds.extendByRatio(0.20);
                var center_lat = (largerBounds.getNorthEast().lat() + largerBounds.getSouthWest().lat()) / 2.0;
                var center_lng = (largerBounds.getNorthEast().lng() + largerBounds.getSouthWest().lng()) / 2.0;
                var zoom = map.getBoundsZoomLevel(largerBounds);
                if(zoom>12)
                zoom=12;
                this.map.setCenter(new GLatLng(center_lat, center_lng), zoom) 
	          }
	        else
	        {
	            var point = new GLatLng(startLat, startLng);
			    this.map.setCenter(point, startZoom);
	        }
		

			
			//console.log(this.map.getZoom());

			// desclare variables, so that they are available in the object scope
			this.mainMarkerPoint;
			this.mainMarker;

			// retrieve markers on startup from server
			this.updateMarkers();

			// enable scroll wheel zoom :-)
			/*
			this.map.enableScrollWheelZoom();
			*/

			// 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));

			// 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));
			*/

			// build templates for the hostel tab info window
			
			//data erased <p>Phone: #{hostelPhone}</p><p>E-Mail: #{hostelEmail}</p>
			
			this.templates = {
				'hostelDescription'     : new Template('<b>#{hostelName}</b><div style="height: 62px; overflow: auto;"><p>#{hostelDescription}</p><p><b>Best Price</b>: #{minPrice}</p><p><a href="#{link}"><b>Book Now</b></a></p></div>'),
				'hostelContact'         : new Template('<div style="height: 80px; overflow: auto;"><p>#{hostelStreet}</p><p>#{hostelPostcode} #{hostelCity}</p></div>'),
				'locationDescription'   : new Template('<b>#{locationName}</b><div style="height: 62px; overflow: auto;"><p><a href="#{link}">View Details</a></p></div>'),
				'countryDescription'   : new Template('<b>#{countryName}</b><div style="height: 62px; overflow: auto;"><p><a href="#{link}">View Details</a></p></div>')
			};

			// build up a point cache
			this.points = {
				'global'     : {},
				'hostels'    : {},
				'cities'     : {},
				'countries'  : {}
			}
	    } else {
	    	mapElement.innerHTML = 'Bitte aktivieren Sie JavaScript, um die Karte zu sehen.';
	    }
	     var drawCircleBool = parseInt($('drawCircleBool').innerHTML);
            if(drawCircleBool==1)
            {
                this.drawCircle(new GPoint(startLat,startLng),new GPoint(startLat-0.05,startLng-0.05), "#ff0000", "#ff0000");
            }
	    
	},
    'drawCircle' : function (centerMarker, radiusMarker, borderColour, fillColour)
    {
     
    var normalProj = this.map.getCurrentMapType().getProjection();
    var zoom = this.map.getZoom();

    var centerPt = centerMarker;//normalProj.fromLatLngToPixel(centerMarker, zoom);
    var radiusPt = radiusMarker;//normalProj.fromLatLngToPixel(radiusMarker, zoom);

    var circlePoints = Array();

    with (Math) {
        var radius = floor(sqrt(pow((centerPt.x-radiusPt.x),2) +pow((centerPt.y-radiusPt.y),2)));

                        for (var i = 0 ; i < 361 ; i+=5 ) {
                                var aRad = i*(PI/180);
                                y = centerPt.y + radius * sin(aRad)
                                x = centerPt.x + radius * cos(aRad)
                                var p = new GPoint(x,y);
                                circlePoints.push(p/*normalProj.fromPixelToLatLng(p, zoom)*/);
                        }
                        circleLine2 = new GPolygon(circlePoints,borderColour,0, 0,fillColour,0.5);
                        this.map.addOverlay(circleLine2); 
                        }

    },
	'setMarkerOnMap' : function(lat, lng, options) {
		// set default values
		options = options || {};

		 //check icon, if set
		 if(options.icon != undefined) {
		 switch(options.icon.perspective)
		 {
		    case "country":
		    // Create our "tiny" marker icon
			var icon = new GIcon();
			icon.image = options.icon.image;
			icon.shadow = this.resourcePath + options.icon.shadow;
			icon.iconSize = options.icon.iconSize;
			icon.shadowSize = new GSize(25, 18);
			icon.iconAnchor = new GPoint(9, 9);
			icon.infoWindowAnchor = new GPoint(5, 1);

			var marker = new GMarker(new GLatLng(lat, lng), icon);
		    break;
		    default:
		    // Create our "tiny" marker icon
			var icon = new GIcon();
			icon.image = this.resourcePath + options.icon.image;
			icon.shadow = this.resourcePath + options.icon.shadow;
			icon.iconSize = options.icon.iconSize;
			icon.shadowSize = new GSize(25, 18);
			icon.iconAnchor = new GPoint(9, 9);
			icon.infoWindowAnchor = new GPoint(5, 1);

			var marker = new GMarker(new GLatLng(lat, lng), icon);
		    
		    break;
		 }
			
		 }
		 else
		 {
		 // Create our "tiny" marker icon
			var icon = new GIcon();
			icon.image = this.resourcePath + "images/house.png";
			icon.shadow = this.resourcePath + "images/house_shadow.png";
			icon.iconSize = new GSize(16, 16);
			icon.shadowSize = new GSize(25, 18);
			icon.iconAnchor = new GPoint(9, 9);
			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() {
				marker.openInfoWindowHtml('<b>' + name + '</b><br/>' + description);
			}.bind(this));
		}

		this.map.addOverlay(marker);
		return marker;
	},

	'addHostelMarker' : function(item) {
		var marker = this.setMarkerOnMap(item.lat, item.lng,{icon: {image:'images/house.png',shadow:'images/house_shadow.png',iconSize:new GSize(16, 16),perspective:'hostel'}});

		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: '150', maxHeight: '80'});
		}.bind(this));

		this.map.addOverlay(marker);
		return marker;
	},
	'addCityMarker' : function(item) {
		var marker = this.setMarkerOnMap(item.lat, item.lng,{icon: {image:'images/city4.gif',shadow:'images/city_shadow.png',iconSize:new GSize(19, 16),perspective:'city'}});
		GEvent.addListener(marker, 'mouseover',
			function() {
				marker.openToolTip(item.locationName, 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() {
			self.location.href=item.link;
		}.bind(this));

		this.map.addOverlay(marker);
		return marker;
	},
    'addCountryMarker' : function(item) {
		var marker = this.setMarkerOnMap(item.lat, item.lng,{icon: {image:item.imagePath,shadow:'images/flag_shadow.png',iconSize:new GSize(18, 18),perspective:'country'}});
		GEvent.addListener(marker, 'mouseover',
			function() {
				marker.openToolTip(item.countryName, this);
				this.map.addOverlay(marker.ToolTipInstance);
			}.bind(this)
		);
GEvent.addListener(marker, 'mouseout',
			function() {
				this.map.removeOverlay(marker.ToolTipInstance);
				marker.closeToolTip();
			}.bind(this)
		);
		GEvent.addListener(marker, 'click', function() {
			self.location.href=item.link;
		}.bind(this));

		this.map.addOverlay(marker);
		return marker;
	},
	'addContinentMarker' : function(item) {
		var marker = this.setMarkerOnMap(item.lat, item.lng,{icon: {image:'images/mm_20_green.png',shadow:'images/mm_20_shadow.png',iconSize:new GSize(12, 20),perspective:'continent'}});
		GEvent.addListener(marker, 'mouseover',
			function() {
				marker.openToolTip(item.continentName, this);
				this.map.addOverlay(marker.ToolTipInstance);
			}.bind(this)
		);
GEvent.addListener(marker, 'mouseout',
			function() {
				this.map.removeOverlay(marker.ToolTipInstance);
				marker.closeToolTip();
			}.bind(this)
		);
		GEvent.addListener(marker, 'click', function() {
			self.location.href=item.link;
		}.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 <= 1) {
			url = url + '&get=continents';
		}
	
		// handle the continent request
		if(zoomLevel > 1 && zoomLevel <= 4) {
			url = url + '&get=countries';
		}
	
		// handle the continent request
		if(zoomLevel > 4 && zoomLevel <= 9) {
			url = url + '&get=cities';
		}
	
		// 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'    : {},
				'cities'     : {},
				'countries'  : {},
				'continents'  : {}
				
			}
			this.map.clearOverlays();
		}

		// pois are JSON encoded arrays of points
		var elements = responseText.evalJSON();
		elements.each(function(item) {
		if(zoomLevel >= 0 && zoomLevel <= 1) {
			if(!this.points.continents[item.continentId]) {
					// put poi in currents level cache
					this.points.continents[item.continentId] = item;

					// add specific marker for a hostel
					this.addContinentMarker(item);
		
		}
	}
		// handle the continent request
		if(zoomLevel > 1 && zoomLevel <= 4) {
			if(!this.points.countries[item.countryId]) {
					// put poi in currents level cache
					this.points.countries[item.countryId] = item;

					// add specific marker for a hostel
					this.addCountryMarker(item);
		}
	}
		// handle the continent request
		if(zoomLevel > 4 && zoomLevel <= 9) {
			if(!this.points.cities[item.locationId]) {
					// put poi in currents level cache
					this.points.cities[item.locationId] = item;

					// add specific marker for a hostel
					this.addCityMarker(item);
		}
	}
		// handle the continent request
		if(zoomLevel > 9 && zoomLevel <= 17) {
			// 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) {

	}
}
