/**
 * Generic Google Maps handling for Gutenberg Framework
 *
 * @author   Andy West
 *
 **/


/**
 * Class that acts as a wrapper for a GoogleMap
 *
 **/
function MapHandler()
{


	/**
	 * Holds an array of GIcon objects
	 *
	 **/
	this.Icons = new Array();

	
	/**
	 * Holds an array of different GutenMarkerType objects
	 *
	 **/
	this.MarkerTypes = new Array();


	/**
	 * Holds an array of GutenMarker objects
	 *
	 **/
	this.Markers = new Array();


	/**
	 * Calls all of the functions required to create the GMap2 object
	 * and adds Markers as defined in the ParsedXml global variable.
	 *
	 **/
	this.MakeMap = function()
	{

     	this.ReadBasicConfig();
		this.MakeIcons();
		this.MakeMarkerTypes();
		this.MakeMarkers();
		this.GoHome();
		this.PlaceMarkers();

	};


	/**
	 * Creates the GMap2 object and sets the starting point, zoom level and map type.
	 *
	 **/
	this.ReadBasicConfig = function()
	{
		var Config = ParsedXml.documentElement.getElementsByTagName("config");
		
		// containingelement is the id of the DOM element that the map will be
		// placed into
		this.Map = new GMap($(Config[0].getAttribute("containingelement")));
		
		// zoomcallback is the name of an external javascript function that
		// will be called when the map has finished any zoom operations
		this.ZoomCallBack = eval(Config[0].getAttribute("zoomcallback"));

		// holds a GPoint object for the default starting point of the map
      this.StartPoint = new GLatLng(parseFloat(Config[0].getAttribute("startlat")),
												parseFloat(Config[0].getAttribute("startlng")));
												
		// the initial zoom level of the map
		this.StartZoom  = parseInt(Config[0].getAttribute('startzoom'),10);
		
		// the type of map ie. map, satellite or hybrid
		this.MapType = Config[0].getAttribute("maptype");
		
		//Gets the name of the GeoCodeOverrideFunction to be called
		this.GeoCodeOverrideFunction = Config[0].getAttribute("geocodeoverride");
		
		
	}


	/**
	 * Creates the array of Icons as specified in the XML
	 *
	 **/
	this.MakeIcons = function()
	{
		var Icons = ParsedXml.documentElement.getElementsByTagName("icon");
		for (var i = 0; i < Icons.length; i++)
		{
			// Assign to a temp variable as it doesn't seem to work if you add directly to the array
			var TempIcon = new GIcon();
			TempIcon.image            = Icons[i].getAttribute('image');
			TempIcon.shadow           = Icons[i].getAttribute('shadow');
			TempIcon.iconSize         = new GSize(parseInt(Icons[i].getAttribute('iconsizex')), parseInt(Icons[i].getAttribute('iconsizey')));
			TempIcon.shadowSize       = new GSize(parseInt(Icons[i].getAttribute('shadowsizex')), parseInt(Icons[i].getAttribute('shadowsizey')));
			TempIcon.iconAnchor       = new GPoint(parseInt(Icons[i].getAttribute('iconanchorx')), parseInt(Icons[i].getAttribute('iconanchory')));
			TempIcon.infoWindowAnchor = new GPoint(parseInt(Icons[i].getAttribute('infowindowanchorx')), parseInt(Icons[i].getAttribute('infowindowanchory')));
			
			this.Icons[this.Icons.length] = TempIcon;
		}
	}


	/**
	 * Creates the array of GutenMarkerTypes as specified in the XML
	 *
	 **/
	this.MakeMarkerTypes = function()
	{
		var MarkerTypes = ParsedXml.documentElement.getElementsByTagName("markertype");
		for (var i = 0; i < MarkerTypes.length; i++)
		{
		   this.MarkerTypes[this.MarkerTypes.length] = new GutenMarkerType(parseFloat(MarkerTypes[i].getAttribute("id")), this.Icons[parseInt(MarkerTypes[i].getAttribute("icon"))], MarkerTypes[i].getAttribute("uri"));
		}
	}


	/**
	 * Creates the array of GutneMarkers as specified in the XML
	 *
	 **/
	this.MakeMarkers = function()
	{
		var Markers = ParsedXml.documentElement.getElementsByTagName("marker");
		for (var i = 0; i < Markers.length; i++)
		{
			var Point = new GLatLng(parseFloat(Markers[i].getAttribute("lat")), parseFloat(Markers[i].getAttribute("lng")));
			this.Markers[this.Markers.length] = new GutenMarker(parseInt(Markers[i].getAttribute("id")), parseFloat(Markers[i].getAttribute("type")), Point);
		}
	}


	/**
	 * Iterates the array of Markers and places them on the map.  The PlaceMarker
	 * function used to put the markers on the map has been placed outside the class
	 * definition to get around a nasty scoping problem that I was unable to solve
	 * in any other way. @@TODO: Maker this use the MarkerManager object
	 *
	 **/
	this.PlaceMarkers = function()
	{
/*	   if (typeof(this.MarkerManager == 'undefined'))
	   {
	      this.MarkerManager = new GMarkerManager(this.Map);
	   }
*/
	   for(var i=0; i < this.Markers.length; i++)
	   {
		   PlaceMarker(this.Markers[i], this);
	   }
	   
	};


	/**
	 * Returns the map to the starting point and zoom level.
	 *
	 **/
	this.GoHome = function()
	{
		this.Map.setCenter(this.StartPoint, this.StartZoom);
		if (typeof(this.ZoomCallBack) != 'undefined')
		{
			this.ZoomCallBack(this);
		}
	};


	/**
	 * Starts panning in the specified direction.  Uses an interval to allow
	 * continuous scrolling rather than the default behaviour of scrolling half
	 * a screen and stopping.
	 *
	 **/
   this.panDirection = function(dx, dy)
   {
      this.Map.panDirection(dx, dy);
		this.Panner = setInterval('MyMap.Map.panDirection('+dx+','+dy+')',5);
   };


	/**
	 * Clears the interval used for panning so that you cannot end up with a
	 * massive buffer of movements after releasing the control to move the map
	 *
	 **/
   this.StopPanning = function()
   {
      clearInterval(this.Panner);
   };


	/**
	 * Zooms the map in one level and calls the ZoomCallBack function
	 *
	 **/
   this.ZoomIn = function()
   {
      this.Map.zoomIn();
      this.ZoomCallBack(this);
   };


	/**
	 * Zooms the map out one level and calls the ZoomCallBack function
	 *
	 **/
   this.ZoomOut = function()
   {
      this.Map.zoomOut();
      this.ZoomCallBack(this);
   };


	/**
	 * Returns the current zoom level of the map
	 *
	 **/
	this.getZoom = function()
	{
	   return this.Map.getZoom();
	};


	/**
	 * Returns a GutenMarkerType for the given TypeId
	 *
	 * @param    TypeId    int    The ID of the GutenMarkerType we are looking for
	 *
	 * @return   GutenMarkerType
	 *
	 **/
	 this.GetMarkerType = function(TypeId)
	 {
	   for (var i =0; i < this.MarkerTypes.length; i++)
	   {
	      if(this.MarkerTypes[i].TypeId == TypeId)
	      {
	         return this.MarkerTypes[i];
	      }
	   }
	 };


	/**
	 * Function to ensure we only instantiate one GeoCoder
	 *
	 **/
	this.GetGeoCoder = function()
	{
	   if (typeof(this.GeoCoder == 'undefined'))
	   {
	      this.GeoCoder = new GClientGeocoder();
	   }
		return this.GeoCoder;
	}


	/**
	 *
	 *
	 **/
	this.GeoCodeAddress = function (Address)
	{
		this.GetGeoCoder().getLatLng( Address,
												 function(Point)
												 {
													if (!Point)
													{
														alert(Address + " not found");
													}
													else
													{
														AddressPoint = Point;
													}
												 }
												);

	};


	this.FindNearest = function (Address)
	{
		showPopup = false;
		if (this.GeoCodeOverrideFunction != '')
		{
			eval(this.GeoCodeOverrideFunction)(Address);
		}
		else
		{
			this.GetGeoCoder().getLatLng( Address,
												 function(Point)
												 {
													if (!Point)
													{
															ShowAlert('no result', 'orig');
													}
													else
													{
														//ShowAlert(Point,'orig')
													}
													
												 }
									 );
		}
	}

	

	/**
	 * Returns the class type so that it is nice and easy to identify the class
	 *
	 **/
	this.toString = function() {return 'MapHandler';};
} // End of MapHandler()




function ShowAlert(Point,WhereFrom)
{
	
	if (Point != 'no result')
	{
	   // Roughly a half way around the earth.  As far as you can go.
		var ShortestDistance = 20040000;

		for (var i =0; i < MyMap.Markers.length; i++)
		{
			var x = Point.distanceFrom(MyMap.Markers[i].Point);
			if (x < ShortestDistance)
			{
				var NearestMarker = MyMap.Markers[i];
				ShortestDistance = x;
			}
		}
		ReportNearest(NearestMarker);
	}
	else
	{
	   // TODO :: un-hard-code this
		document.getElementById('search_message').innerHTML = 'Your address could not be found.';
	}	
}




/**
 * Class to hold data relevant to the different types of Marker we want to show
 * on our map
 *
 **/
function GutenMarkerType(TypeId, Icon, HtmlUrl)
{
	this.TypeId = TypeId;
	this.Icon = Icon;
	this.HtmlUrl = HtmlUrl;


	/**
	 * Returns the icon associated with this MarkerType
	 *
	 **/
	this.GetIcon = function()
	{
	   return this.Icon;
	};


	/**
	 * Returns the class type so that it is nice and easy to identify the class
	 *
	 **/
	this.toString = function()
	{
		var ObjectString = 'GutenMarkerType\n\n';
		ObjectString += 'TypeId: '+this.TypeId+'\n';
		//ObjectString += 'Icon: '+this.Icon+'\n';
		ObjectString += 'HtmlUrl: '+this.HtmlUrl;
		return ObjectString;
	};

} // End of GutenMarkerType()


/**
 *
 *
 **/
function GutenMarker(MarkerId, Type, Point)
{
	this.MarkerId = MarkerId;
	this.MarkerType = Type;
	this.Point = Point;

	this.prototype = new GMarker(Point);


	/**
	 * Gets the icon appropriate for this Marker, based on it's Type
	 *
	 **/
	this.GetIcon = function(MarkerTypes)
	{
	   return MarkerTypes[this.MarkerType].GetIcon();
	};


	/**
	 * Returns the GutenMarkerType for this GutenMarker
	 *
	 **/
	this.GetMarkerType = function()
	{
	   var MarkerType = MyMap.GetMarkerType(this.MarkerType);
		return MarkerType;
	};


	/**
	 * Returns the Url for the ajax request for the Markers Html info window
	 *
	 **/
	this.GetAjaxUrl = function()
	{
	   var MarkerType = this.GetMarkerType();
		return MarkerType.HtmlUrl + this.MarkerId;
	};
	 
	 
	/**
	 * Returns the class type so that it is nice and easy to identify the class
	 *
	 **/
	this.toString = function() {
		var ObjectString = 'GutenMarker\n\n';
		ObjectString += 'MarkerId: '+this.MarkerId+'\n';
		ObjectString += 'MarkerType: '+this.MarkerType+'\n';
		ObjectString += 'Point: '+this.Point;
		return ObjectString;
	};

}  // End of GutenMarker()

GutenMarker.prototype = GMarker.prototype;//(new GLatLng(1,1));


/**
 * Extend the GOverlay class with our own class to allow for more control over
 * the functionality.
 *
 **/
function GutenOverlay(Width, Height, PointXOffset, PointYOffset, FocalPoint, AjaxWrapper) {
  this.Width = Width;
  this.Height = Height;
  this.PointXOffset = PointXOffset;
  this.PointYOffset = PointYOffset;
  this.FocalPoint = FocalPoint;
  this.div_ = AjaxWrapper;
}

GutenOverlay.prototype = new GOverlay();


/**
 * Adds the overlay div to the map pane
 *
 **/
GutenOverlay.prototype.initialize = function(Map) {
	// Add the overlay to the correct PANE of the map
	Map.getPane(G_MAP_FLOAT_PANE).appendChild(this.div_);
	this.map_ = Map;
}


/**
 * Remove the overlay DIV from the map pane
 *
 **/
GutenOverlay.prototype.remove = function() {
  this.div_.parentNode.removeChild(this.div_);
}


/**
 * Copy the overlay
 *
 **/
GutenOverlay.prototype.copy = function() {
  return new GutenOverlay(this.Width, this.Height, this.PointXOffset, this.PointYOffset, this.FocalPoint);
}


/**
 * Redraw the overlay based on the current projection and zoom level
 *
 **/
GutenOverlay.prototype.redraw = function(force) {
  // We only need to redraw if the coordinate system has changed
  if (!force) return;

  // Get the pixel position of the focal point of the overlay
  var OverlayCoords = this.map_.fromLatLngToDivPixel(this.FocalPoint);

  // Now position our DIV based on the DIV coordinates of our bounds
  this.div_.style.left = (OverlayCoords.x - this.PointXOffset) + "px";
  this.div_.style.top = (OverlayCoords.y - this.PointYOffset) + "px";
}

