Clusterer = function(map)
{
	this.map = map;
	this.markers = [];
	this.clusters = [];
	this.clustersUmsaetze = [];
	this.timeout = null;
	this.currentZoomLevel = map.getZoom();
	this.maxVisibleMarkers = Clusterer.defaultMaxVisibleMarkers;
	this.gridSize = Clusterer.defaultGridSize;
	this.minMarkersPerCluster = Clusterer.defaultMinMarkersPerCluster;
	this.maxLinesPerInfoBox = Clusterer.defaultMaxLinesPerInfoBox;
	this.icon = Clusterer.defaultIcon;
	GEvent.addListener(map, 'zoomend', Clusterer.MakeCaller(Clusterer.Display, this));
	GEvent.addListener(map, 'moveend', Clusterer.MakeCaller(Clusterer.Display, this));
	GEvent.addListener(map, 'infowindowclose', Clusterer.MakeCaller(Clusterer.PopDown, this));
};

Clusterer.defaultMaxVisibleMarkers = 200;
Clusterer.defaultGridSize = 8;
Clusterer.defaultMinMarkersPerCluster = 1;
Clusterer.defaultMaxLinesPerInfoBox = 10;
Clusterer.defaultIcon = new GIcon();
Clusterer.defaultIcon.image = '{{MAP_SYM}}medium_system.png';
Clusterer.defaultIcon.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
Clusterer.defaultIcon.iconSize = new GSize(30, 51);
Clusterer.defaultIcon.shadowSize = new GSize(56, 51);
Clusterer.defaultIcon.iconAnchor = new GPoint(13, 34);
Clusterer.defaultIcon.infoWindowAnchor = new GPoint(13, 3);
Clusterer.defaultIcon.infoShadowAnchor = new GPoint(27, 37);

Clusterer.prototype.SetIcon = function(icon)
{
	this.icon = icon;
};

Clusterer.prototype.SetMaxVisibleMarkers = function(n)
{
	this.maxVisibleMarkers = n;
};

Clusterer.prototype.SetMinMarkersPerCluster = function(n)
{
	this.minMarkersPerCluster = n;
};

Clusterer.prototype.SetMaxLinesPerInfoBox = function(n)
{
	this.maxLinesPerInfoBox = n;
};

Clusterer.prototype.AddMarker = function(marker, title)
{
	if (marker.setMap != null) marker.setMap(this.map);
	marker.title = title;
	marker.onMap = false;
	this.markers.push(marker);
	this.DisplayLater();
};

Clusterer.prototype.RemoveMarker = function(marker)
{
	for (var i = 0; i < this.markers.length; ++i)
		if (this.markers[i] == marker)
		{
			if (marker.onMap) this.map.removeOverlay(marker);
			for (var j = 0; j < this.clusters.length; ++j)
			{
				var cluster = this.clusters[j];
				if (cluster != null)
				{
					for (var k = 0; k < cluster.markers.length; ++k)
						if (cluster.markers[k] == marker)
						{
							cluster.markers[k] = null;
							--cluster.markerCount;
							break;
						}
						if (cluster.markerCount == 0)
						{
							this.ClearCluster(cluster);
							this.clusters[j] = null;
						}
						else if (cluster == this.poppedUpCluster) Clusterer.RePop(this);
				}
			}
			this.markers[i] = null;
			break;
		}
		
	this.DisplayLater();
};

Clusterer.prototype.DisplayLater = function()
{
	if (this.timeout != null) clearTimeout(this.timeout);
	this.timeout = setTimeout(Clusterer.MakeCaller(Clusterer.Display, this), 50);
};

Clusterer.Display = function(clusterer)
{
	var i, j, marker, cluster;
	clearTimeout(clusterer.timeout);
	var newZoomLevel = clusterer.map.getZoom();
	if (newZoomLevel != clusterer.currentZoomLevel)
	{
		for (i = 0; i < clusterer.clusters.length; ++i)
			if (clusterer.clusters[i] != null)
			{
				clusterer.ClearCluster(clusterer.clusters[i]);
				clusterer.clusters[i] = null;
			}
		clusterer.clusters.length = 0;
		clusterer.currentZoomLevel = newZoomLevel;
	}
	var bounds = clusterer.map.getBounds();
	var sw = bounds.getSouthWest();
	var ne = bounds.getNorthEast();
	var dx = ne.lng() - sw.lng();
	var dy = ne.lat() - sw.lat();
	if (dx < 300 && dy < 150)
	{
		dx *= 0.10;
		dy *= 0.10;
		bounds = new GLatLngBounds(new GLatLng(sw.lat() - dy, sw.lng() - dx), new GLatLng(ne.lat() + dy, ne.lng() + dx));
	}
	var visibleMarkers = [];
	var nonvisibleMarkers = [];
	for (i = 0; i < clusterer.markers.length; ++i)
	{
		marker = clusterer.markers[i];
		if (marker != null)
			if (bounds.contains(marker.getPoint())) visibleMarkers.push(marker);
			else nonvisibleMarkers.push(marker);
	}
	for (i = 0; i < nonvisibleMarkers.length; ++i)
	{
		marker = nonvisibleMarkers[i];
		if (marker.onMap)
		{
			clusterer.map.removeOverlay(marker);
			marker.onMap = false;
		}
	}
	for (i = 0; i < clusterer.clusters.length; ++i)
	{
		cluster = clusterer.clusters[i];
		if (cluster != null && !bounds.contains(cluster.marker.getPoint()) && cluster.onMap)
		{
			clusterer.map.removeOverlay(cluster.marker);
			cluster.onMap = false;
		}
	}
	if (visibleMarkers.length > clusterer.maxVisibleMarkers)
	{
		var latRange = bounds.getNorthEast().lat() - bounds.getSouthWest().lat();
		var latInc = latRange / clusterer.gridSize;
		var lngInc = latInc / Math.cos((bounds.getNorthEast().lat() + bounds.getSouthWest().lat()) / 2.0 * Math.PI / 180.0);
		for (var lat = bounds.getSouthWest().lat(); lat <= bounds.getNorthEast().lat(); lat += latInc)
			for (var lng = bounds.getSouthWest().lng(); lng <= bounds.getNorthEast().lng(); lng += lngInc)
			{
				cluster = new Object();
				cluster.clusterer = clusterer;
				cluster.bounds = new GLatLngBounds(new GLatLng(lat, lng), new GLatLng(lat + latInc, lng + lngInc));
				cluster.markers = [];
				cluster.markerCount = 0;
				cluster.onMap = false;
				cluster.marker = null;
				clusterer.clusters.push(cluster);
			}
		for (i = 0; i < visibleMarkers.length; ++i)
		{
			marker = visibleMarkers[i];
			if (marker != null && !marker.inCluster)
			{
				for (j = 0; j < clusterer.clusters.length; ++j)
				{
					cluster = clusterer.clusters[j];
					if (cluster != null && cluster.bounds.contains(marker.getPoint()))
					{
						cluster.markers.push(marker);
						++cluster.markerCount;marker.inCluster = true;
					}
				}
			}
		}
		for (i = 0; i < clusterer.clusters.length; ++i)
			if (clusterer.clusters[i] != null && clusterer.clusters[i].markerCount < clusterer.minMarkersPerCluster)
			{
				clusterer.ClearCluster(clusterer.clusters[i]);
				clusterer.clusters[i] = null;
			}
		for (i = clusterer.clusters.length - 1; i >= 0; --i)
			if (clusterer.clusters[i] != null) break;
			else --clusterer.clusters.length;
		for (i = 0; i < clusterer.clusters.length; ++i)
		{
			cluster = clusterer.clusters[i];
			if (cluster != null)
			{
				for (j = 0; j < cluster.markers.length; ++j)
				{
					marker = cluster.markers[j];
					if (marker != null && marker.onMap)
					{
						clusterer.map.removeOverlay(marker);
						marker.onMap = false;
					}
				}
			}
		}
                // 
                MAP_SUMME2=3000;
                MAP_SUMME3=6000;
                MAP_SUMME4=10000;
                //
                var m_anzahl = 0, m_umsatz=0.0,max_umsatz=0.0;
                //
		for (i = 0; i < clusterer.clusters.length; ++i)
		{
			cluster = clusterer.clusters[i];
			if (cluster != null && cluster.marker == null)
			{
				var umsatzSumme = 0.0;
				
				for (j = 0; j < cluster.markers.length; ++j)
				{
					marker = cluster.markers[j];
					if (marker != null)
					{
						xTotal += (+marker.getPoint().lng());
						yTotal += (+marker.getPoint().lat());
						umsatzSumme += marker.umsatz;
					}
				}
                                m_anzahl++;
                                m_umsatz+=umsatzSumme;
                                if (umsatzSumme>max_umsatz) max_umsatz=umsatzSumme;
			}
		}
                //
                //
                if (m_anzahl==0) m_anzahl=0.0001;
                MAP_SUMME2=(m_umsatz/m_anzahl)*0.75;
                MAP_SUMME3=(m_umsatz/m_anzahl)*2;
                MAP_SUMME4=max_umsatz*0.75; 

//alert("Anzahl"+m_anzahl+" gumsatz "+m_umsatz+" max "+max_umsatz+"\n2: "+MAP_SUMME2+" 3: "+MAP_SUMME3+" 4: "+MAP_SUMME4);

 
                //
                //
		for (i = 0; i < clusterer.clusters.length; ++i)
		{
			cluster = clusterer.clusters[i];
			if (cluster != null && cluster.marker == null)
			{
				var xTotal = 0.0, yTotal = 0.0, umsatzSumme = 0.0;
				
				for (j = 0; j < cluster.markers.length; ++j)
				{
					marker = cluster.markers[j];
					if (marker != null)
					{
						xTotal += (+marker.getPoint().lng());
						yTotal += (+marker.getPoint().lat());
						umsatzSumme += marker.umsatz;
					}
				}

				var location = new GLatLng(yTotal / cluster.markerCount, xTotal / cluster.markerCount);
				marker = new GMarker(location, {icon:selectIcon(umsatzSumme)});
				cluster.marker = marker;
				GEvent.addListener(marker, 'click', Clusterer.MakeCaller(Clusterer.PopUp, cluster));
			}
		}
	}
	
	for (i = 0; i < visibleMarkers.length; ++i)
	{
		marker = visibleMarkers[i];
		if (marker != null && !marker.onMap && !marker.inCluster)
		{
			clusterer.map.addOverlay(marker);
			if (marker.addedToMap != null) marker.addedToMap();
			marker.onMap = true;
		}
	}
	
	for (i = 0; i < clusterer.clusters.length; ++i)
	{
		cluster = clusterer.clusters[i];
		if (cluster != null && !cluster.onMap && bounds.contains(cluster.marker.getPoint()))
		{
			clusterer.map.addOverlay(cluster.marker);
			cluster.onMap = true;
		}
	}
	
	Clusterer.RePop(Clusterer);
};

Clusterer.PopUp = function(cluster)
{
	var clusterer = cluster.clusterer;
	var html = '<table width="300">';
	var n = 0;
	
	for (var i = 0; i < cluster.markers.length; ++i)
	{
		var marker = cluster.markers[i];
		if (marker != null)
		{
			/* Geodaten des aktuellen Markers
			var lng = marker.getLatLng().lng();
			var lat = marker.getLatLng().lat();
			*/
			
			++n;
			
			// Beginn Zeile
			html += '<tr><td>';
			if (marker.getIcon().smallImage != null)
				html += '<img src="'+ marker.getIcon().smallImage +'">';
			else
				html += '<img src="'+ marker.getIcon().image +'" width="'+ (marker.getIcon().iconSize.width / 2) +'" height="'+ (marker.getIcon().iconSize.height / 2)+ '">';

                        if (display_type==0)
                        {
        		 html += '</td><td>'+ marker.title +'</td></tr>';
                        }
                        if (display_type==1)
                        {
        		 html += '</td><td>'+ marker.title +'<br /><small>'+ marker.umsatz +' EUR</small></td></tr>';
                        }
                        if (display_type==2)
                        {
        		 html += '</td><td>'+ marker.title +'<br /><small>'+ marker.umsatz +' Kontakte</small></td></tr>';
                        }
                        if (display_type==3)
                        {
        		 html += '</td><td>'+ marker.title +'</td></tr>';
                        }

			// Ende Zeile
			
			if (n == clusterer.maxLinesPerInfoBox - 1 && cluster.markerCount > clusterer.maxLinesPerInfoBox)
			{
				html += '<tr><td colspan="2">...und '+ (cluster.markerCount - n) +' weitere</td></tr>';
				break;
			}
		}
	}
	
	html += '</table>';
	clusterer.map.closeInfoWindow();
	cluster.marker.openInfoWindowHtml(html);
	clusterer.poppedUpCluster = cluster;
};

Clusterer.RePop = function(clusterer)
{
	if (clusterer.poppedUpCluster != null) clusterer.PopUp(clusterer.poppedUpCluster);
};

Clusterer.PopDown = function(clusterer)
{
	clusterer.poppedUpCluster = null;
};

Clusterer.prototype.ClearCluster = function(cluster)
{
	var i, marker;
	for (i = 0; i < cluster.markers.length; ++i)
		if (cluster.markers[i] != null)
		{
			cluster.markers[i].inCluster = false;
			cluster.markers[i] = null;
		}
	cluster.markers.length = 0;
	cluster.markerCount = 0;
	
	if (cluster == this.poppedUpCluster) this.map.closeInfoWindow();
	
	if (cluster.onMap)
	{
		this.map.removeOverlay(cluster.marker);
		cluster.onMap = false;
	}
};

Clusterer.MakeCaller = function(func,arg)
{
	return function() {
		func(arg);
	};
};

GMarker.prototype.setMap = function(map)
{
	this.map = map;
};

GMarker.prototype.addedToMap = function()
{
	this.map = null;
};

GMarker.prototype.origOpenInfoWindow = GMarker.prototype.openInfoWindow;

GMarker.prototype.openInfoWindow = function(node, opts)
{
	if (this.map != null) return this.map.openInfoWindow(this.getPoint(), node,opts);
	else return this.origOpenInfoWindow(node, opts);
};

GMarker.prototype.origOpenInfoWindowHtml = GMarker.prototype.openInfoWindowHtml;

GMarker.prototype.openInfoWindowHtml = function(html, opts)
{
	if (this.map != null) return this.map.openInfoWindowHtml(this.getPoint(), html, opts);
	else return this.origOpenInfoWindowHtml(html, opts);
};

GMarker.prototype.origOpenInfoWindowTabs = GMarker.prototype.openInfoWindowTabs;

GMarker.prototype.openInfoWindowTabs = function(tabNodes, opts)
{
	if (this.map != null) return this.map.openInfoWindowTabs(this.getPoint(), tabNodes, opts);
	else return this.origOpenInfoWindowTabs(tabNodes, opts);
};

GMarker.prototype.origOpenInfoWindowTabsHtml = GMarker.prototype.openInfoWindowTabsHtml;

GMarker.prototype.openInfoWindowTabsHtml = function(tabHtmls, opts)
{
	if (this.map != null) return this.map.openInfoWindowTabsHtml(this.getPoint(), tabHtmls, opts);
	else return this.origOpenInfoWindowTabsHtml(tabHtmls, opts);
};

GMarker.prototype.origShowMapBlowup = GMarker.prototype.showMapBlowup;

GMarker.prototype.showMapBlowup = function(opts)
{
	if (this.map != null) return this.map.showMapBlowup(this.getPoint(), opts);
	else return this.origShowMapBlowup(opts);
};

// Umsatz für Marker mitspeichern
GMarker.prototype.setUmsatz = function(umsatz)
{
	this.umsatz = umsatz;
};

// Angezeigtes Icon anhand der Umsatzsumme wählen
function selectIcon(umsatzSumme)
{
 if (umsatzSumme > MAP_SUMME4)
 {
  return iccluster4;
 }
 else if (umsatzSumme > MAP_SUMME3)
 {
  return iccluster3;
 }
 else if (umsatzSumme > MAP_SUMME2)
 {
  return iccluster2;
 }
 else return iccluster1;
}
