function GoogleMap()
{
	var baseObject = this;
	var googleMapArray = new Object();


	this._SetPoint = function(pointJSON, mapName, position)
	{
		if (pointJSON.Status.code != 200)
		{
			var msg = "An error occurred and a point cannot be shown. (Google Reason: ";

			switch (pointJSON.Status.code)
			{
				case 400:
					msg += "A directions request could not be successfully parsed. For example, the request may have been rejected if it contained more than the maximum number of waypoints allowed.";
					break;

				case 500:
					msg += "A geocoding, directions or maximum zoom level request could not be successfully processed, yet the exact reason for the failure is not known.";
					break;

				case 601:
					msg += "Synonym for G_GEO_MISSING_QUERY.";
					break;

				case 602:
					msg += "No corresponding geographic location could be found for the specified address. This may be due to the fact that the address is relatively new, or it may be incorrect.";
					break;

				case 603:
					msg += "The geocode for the given address or the route for the given directions query cannot be returned due to legal or contractual reasons.";
					break;

				case 604:
					msg += "The GDirections object could not compute directions between the points mentioned in the query. ";
					msg += "This is usually because there is no route available between the two points, or because we do not have data for routing in that region.";
					break;

				case 610:
					msg += "The given key is either invalid or does not match the domain for which it was given.";
					break;

				case 620:
					msg += "The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time. ";
					msg += "If you're sending multiple requests in parallel or in a tight loop, use a timer or pause in your code to make sure you don't send the requests too quickly.";
					break;

				default:
					msg += "Code " + pointJSON.Status.code + ": is an unknown code.";
			}

			msg += ").";
			alert(msg);
		}
		else
		{
			// Verify location was found
			if (pointJSON.Placemark)
			{
				googleMapArray[mapName].points[position].latitude = pointJSON.Placemark[0].Point.coordinates[1];
				googleMapArray[mapName].points[position].longitude = pointJSON.Placemark[0].Point.coordinates[0];
		
		
				if (googleMapArray[mapName].points[position].onGeocode != null)
				{
					googleMapArray[mapName].points[position].onGeocode(googleMapArray[mapName].points[position]);
				}
			}
	
			// This is the only point to load on the map
			else if (googleMapArray[mapName].points.length == 1)
			{
				var originalAddress = googleMapArray[mapName].points[0].address;
				var address = googleMapArray[mapName].points[0].city + " " + googleMapArray[mapName].points[0].state;
				var geocoder = new GClientGeocoder();
				var centerMap = function(ptJSON)
				{
					var msg = "Address (" + originalAddress + ") could not be located.";
					
					// Verify location was found
					if (ptJSON.Placemark)
					{
						googleMapArray[mapName].points[0].altLatitude = ptJSON.Placemark[0].Point.coordinates[1];
						googleMapArray[mapName].points[0].altLongitude = ptJSON.Placemark[0].Point.coordinates[0];
						
						msg += " Instead the map has attempted to center on the city the address is located in.";
					}
	
					alert(msg);
				};
	
				geocoder.getLocations(address, centerMap);
			}
		}
	}


	this.AddMapPoint = function(attributes)
	{
		// Create map object and add it to the array
		if (!googleMapArray[attributes.mapName])
		{
			var mapObj = new Object();
			mapObj.points = new Array();

			googleMapArray[attributes.mapName] = mapObj;
		}


		var pointInfo = new Object();
		pointInfo.address = attributes.address;
		pointInfo.city = attributes.city;
		pointInfo.state = attributes.state;
		pointInfo.country = attributes.country;
		pointInfo.latitude = attributes.latitude;
		pointInfo.longitude = attributes.longitude;
		pointInfo._loaded = false;
		pointInfo.title = attributes.title;
		pointInfo.infoWindowID = attributes.infoWindowID;
		pointInfo.marker = null;
		pointInfo.center = attributes.center;
		pointInfo.initShow = attributes.show;
		pointInfo.zoomLevel = attributes.zoomLevel;
		pointInfo.customIcon = attributes.customIcon;
		pointInfo.onGeocode = attributes.onGeocode;


		position = googleMapArray[attributes.mapName].points.push(pointInfo);
		position--;


		if ((attributes.latitude == null || attributes.longitude == null) && attributes.address != null)
		{
			// Position Reference Fix
			var runGeocoder = function(position)
			{
				var pos = position;
				var geocoder = new GClientGeocoder();
				geocoder.getLocations(attributes.address, function(pointJSON) {baseObject._SetPoint(pointJSON, attributes.mapName, pos);});
			}

			runGeocoder(position);
		}
	}


	this.AddMap = function(mapName, bgColor, mapTypes, panZoomType, showMapType, showOverview, showPanZoom, showScale, pointCount, latitude, longitude)
	{
		var mTypes = GetMapTypes(mapTypes);
		var pzType = GetPanZoomType(panZoomType);

		var map = new GMap2(document.getElementById(mapName), {backgroundColor: bgColor, mapTypes: eval(mTypes)});

		map.enableContinuousZoom();
		map.enableScrollWheelZoom();

		if (showMapType)
		{
			map.addControl(new GMapTypeControl());
		}
		if (showOverview)
		{
			map.addControl(new GOverviewMapControl());
		}
		if (showPanZoom)
		{
			map.addControl(eval("new " + pzType));
		}
		if (showScale)
		{
			map.addControl(new GScaleControl());
		}
		if (latitude != null && longitude != null)
		{
			map.setCenter(new GLatLng(latitude, longitude), 0);
		}


		// Create map object and add it to the array
		if (!googleMapArray[mapName])
		{
			var mapObj = new Object();
			mapObj._loaded = false;
			mapObj.map = map;
			mapObj.points = new Array();
			mapObj.pointCount = pointCount;
			mapObj.pointLoadAttempts = 0;
	
			googleMapArray[mapName] = mapObj;
		}
		else
		{
			googleMapArray[mapName]._loaded = false;
			googleMapArray[mapName].map = map;
			googleMapArray[mapName].pointCount = pointCount;
			googleMapArray[mapName].pointLoadAttempts = 0;
		}


		LoadPoints(mapName);
	}


	this.GetMap = function(mapName)
	{
		var map = null;

		if (googleMapArray[mapName])
		{
			map = googleMapArray[mapName].map;
		}

		return map;	
	}


	this.GetMapObject = function(mapName)
	{
		var mapObject = null;

		if (googleMapArray[mapName])
		{
			mapObject = googleMapArray[mapName];
		}

		return mapObject;	
	}


	this.GetMapPoint = function(mapName, pointPosition)
	{
		var point = null

		if (googleMapArray[mapName])
		{
			if (pointPosition >= 0 && pointPosition < googleMapArray[mapName].points.length)
			{
				point = googleMapArray[mapName].points[pointPosition].marker;
			}
		}

		return point;
	}


	this.GetMapPoints = function(mapName)
	{
		var points = new Array();

		if (googleMapArray[mapName])
		{
			for (var i = 0; i < googleMapArray[mapName].points.length; i++)
			{
				points.push(googleMapArray[mapName].points[i].marker);
			}
		}

		return points;
	}


	this.GetMapPointObject = function(mapName, pointPosition)
	{
		var pointObject = null

		if (googleMapArray[mapName])
		{
			if (pointPosition >= 0 && pointPosition < googleMapArray[mapName].points.length)
			{
				pointObject = googleMapArray[mapName].points[pointPosition];
			}
		}

		return pointObject;
	}


	function GetMapTypes(mapTypes)
	{
		mapTypes = mapTypes.toLowerCase();
		var mTypes = "";


		if (mapTypes.search(/normal/) > -1)
		{
			mTypes = "G_NORMAL_MAP";
		}


		if (mapTypes.search(/satellite/) > -1)
		{
			if (mTypes.length > 0)
			{
				mTypes += ",";
			}
			mTypes += "G_SATELLITE_MAP";
		}


		if (mapTypes.search(/hybrid/) > -1)
		{
			if (mTypes.length > 0)
			{
				mTypes += ",";
			}
			mTypes += "G_HYBRID_MAP";
		}


		if (mapTypes.search(/physical/) > -1)
		{
			if (mTypes.length > 0)
			{
				mTypes += ",";
			}
			mTypes += "G_PHYSICAL_MAP";
		}


		mTypes = "[" + mTypes + "]";


		if (mapTypes.search(/default/) > -1)
		{
			mTypes = "G_DEFAULT_MAP_TYPES";
		}

		return mTypes;
	}


	function GetPanZoomType(panZoomType)
	{
		var pzType = "";

		switch (panZoomType)
		{
			case "small":
				pzType = "GSmallMapControl()";

			case "smallZoom":
				pzType = "GSmallZoomControl()";

			case "smallZoom3D":
				pzType = "GSmallZoomControl3D()";

			case "large":
				pzType = "GLargeMapControl()";

			default:
				pzType = "GLargeMapControl3D()";
		}

		return pzType;
	}


	function LoadPoints(mapName)
	{
		googleMapArray[mapName].pointLoadAttempts++;

		var point = null;
		var marker = null;
		var markerOptions = null;
		var pointsLoaded = true;
		var infoWindow = null;
		var opener = null;


		for (var i = 0; i < googleMapArray[mapName].points.length; i++)
		{
			if (!googleMapArray[mapName].points[i]._loaded && googleMapArray[mapName].points[i].latitude != null)
			{
				googleMapArray[mapName].points[i]._loaded = true;

				point = new GLatLng(googleMapArray[mapName].points[i].latitude, googleMapArray[mapName].points[i].longitude);

				// Load Marker options
				if (googleMapArray[mapName].points[i].title.length > 0 || googleMapArray[mapName].points[i].customIcon != null)
				{
					markerOptions = {title: "", icon: null};
					if (googleMapArray[mapName].points[i].customIcon != null)
					{
						markerOptions.icon = googleMapArray[mapName].points[i].customIcon();
						googleMapArray[mapName].points[i].customIcon = null;
					}
					
					markerOptions.title = googleMapArray[mapName].points[i].title;
				}

				googleMapArray[mapName].points[i].marker = new GMarker(point, markerOptions);

				// Bind info window
				if (googleMapArray[mapName].points[i].infoWindowID != null && googleMapArray[mapName].points[i].infoWindowID != "")
				{
					infoWindow = document.getElementById(googleMapArray[mapName].points[i].infoWindowID);

					googleMapArray[mapName].points[i].marker.bindInfoWindowHtml(infoWindow.innerHTML);
					infoWindow.parentNode.removeChild(infoWindow);
				}

				googleMapArray[mapName].map.addOverlay(googleMapArray[mapName].points[i].marker);

				// Hide if not init shown
				if (!googleMapArray[mapName].points[i].initShow)
				{
					googleMapArray[mapName].points[i].marker.hide();
				}
			}
			else if (!googleMapArray[mapName].points[i]._loaded)
			{
				pointsLoaded = false;
			}
		}


		// Attempt a reload
		if (
				googleMapArray[mapName].pointLoadAttempts <= 5 && 
				(
					googleMapArray[mapName].points.length != googleMapArray[mapName].pointCount ||
					!pointsLoaded
				)
			)
		{
			var caller = function() {LoadPoints(mapName);};
			setTimeout(caller, 500);
			return;
		}


		// Set Loaded property and center map
		if (
				(
				 	googleMapArray[mapName].points.length == googleMapArray[mapName].pointCount || 
					googleMapArray[mapName].pointCount == 0
				) &&
				pointsLoaded
			)
		{
			googleMapArray[mapName]._loaded = true;
			ResizeAndCenterMap(mapName);
		}

		if (
				(
				 	googleMapArray[mapName].points.length == googleMapArray[mapName].pointCount || 
					googleMapArray[mapName].pointCount == 0
				) &&
				!pointsLoaded && 
				googleMapArray[mapName].pointLoadAttempts > 5				
			)
		{
			ResizeAndCenterMap(mapName);
		}
	}


	function ResizeAndCenterMap(mapName)
	{
		// Center on single point
		if (googleMapArray[mapName].points.length == 1)
		{
			if (googleMapArray[mapName].points[0]._loaded)
			{
				if (googleMapArray[mapName].points[0].zoomLevel != null)
				{
					var zoomLevel = googleMapArray[mapName].points[0].zoomLevel;
				}
				else
				{
					var zoomLevel = 14;
				}
	
				googleMapArray[mapName].map.setCenter(new GLatLng(googleMapArray[mapName].points[0].latitude, googleMapArray[mapName].points[0].longitude), zoomLevel);
			}
			else if (googleMapArray[mapName].points[0].altLatitude)
			{
				if (googleMapArray[mapName].points[0].zoomLevel != null)
				{
					var zoomLevel = googleMapArray[mapName].points[0].zoomLevel;
				}
				else
				{
					var zoomLevel = 14;
				}
	
				googleMapArray[mapName].map.setCenter(new GLatLng(googleMapArray[mapName].points[0].altLatitude, googleMapArray[mapName].points[0].altLongitude), zoomLevel);
			}
		}

		// Center on multiple points
		else if (googleMapArray[mapName].points.length > 1)
		{
			var minLatitude = null;
			var maxLatitude = null;
			var minLongitude = null;
			var maxLongitude = null;

			var centerLatitude = null;
			var centerLongitude = null;


			for (var i = 0; i < googleMapArray[mapName].points.length; i++)
			{
				if (googleMapArray[mapName].points[i].latitude != null && googleMapArray[mapName].points[i].longitude != null)
				{
					if (minLatitude == null || googleMapArray[mapName].points[i].latitude < minLatitude)
					{
						minLatitude = googleMapArray[mapName].points[i].latitude;
					}
	
	
					if (maxLatitude == null || googleMapArray[mapName].points[i].latitude > maxLatitude)
					{
						maxLatitude = googleMapArray[mapName].points[i].latitude;
					}
	
	
					if (minLongitude == null || googleMapArray[mapName].points[i].longitude < minLongitude)
					{
						minLongitude = googleMapArray[mapName].points[i].longitude;
					}
	
	
					if (maxLongitude == null || googleMapArray[mapName].points[i].longitude > maxLongitude)
					{
						maxLongitude = googleMapArray[mapName].points[i].longitude;
					}
	
					if (
							centerLatitude == null && 
							googleMapArray[mapName].points[i].center &&
							googleMapArray[mapName].points[i].latitude != null &&
							googleMapArray[mapName].points[i].longitude != null
						)
					{
						centerLatitude = googleMapArray[mapName].points[i].latitude;
						centerLongitude = googleMapArray[mapName].points[i].longitude;
					}
				}
			}


			var southWestCorner = new GLatLng(minLatitude, minLongitude);
			var northEastCorner = new GLatLng(maxLatitude, maxLongitude);
			var bounds = new GLatLngBounds(southWestCorner, northEastCorner);
			var zoomLevel = googleMapArray[mapName].map.getBoundsZoomLevel(bounds);

			googleMapArray[mapName].map.setCenter(bounds.getCenter(), zoomLevel);

			if (centerLatitude != null)
			{
				googleMapArray[mapName].map.setCenter(new GLatLng(centerLatitude, centerLongitude), zoomLevel);
			}
		}
	}
}
