var toll=toll || {};
//toll.Map
toll.GMap2Wrapper=function(gmap2, onLoadCallback){
	//Move info window up a little so its pointing at the marker instead of the same lat, lng point that the marker is pointing to.
	//var _INFO_WINDOW_OFFSET=new GSize(0, -21);//Numbers dependent on icons used
	var _mapBounds={};//{xMin:,xMax:,yMin:,yMax:,}    (latitude and longitude numbers  x is longitude, y is latitude)
	
	//Avoid drawing markers twice on new search result
	var _zoomInit=false;
	var _locations=new Array();//[{latitude:,longitude:,id:}]
	// Locations with invalid lat,lng and cannot be shown on map
	var _badLocations = new Array();
	var _infoWindowContentFactory;//Function
	var _markerFactory;//Function
	var _removeMarkersFunction;//Function

	var _getCenterPoint=function(bounds/*{yMin:,yMax:,xMin:,xMax:}*/){
		var yMin=Number(bounds['yMin']), yMax=Number(bounds['yMax']), xMin=Number(bounds['xMin']), xMax=Number(bounds['xMax']);
		var centerLat=yMin+((yMax-yMin)/2);
		var centerLng=xMin+((xMax-xMin)/2);
		var point=new GLatLng(centerLat, centerLng);
		return point;
	};

	var _locationTOMap={
		lat:"latitude",
		lng:"longitude"
	}

	var  _validCoordinates={ 
		lat:{min:-90, max:90},
		lng:{min:-180, max:180}
	}
	
	var _getClusterCenterPoint=function(cluster/*[{latitude:,longitude:}]*/)/*GLatLng*/{
		//return new GLatLng(cluster[0]['latitude'],cluster[0]['longitude']);
        var x, y, xMin=0, xMax=0, yMin=0, yMax=0, loc;
        for (var i = 0; i < cluster.length; i++ ){
            loc = cluster[i];
            x = loc[_locationTOMap.lng];
            y = loc[_locationTOMap.lat];
            //Math.abs(x)?
            if (i == 0) {xMin = x; xMax = x; yMin = y; yMax = y;}
            else{
                if (x < xMin) xMin = x;
                if (x > xMax) xMax = x;
                if (y < yMin) yMin = y;
                if (y > yMax) yMax = y;
            }
        }
        var centerPoint = _getCenterPoint({yMin:yMin, yMax:yMax, xMin:xMin, xMax:xMax});
        return centerPoint;
	};
	var _getLatLngBounds=function(locations/*[latitude:"",longitude:""]*/)/*{xMin:"",xMax:"",yMin:"",yMax:""}*/{
		// xMin = longitude, yMin = latitude
		var obj={};
		
		locations.sort(function(a,b){return a[_locationTOMap.lat] - b[_locationTOMap.lat]});
		obj.yMin=locations[0][_locationTOMap.lat];
		obj.yMax=locations[locations.length-1][_locationTOMap.lat];
		
		locations.sort(function(a,b){return a[_locationTOMap.lng] - b[_locationTOMap.lng]});
		obj.xMin=locations[0][_locationTOMap.lng];
		obj.xMax=locations[locations.length-1][_locationTOMap.lng];
		
		return obj;
	}
	var _getGLatLngBounds=function( bounds/*{xMin:,xMax:,yMin:,yMax:}*/  )/*GLatLngBounds*/{	
		var yMin=Number(bounds['yMin']), yMax=Number(bounds['yMax']), xMin=Number(bounds['xMin']), xMax=Number(bounds['xMax']);
		//SW
		var ymn=yMin, xmn=xMin < xMax ? xMin : xMax ;
		//NE
		var ymx=yMax, xmx=xMin > xMax ? xMin : xMax ;
		
		var sw = new GLatLng(ymn, xmn);
		var ne = new GLatLng(ymx, xmx);
		
		return new GLatLngBounds(sw, ne);
	};
	
	var _isCluster=function(pointA/*GLatLng*/, pointB/*GLatLng*/){
		
		//if(pointA.lat() == pointB.lat() && pointA.lng() == pointB.lng())return true;
		
		var pixelPointA=gmap2.fromLatLngToContainerPixel(pointA);//GPoint {x:,y:}
		var pixelPointB=gmap2.fromLatLngToContainerPixel(pointB);//GPoint {x:,y:}
		
		var pixelDistanceX=Math.abs(pixelPointA.x - pixelPointB.x);
		var pixelDistanceY=Math.abs(pixelPointA.y - pixelPointB.y);
		
		if(pixelDistanceX<4 && pixelDistanceY<4)return true;
		
		return false;
	};
	
	var _getCleanLocations=function(locations)/*[]*/{
		//Return clean copy of passed array
		//Remove location objects from array if lat, lng values are invalid
		var loc,lat,lng;
		var cleanLocations=new Array();
		_badLocations=new Array();
		for(var i=0;i<locations.length;i++){
			loc = locations[i];
			lat=loc[_locationTOMap.lat];
			lng=loc[_locationTOMap.lng];
			if( 
				lat == "" ||
				lng == "" ||
				isNaN(lat) ||
				isNaN(lng) ||
				lat < _validCoordinates.lat.min || 
				lat > _validCoordinates.lat.max ||
				lng < _validCoordinates.lng.min ||
				lng > _validCoordinates.lng.max      ){
				//is bad
				_badLocations.push(loc);
			}else{
				cleanLocations.push(loc);
			}
		}
		return cleanLocations;
	};
	

	var mapWrapper = {
		//Wraps GMap2 instance
		enableScrollWheelZoom:function(){
			gmap2.enableScrollWheelZoom();
		},
		disableScrollWheelZoom:function(){
			gmap2.disableScrollWheelZoom();
		},
		getBadLocations:function(){
			return _badLocations;
		},
		
		drawMarkers:function(
					locations/*[{id:,latitude:,longitude:}]*/,
					infoWindowContentFactory/*{function(data/*[{id:}]*/,
					markerFactory/*{function(map:GMap2,point:GLatLng, cluster:[{id:,latitude:,longitude:}], infoWindowContentFactory:function){},}*/,
					removeMarkersFunction,
					zoomToLocations/*boolean*/
					){
			if(gmap2==null)throw new Error('The map must be created first before drawing markers!');
			if(locations==null || locations.length < 1 || infoWindowContentFactory==null || markerFactory==null)return;
			if(typeof infoWindowContentFactory != 'function')throw new Error('infoWindowContentFactory passed to drawMarkers is not a function!');
			if(typeof markerFactory != 'function')throw new Error('markerFactory passed to drawMarkers is not a function!');
			if(typeof removeMarkersFunction != 'function')throw new Error('removeMarkersFunction passed to drawMarkers is not a function!');
			
			if(_removeMarkersFunction != null){
				_removeMarkersFunction();
			}
			_zoomInit=false;
			// Remove records with bad lat, lng
			//locations = ;
			
			_locations=_getCleanLocations(locations);
			_infoWindowContentFactory=infoWindowContentFactory;
			_markerFactory=markerFactory;
			_removeMarkersFunction = removeMarkersFunction;
			
		    //Set bounds/zoom level of map
		    _mapBounds=_getLatLngBounds(_locations);

			//Map | Satellite | Hybrid  -comment out to only allow map
			//map.addControl(new GMapTypeControl());
			
			//var centerPoint=_getCenterPoint(mapBounds);
			if(zoomToLocations){
				var centerPoint=_getCenterPoint(_mapBounds);
				var bounds=_getGLatLngBounds(_mapBounds);
				
				var zoomLevel=gmap2.getBoundsZoomLevel(bounds);
				
				// Don't zoom into much if there are very few locations, or they are very close tocether.
				var maxZoom = 13;
				var zoomLevel = zoomLevel > maxZoom ? maxZoom : zoomLevel;
				//
				gmap2.setCenter(centerPoint, zoomLevel);
			}
			//TODO remove markers that aren't relevent to zoom level, keep good ones.
			//alert(zoomLevel)
			//return;
			
			//    Draw markers
			
			//Sort locations by latitude to aid in creating location clusters
			_locations.sort(function(a,b){return a['latitude'] - b['latitude']});
			
			var clusteredLocations=new Array();
			var cluster=new Array();
			for(var i=0; i<_locations.length; i++){
				var loc=_locations[i];
				
				var lat=loc[_locationTOMap.lat];
				var lng=loc[_locationTOMap.lng];
				
				var point=new GLatLng(lat,lng);
				
				if(i>0 && !_isCluster(point, prevPoint)){
				    //if(i>0 && prevLat != lat && prevLng != lng){
					clusteredLocations.push(cluster.slice());
					cluster=new Array();
				}
				
				var prevPoint=point;
				
				loc['index']=i;
				cluster.push(loc);
				if(i==_locations.length-1){
					clusteredLocations.push(cluster.slice());
				}
			}
			
			for(var i=0; i<clusteredLocations.length; i++){
				var cluster=clusteredLocations[i];
				var marker = markerFactory(gmap2, _getClusterCenterPoint(cluster), cluster, infoWindowContentFactory);
				//_createMarker(_getClusterCenterPoint(cluster), cluster, infoWindowContentFactory);
				//Check if marker already exists
				//var marker=_markerWrapperMap[0]['id']]
				if(marker != null){
					gmap2.addOverlay(marker);
				}

			}
		}
		
	}

	var _init=function() {
		// Call this after page DOM loaded
		if(gmap2 == null)throw new Error("Can't create map. Map html element is null");
	    if(!GBrowserIsCompatible())return;
		
		
	 	GEvent.addListener(gmap2, "zoomend", function() {
	 	 	if(_locations==null || _locations.length==0)return;
	 	 	if(_zoomInit){
	 	 		mapWrapper.drawMarkers.apply(mapWrapper,[_locations, _infoWindowContentFactory, _markerFactory, _removeMarkersFunction, false]);
	 	 	}
	 	 	_zoomInit=true;
	 	});
	 	//gmap2.enableScrollWheelZoom();
		gmap2.addControl(new GLargeMapControl());
		
		
		GEvent.addListener(gmap2, "load", function() {
			onLoadCallback();
		});
	}
	_init();
	return mapWrapper;
};

