/* Clusterer */
	function FlexiCluster(map, pMarkers) {
		this.extend(FlexiCluster, google.maps.OverlayView);
		
		this.ready_ = false;
		this.map_ = map;
		//this.zoom_ = this.map_.getZoom();
		this.marker_ = []
		
		this.cluster = [];
		this.classname = null;
		this.icon = null;
		this.gridSize = 52;
	
		this.setMap(map);
		
		/*
		google.maps.event.addListener(this.map_, 'zoom_changed', function() {
			var maxZoom = reference.map_.mapTypes[reference.map_.getMapTypeId()].maxZoom;
	  		var zoom = reference.map_.getZoom();
	  		
			if(zoom < 0 || zoom > maxZoom) {
				return;
			}
	
			if(reference.zoom_ != zoom) {
				reference.zoom_ = reference.map_.getZoom();
				reference.resetViewport();
			}
		});
		*/
		
		var reference = this;
		google.maps.event.addListener(this.map_, 'bounds_changed', function() {
			reference.redraw();
		});
	
		if(pMarkers && pMarkers.length) {
			for(var i = 0, marker; marker = pMarkers[i]; i++) {
				this.addMarker(marker, false);
			}
		}
	}
	
	FlexiCluster.prototype.extend = function(obj1, obj2) {
		return (function(object) {
			for (property in object.prototype) {
				this.prototype[property] = object.prototype[property];
			}
			
			return this;
		}).apply(obj1, [obj2]);
	};
	
	FlexiCluster.prototype.onAdd = function() {
		this.ready_ = true;
		this.redraw();
	};
	
	FlexiCluster.prototype.setMap = function(map) {
	  this.map_ = map;
	};
	
	FlexiCluster.prototype.getExtendedBounds = function(bounds) {
	  var projection = this.getProjection();
	
	  // Turn the bounds into latlng.
	  var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), bounds.getNorthEast().lng());
	  var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), bounds.getSouthWest().lng());
	
	  // Convert the points to pixels and the extend out by the grid size.
	  var trPix = projection.fromLatLngToDivPixel(tr);
	  trPix.x += this.gridSize;
	  trPix.y -= this.gridSize;
	
	  var blPix = projection.fromLatLngToDivPixel(bl);
	  blPix.x -= this.gridSize;
	  blPix.y += this.gridSize;
	
	  // Convert the pixel points back to LatLng
	  var ne = projection.fromDivPixelToLatLng(trPix);
	  var sw = projection.fromDivPixelToLatLng(blPix);
	
	  // Extend the bounds to contain the new bounds.
	  bounds.extend(ne);
	  bounds.extend(sw);
	
	  return bounds;
	};
	
	FlexiCluster.prototype.addMarker = function(marker, pDraw) {
		marker.setVisible(false);
		marker.setMap(null);
		marker.isAdded = false;
		
		this.marker_.push(marker);
		
		if(pDraw == true) {
			this.redraw();
		}
	};
	
	FlexiCluster.prototype.createCluster = function() {
		this.cluster = [];
		var bounds = this.getExtendedBounds(new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), this.map_.getBounds().getNorthEast()));
		
		for(var i = 0, marker; marker = this.marker_[i]; i++) {
			var added = false;
			if(!marker.isAdded && bounds.contains(marker.getPosition())) {
				for(var j = 0, cluster; cluster = this.cluster[j]; j++) {
					if(!added && cluster.center && cluster.bounds.contains(marker.getPosition())) {
						added = true;
						cluster.marker.push(marker);
						break;
					}
				}
	
				if(!added) {
					this.cluster.push(new Cluster(this, marker));
				}
			}
		}
		
		for(var i = 0, cluster; cluster = this.cluster[i]; i++) {
			cluster.redraw();
		}
	}
	
	FlexiCluster.prototype.draw = function() {};
	
	FlexiCluster.prototype.redraw = function() {
		if(!this.ready_) {
			return;
		}
		
		this.getPanes().floatPane.innerHTML = '';
		
		for (var i = 0, marker; marker = this.marker_[i]; i++) {
			marker.isAdded = false;
		}
	  
		for(var i = 0, cluster; cluster = this.cluster[i]; i++) {
			cluster.remove();
		}
	
		this.cluster = [];
		
		this.createCluster();
		this.onDraw();
	};
	
	FlexiCluster.prototype.resetViewport = function() {};
	
	// events
		FlexiCluster.prototype.onClick = function(cluster) {};
		FlexiCluster.prototype.onDraw = function() {};
		FlexiCluster.prototype.onDrawCluster = function(cluster) {};

/* Cluster */
	function Cluster(clusterer, marker) {
		this.clusterer_ = clusterer;
		this.marker_ = null;
		this.div_ = null;
		this.center = marker.getPosition();
		this.bounds = this.clusterer_.getExtendedBounds(new google.maps.LatLngBounds(marker.getPosition(), marker.getPosition()));
		this.marker = [];
		
		this.marker.push(marker);
	}
	
	Cluster.prototype.remove = function() {
		this.marker_.setMap(null);
		this.marker_.setVisible(false);
		//delete this;
	}
	
	Cluster.prototype.redraw = function() {
		this.bounds = new google.maps.LatLngBounds(this.marker[0].getPosition(), this.marker[0].getPosition());

		for(var i = 1, marker; marker = this.marker[i]; i++) {
			this.bounds.extend(marker.getPosition());
		}
		
		this.center = this.bounds.getCenter();
		
		this.marker_ = new google.maps.Marker({ position: this.center });
		this.marker_.setMap(this.clusterer_.map_);
		this.marker_.setVisible(true);
		if(this.clusterer_.icon != null) {
			this.marker_.setIcon(this.clusterer_.icon);
		} 

		this.div_ = document.createElement('DIV');
		
		var position = this.clusterer_.getProjection().fromLatLngToDivPixel(this.center);
		
		if(this.clusterer_.classname != null) {
			this.div_.setAttribute('class', this.clusterer_.classname);
		}
		
		this.div_.style.cssText = 'position: absolute; top: ' + (position.y + 2) + 'px; left: ' + (position.x + 1) + 'px; margin: -' + this.clusterer_.icon.anchor.y + 'px 0 0 -' + this.clusterer_.icon.anchor.x + 'px; cursor: pointer;';
	   	this.div_.innerHTML = this.marker.length;
	      	
		this.clusterer_.getPanes().floatPane.appendChild(this.div_);
		
		var reference = this;
		google.maps.event.addDomListener(this.div_, 'click', function() {
			reference.clusterer_.onClick(reference);
		});

		reference.clusterer_.onDrawCluster(reference);
	}
	
	Cluster.prototype.setTitle = function(title) {
		title = title.toString();
		
		this.div_.setAttribute('title', title);
		this.marker_.setTitle(title);
	}
	
	Cluster.prototype.getTitle = function() {
		return this.marker_.getTitle();
	}
