function ClusterManager( bounds, tileDimensions, regionManager ) {
	this.setRegionManager( regionManager );
	this.init( bounds, tileDimensions );
}

ClusterManager.prototype.init = function( bounds, tileDimensions ) {
	this.tileDimensions = tileDimensions;
	
	var mbounds = bounds.toSpan();
	
	this.stepx = mbounds.lng() / this.tileDimensions.x;
	this.stepy = mbounds.lat() / this.tileDimensions.y;
	this.latBoundary = 0; //bounds.getSouthWest().lat();
	this.lngBoundary = 0; //bounds.getSouthWest().lng();
	
	this.clusters = new Array();
	
	this.clear();
}

ClusterManager.prototype.getGridParametersObject = function() {
	return { gridWidth: this.stepx, 
			 gridHeight: this.stepy, 
			 gridX: this.lngBoundary, 
			 gridY: this.latBoundary };
}

ClusterManager.prototype.setRegionManager = function( regionManager ) {
	this.regionManager = regionManager;
}

ClusterManager.prototype.addDestination = function( dest, point ) {
	if ( point == null ) {
		point = new GLatLng( dest.latitude, dest.longitude );
	}
	
	var clu = this.getClusterForCoordinates( point );
	if ( !clu.addConditionally( dest, point ) ) {
		// Internal error. Set a breakpoint here.
		var j = 0;
		
		return false;
	}
	
	return true;
};

ClusterManager.prototype.getClusterForCoordinates = function( point ) {
	var index = this.getIndexFor( point );
	
	return this.getOrCreateClusterForIndex( index.x, index.y );
};

ClusterManager.prototype.getIndexFor = function( point ) {
	var lngnorm = point.lng() - this.lngBoundary;
	lngnorm /= this.stepx;
	lngnorm = Math.floor(lngnorm);	// Calculate tile x-index.

	var latnorm = point.lat() - this.latBoundary;
	latnorm /= this.stepy;
	latnorm = Math.floor(latnorm);	// Calculate tile y-index.

	return { x: lngnorm, y: latnorm };
};

ClusterManager.prototype.getOrCreateClusterForIndex = function( x, y ) {
	var c = this.getCluster( x, y );
	if( c == null ) {
		c = this.createCluster( x, y );
	}
	
	return c;
};

ClusterManager.prototype.getCluster = function( x, y ) {
	var key = this.makeKeyFromIndices( x, y );
	
	return this.clusters[key];
};

ClusterManager.prototype.createCluster = function( x, y ) {
	var bounds = this.getClusterBounds( x, y );
	var clu = new Cluster( bounds, 0, 0 );
	this.addCluster( x, y, clu );
	return clu;
}

ClusterManager.prototype.addCluster = function( x, y, clu ) {
	var key = this.makeKeyFromIndices( x, y );
	this.clusters[key] = clu;
};

ClusterManager.prototype.makeKeyFromIndices = function( x, y ) {
	return "" + x + "|" + y;
};

ClusterManager.prototype.getClusterButtonAt = function( latlng ) {
	var index = this.getIndexFor( latlng );
	clu = this.getCluster( index.x, index.y );
	
	if( clu == null ) {
		return null;
	}
	
	return ( clu.isPointInsideButton( latlng ) ) ? clu : null; 
};

ClusterManager.prototype.getClusterBounds = function( x, y ) {
	var lat = this.latBoundary + ( y * this.stepy );
	var lng = this.lngBoundary + ( x * this.stepx );
	return new GLatLngBounds(
			new GLatLng( lat, lng ), // SW
			new GLatLng( lat + this.stepy, lng + this.stepx )	 // NE
			);
};

ClusterManager.prototype.getClusters = function() {
	return this.clusters;
};

ClusterManager.prototype.clear = function() { this.clusters = []; }

ClusterManager.prototype.getClipTiles = function( bounds ) {
	var southWest = this.getIndexFor( bounds.getSouthWest() );
	var northEast = this.getIndexFor( bounds.getNorthEast() );

	clipTiles = [];
	for( var x = southWest.x; x < northEast.x; x++ ) {
		for( var y = southWest.y; y < northEast.y; y++ ) {
			clipTiles.push( this.getClusterBounds( x, y ) );
		}
	}
	
	return clipTiles;
}
