
function ClusterMarker($map, $options){
	this._map=$map;
	this._mapMarkers=[];
	this._iconBounds=[];
	this._clusterMarkers=[];
	this._clusteredMarkers=[];
	this._eventListeners=[];
	this._objGmap=null;
	
	if(typeof($options)==='undefined'){
		$options={};
	}
	this.borderPadding=($options.borderPadding)?$options.borderPadding:64;
	this.clusteringEnabled=($options.clusteringEnabled===false)?false:true;
	
	if($options.clusterMarkerClick){
		this.clusterMarkerClick=$options.clusterMarkerClick;
	}
	if($options.clusterMarkerIcon){
		this.clusterMarkerIcon=$options.clusterMarkerIcon;
	}else{
		this.clusterMarkerIcon=new GIcon();
		this.clusterMarkerIcon.image='/config_v3/imgs_defaut/punaise_G.png';
		this.clusterMarkerIcon.iconSize=new GSize(20, 31);
		this.clusterMarkerIcon.iconAnchor=new GPoint(9, 31);
		this.clusterMarkerIcon.infoWindowAnchor=new GPoint(9, 31);
		this.clusterMarkerIcon.shadow='http://labs.google.com/ridefinder/images/mm_20_shadow.png';
		this.clusterMarkerIcon.shadowSize=new GSize(20, 34);
	}
	this.clusterMarkerTitle=($options.clusterMarkerTitle)?$options.clusterMarkerTitle:'Cliquez ici pour voir les %count hébergements';
	if($options.fitMapMaxZoom){
		this.fitMapMaxZoom=$options.fitMapMaxZoom;
	}
	this.intersectPadding=($options.intersectPadding)?$options.intersectPadding:0;
	if($options.markers){
		this.addMarkers($options.markers);
	}
	//GEvent.bind(this._map, 'moveend', this, this._moveEnd);
	GEvent.bind(this._map, 'zoomend', this, this._zoomEnd);
	GEvent.bind(this._map, 'maptypechanged', this, this._mapTypeChanged);
}
ClusterMarker.prototype.setObjGmap=function($objGmap){
	this._objGmap=$objGmap;
}

ClusterMarker.prototype.cacheSideBar=function(){
	if (this._objGmap)this._objGmap.cacheSideBar();
}

ClusterMarker.prototype.addMarkers=function($markers){
	var i;
	if(!$markers[0]){
		//	assume $markers is an associative array and convert to a numerically indexed array
		var $numArray=[];
		for(i=1;i<$markers.length;i++){// in $markers){
			$numArray.push($markers[i]);
		}
		$markers=$numArray;
	}
	for(i=$markers.length-1; i>=0; i--){
		$markers[i]._isVisible=false;
		$markers[i]._isActive=false;
		$markers[i]._makeVisible=false;
	}
	this._mapMarkers=this._mapMarkers.concat($markers);
};

ClusterMarker.prototype._clusterMarker=function($clusterGroupIndexes,$id){
	function $newClusterMarker($location, $icon, $title, $nbMarkerInGrp, $nbHeber){
		
			var markerOpts = {};
			markerOpts = {
		 		"icon": $icon,
				"title": $title,
				"labelClass": "divGmapLabelNumGroupe",
 		 		"labelOffset": new GSize(3, -40)
		 	};
		 	var marker = new LabeledMarker($location, markerOpts);
			marker.labelText_=$nbHeber;
			return marker;
			//return new GMarker($location, {icon:$icon, title:$title});
	}
	var $clusterGroupBounds=new GLatLngBounds(), i, $clusterMarker, $clusteredMarkers=[], $marker, $this=this;
	var $nb=0,$strNb,$numNb;
	for(i=$clusterGroupIndexes.length-1; i>=0; --i){
		$marker=this._mapMarkers[$clusterGroupIndexes[i]];
		$marker.index=$clusterGroupIndexes[i];
		$clusterGroupBounds.extend($marker.getLatLng());
		$clusteredMarkers.push($marker);
		
		$strNb=$marker.labelText_;
		if(!$strNb)$numNb=1;
		else $numNb=parseInt($strNb);
		
		$nb=parseInt($nb+$numNb);
	}
	//this.clusteredMarkers[
	$clusterMarker=$newClusterMarker($clusterGroupBounds.getCenter(), this.clusterMarkerIcon, this.clusterMarkerTitle.replace(/%count/gi, $clusterGroupIndexes.length),$clusterGroupIndexes.length,$nb);
	$clusterMarker.clusterGroupBounds=$clusterGroupBounds;	//	only req'd for default cluster marker click action
	this._eventListeners.push(GEvent.addListener($clusterMarker, 'click', function(){
		$this.clusterMarkerClick({clusterMarker:$clusterMarker, clusteredMarkers:$clusteredMarkers, id:$id });
	}));
	//return $clusterMarker;
	return {clusterMarker:$clusterMarker, clusteredMarkers:$clusteredMarkers }
};

ClusterMarker.prototype.clusterMarkerClick=function($args){
	this.cacheSideBar();
	var nb=$args.clusteredMarkers.length;
	var html_clic="<div><p><div id='motcle1'>Cet icone est un regroupement de </div><strong>"+nb+"</strong><div id='motcle2'> icones </div><br />"
	html_clic+="<div id='motcle3'>Soit : </div><strong>"+$args.clusterMarker.labelText_+"</strong><div id='motcle4'> hébergements</div><br />";
	html_clic+="<div id='motcle5'>Vous pouvez : </div><ul>";
	html_clic+="<li><a href=#zoom onclick='gmap.zoomOnClusterById("+$args.id+");'><div id='motcle6'>Zoomer</div></a></li>";
	html_clic+="<li><a href=#eclat onclick='gmap.eclateClusterById("+$args.id+");'><div id='motcle7'>Eclater</div></a></li>";
	html_clic+="<li><a href=#visu onclick='gmap.visualiserHebergOnClusterById("+$args.id+");'><div id='motcle8'>Visualiser</div></a></li>";
	
	html_clic+="</div>";

	chargeMotCleDsDiv('motcle1','LBL_ICONE_REGROUP');
	chargeMotCleDsDiv('motcle2','LBL_ICONE');
	chargeMotCleDsDiv('motcle3','LBL_SOIT');
	chargeMotCleDsDiv('motcle4','LBL_HEBERGEMENTS');
	chargeMotCleDsDiv('motcle5','LBL_VOUSPOUVEZ');
	chargeMotCleDsDiv('motcle6','LBL_ZOOMER');
	chargeMotCleDsDiv('motcle7','LBL_ECLATER');
	chargeMotCleDsDiv('motcle8','LBL_VISUALISER');
	
	//this._map.enableInfoWindow();
	$args.clusterMarker.openInfoWindowHtml(html_clic);
	//this._map.openInfoWindowHtml($args.clusterMarker.getLatLng(),html_clic);
	//this._map.setCenter($args.clusterMarker.getLatLng(), this._map.getBoundsZoomLevel($args.clusterMarker.clusterGroupBounds));
	
	//this._map.removeOverlay($args.clusterMarker);
	//for (var i=0;i<$args.clusteredMarkers.length;i++){
	//	$args.clusteredMarkers[i]._isVisible=true;
	///	this._map.addOverlay($args.clusteredMarkers[i]);
	//}

};
ClusterMarker.prototype.clusterMarkerClick2=function($args){
	this.cacheSideBar();
	var nb=$args.clusteredMarkers.length;
	var html_clic_all="<div><p><div id='motcle1'>Cet icone est un regroupement de </div><strong>"+nb+"</strong><div id='motcle2'> icones </div><a href=#zoom onclick='gmap.zoomOnClusterById("+$args.id+");'><div id='motcle6'>Zoomer</div></a>"

	

	chargeMotCleDsDiv('motcle1','LBL_ICONE_REGROUP');
	chargeMotCleDsDiv('motcle2','LBL_ICONE');
	chargeMotCleDsDiv('motcle3','LBL_SOIT');
	chargeMotCleDsDiv('motcle4','LBL_HEBERGEMENTS');
	chargeMotCleDsDiv('motcle5','LBL_VOUSPOUVEZ');
	chargeMotCleDsDiv('motcle6','LBL_ZOOMER');
	chargeMotCleDsDiv('motcle7','LBL_ECLATER');
	chargeMotCleDsDiv('motcle8','LBL_VISUALISER');
	
	//this._map.enableInfoWindow();
	$args.clusterMarker.openInfoWindowHtml(html_clic);
	//this._map.openInfoWindowHtml($args.clusterMarker.getLatLng(),html_clic);
	//this._map.setCenter($args.clusterMarker.getLatLng(), this._map.getBoundsZoomLevel($args.clusterMarker.clusterGroupBounds));
	
	//this._map.removeOverlay($args.clusterMarker);
	//for (var i=0;i<$args.clusteredMarkers.length;i++){
	//	$args.clusteredMarkers[i]._isVisible=true;
	///	this._map.addOverlay($args.clusteredMarkers[i]);
	//}

};

ClusterMarker.prototype.unclusterCluster=function($id){
	var $clusterMarker=this._clusterMarkers[$id],$clusteredMarkers=this._clusteredMarkers[$id];
	
	this._map.removeOverlay($clusterMarker);
	for (var i=0;i<$clusteredMarkers.length;i++){
		$clusteredMarkers[i]._isVisible=true;
		this._map.addOverlay($clusteredMarkers[i]);
	}
}
ClusterMarker.prototype.zoomOnCluster=function($id){
	var $clusterMarker=this._clusterMarkers[$id];
	this._map.setCenter($clusterMarker.getLatLng(), this._map.getBoundsZoomLevel($clusterMarker.clusterGroupBounds));
	
}
ClusterMarker.prototype.visuDetailsSideBar=function($id){
	var $clusterMarker=this._clusterMarkers[$id],$clusteredMarkers=this._clusteredMarkers[$id];
	var tabHtml=new Array();
	for (var i=0;i<$clusteredMarkers.length;i++){
		for (var j=0;j<$clusteredMarkers[i].tabHtml.length;j++)
			tabHtml.push($clusteredMarkers[i].tabHtml[j]);
		
	}
	if (this._objGmap)this._objGmap.affSideBar_v2ByTabHtml(tabHtml,250);
	
}


ClusterMarker.prototype.clusterMarkerOnMouseOver=function($args){
	for (var i=0;i<$args.clusteredMarkers.length;i++){
		$args.clusteredMarkers[i]._isVisible=true;
		this._map.addOverlay($args.clusteredMarkers[i]);
	}

};
ClusterMarker.prototype.clusterMarkerOnMouseOut=function($args){
	for (var i=0;i<$args.clusteredMarkers.length;i++){
		$args.clusteredMarkers[i]._isVisible=false;
		this._map.removeOverlay($args.clusteredMarkers[i]);
	}

};

ClusterMarker.prototype._filterActiveMapMarkers=function(){
	var $borderPadding=this.borderPadding, $mapZoomLevel=this._map.getZoom(), $mapProjection=this._map.getCurrentMapType().getProjection(), $mapPointSw, $activeAreaPointSw, $activeAreaLatLngSw, $mapPointNe, $activeAreaPointNe, $activeAreaLatLngNe, $activeAreaBounds=this._map.getBounds(), i, $marker, $uncachedIconBoundsIndexes=[], $oldState;
	if($borderPadding){
		$mapPointSw=$mapProjection.fromLatLngToPixel($activeAreaBounds.getSouthWest(), $mapZoomLevel);
		$activeAreaPointSw=new GPoint($mapPointSw.x-$borderPadding, $mapPointSw.y+$borderPadding);
		$activeAreaLatLngSw=$mapProjection.fromPixelToLatLng($activeAreaPointSw, $mapZoomLevel);
		$mapPointNe=$mapProjection.fromLatLngToPixel($activeAreaBounds.getNorthEast(), $mapZoomLevel);
		$activeAreaPointNe=new GPoint($mapPointNe.x+$borderPadding, $mapPointNe.y-$borderPadding);
		$activeAreaLatLngNe=$mapProjection.fromPixelToLatLng($activeAreaPointNe, $mapZoomLevel);
		$activeAreaBounds.extend($activeAreaLatLngSw);
		$activeAreaBounds.extend($activeAreaLatLngNe);
	}
	this._activeMarkersChanged=false;
	if(typeof(this._iconBounds[$mapZoomLevel])==='undefined'){
		//	no iconBounds cached for this zoom level
		//	no need to check for existence of individual iconBounds elements
		this._iconBounds[$mapZoomLevel]=[];
		this._activeMarkersChanged=true;	//	force refresh(true) as zoomed to uncached zoom level
		for(i=this._mapMarkers.length-1; i>=0; --i){
			$marker=this._mapMarkers[i];
			$marker._isActive=$activeAreaBounds.containsLatLng($marker.getLatLng())?true:false;
			$marker._makeVisible=$marker._isActive;
			if($marker._isActive){
				$uncachedIconBoundsIndexes.push(i);
			}
		}
	}else{
		//	icondBounds array exists for this zoom level
		//	check for existence of individual iconBounds elements
		for(i=this._mapMarkers.length-1; i>=0; i--){
			$marker=this._mapMarkers[i];
			$oldState=$marker._isActive;
			$marker._isActive=$activeAreaBounds.containsLatLng($marker.getLatLng())?true:false;
			$marker._makeVisible=$marker._isActive;
			if(!this._activeMarkersChanged && $oldState!==$marker._isActive){
				this._activeMarkersChanged=true;
			}
			if($marker._isActive && typeof(this._iconBounds[$mapZoomLevel][i])==='undefined'){
				$uncachedIconBoundsIndexes.push(i);
			}
		}
	}
	return $uncachedIconBoundsIndexes;
};

ClusterMarker.prototype._filterIntersectingMapMarkers=function(){
	var $clusterGroup, i, j, $mapZoomLevel=this._map.getZoom(),$cluster,$id;
	for(i=this._mapMarkers.length-1; i>0; i--)
	{
		if(this._mapMarkers[i]._makeVisible){
			$nb=0;
			$clusterGroup=[];
			for(j=i-1; j>=0; j--){
				if(this._mapMarkers[j]._makeVisible && this._iconBounds[$mapZoomLevel][i].intersects(this._iconBounds[$mapZoomLevel][j])){
					$clusterGroup.push(j);
				}
			}
			if($clusterGroup.length!==0){
				$clusterGroup.push(i);
				for(j=$clusterGroup.length-1; j>=0; j--){
					this._mapMarkers[$clusterGroup[j]]._makeVisible=false;
				}
				$id=this._clusterMarkers.length;
				$cluster=this._clusterMarker($clusterGroup,$id);
				
				this._clusterMarkers.push($cluster.clusterMarker);
				this._clusteredMarkers.push($cluster.clusteredMarkers);
			}
		}
	}
};

ClusterMarker.prototype.fitMapToMarkers=function(){
	var $markers=this._mapMarkers, $markersBounds=new GLatLngBounds(), i;
	for(i=$markers.length-1; i>=0; i--){
		$markersBounds.extend($markers[i].getLatLng());
	}
	var $fitMapToMarkersZoom=this._map.getBoundsZoomLevel($markersBounds);
		
	if(this.fitMapMaxZoom && $fitMapToMarkersZoom>this.fitMapMaxZoom){
		$fitMapToMarkersZoom=this.fitMapMaxZoom;
	}
	this._map.setCenter($markersBounds.getCenter(), $fitMapToMarkersZoom);
	this.refresh();
};

ClusterMarker.prototype._mapTypeChanged=function(){
	this.refresh(true);
};

ClusterMarker.prototype._moveEnd=function(){
	if(!this._cancelMoveEnd){
		this.refresh();
	}else{
		this._cancelMoveEnd=false;
	}
};

ClusterMarker.prototype._preCacheIconBounds=function($indexes){
	var $mapProjection=this._map.getCurrentMapType().getProjection(), $mapZoomLevel=this._map.getZoom(), i, $marker, $iconSize, $iconAnchorPoint, $iconAnchorPointOffset, $iconBoundsPointSw, $iconBoundsPointNe, $iconBoundsLatLngSw, $iconBoundsLatLngNe, $intersectPadding=this.intersectPadding;
	for(i=$indexes.length-1; i>=0; i--){
		$marker=this._mapMarkers[$indexes[i]];
		$iconSize=$marker.getIcon().iconSize;
		$iconAnchorPoint=$mapProjection.fromLatLngToPixel($marker.getLatLng(), $mapZoomLevel);
		$iconAnchorPointOffset=$marker.getIcon().iconAnchor;
		$iconBoundsPointSw=new GPoint($iconAnchorPoint.x-$iconAnchorPointOffset.x-$intersectPadding, $iconAnchorPoint.y-$iconAnchorPointOffset.y+$iconSize.height+$intersectPadding);
		$iconBoundsPointNe=new GPoint($iconAnchorPoint.x-$iconAnchorPointOffset.x+$iconSize.width+$intersectPadding, $iconAnchorPoint.y-$iconAnchorPointOffset.y-$intersectPadding);
		$iconBoundsLatLngSw=$mapProjection.fromPixelToLatLng($iconBoundsPointSw, $mapZoomLevel);
		$iconBoundsLatLngNe=$mapProjection.fromPixelToLatLng($iconBoundsPointNe, $mapZoomLevel);
		this._iconBounds[$mapZoomLevel][$indexes[i]]=new GLatLngBounds($iconBoundsLatLngSw, $iconBoundsLatLngNe);
	}
};

ClusterMarker.prototype.refresh=function($forceFullRefresh){
	var i,$marker, $uncachedIconBoundsIndexes=this._filterActiveMapMarkers();
	if(this._activeMarkersChanged || $forceFullRefresh){
		this._removeClusterMarkers();
		if(this.clusteringEnabled && this._map.getZoom()<this._map.getCurrentMapType().getMaximumResolution()){
			if($uncachedIconBoundsIndexes.length>0){
				this._preCacheIconBounds($uncachedIconBoundsIndexes);
			}
			this._filterIntersectingMapMarkers();
		}
		for(i=this._clusterMarkers.length-1; i>=0; i--){
			$marker=this._clusterMarkers[i];
			this._map.addOverlay($marker);
		}

		for(i=this._mapMarkers.length-1; i>=0; i--){
			$marker=this._mapMarkers[i];
			if(!$marker._isVisible && $marker._makeVisible){
				this._map.addOverlay($marker);
				$marker._isVisible=true;
			}
			if($marker._isVisible && !$marker._makeVisible){
				this._map.removeOverlay($marker);
				$marker._isVisible=false;
			}
		}
	}
};

ClusterMarker.prototype._removeClusterMarkers=function(){
	for(var i=this._clusterMarkers.length-1; i>=0; i--){
		this._map.removeOverlay(this._clusterMarkers[i]);
	}
	for(i=this._eventListeners.length-1; i>=0; i--){
		GEvent.removeListener(this._eventListeners[i]);
	}
	this._clusterMarkers=[];
	this._clusteredMarkers=[];
	this._eventListeners=[];
};

ClusterMarker.prototype.removeMarkers=function(){
	for(var i=this._mapMarkers.length-1; i>=0; i--){
		if(this._mapMarkers[i]. _isVisible){
			this._map.removeOverlay(this._mapMarkers[i]);
		}
		delete this._mapMarkers[i]._isVisible;
		delete this._mapMarkers[i]._isActive;
		delete this._mapMarkers[i]._makeVisible;
	}
	this._removeClusterMarkers();
	this._mapMarkers=[];
	this._iconBounds=[];
};


ClusterMarker.prototype.triggerClick=function($index){
	var $marker=this._mapMarkers[$index];
	if($marker._isVisible){
		//	$marker is visible
		GEvent.trigger($marker, 'click');
	}
	else if($marker._isActive){
		//	$marker is clustered
		this._map.setCenter($marker.getLatLng());
		this._map.zoomIn();
		this.triggerClick($index);
	}else{
		// $marker is not within active area (map bounds + border padding)
		this._map.setCenter($marker.getLatLng());
		this.triggerClick($index);
	}
};

ClusterMarker.prototype._zoomEnd=function(){
	this._cancelMoveEnd=true;
	this.refresh(true);
};
