// Google Maps Utility Functions - For Use in City Of Sydney Online Applications
// Ali Chamas (c) created 10-11-2006 dragonworxau@yahoo.com.au
// City of Sydney Council www.cityofsydney.nsw.gov.au
// Version History
//		AC 19/03/07 - moved functions into global static GMapUtil object

// global static object to group functions
function GMapUtil() {}

// global variable to hold GMap2 object, consistently referenced throughout session
GMapUtil.map;

// global variable to hold XMLDocument (if loaded)
GMapUtil.xml = null;

// return a property array with the 8 LatLng map bounds corners, plus other extent info
GMapUtil.getMapCorners = function () {
	var bounds = GMapUtil.map.getBounds();
	var latSpan = getMapLatSpan();
	var lngSpan = getMapLngSpan();
	var topLeft = new GLatLng(bounds.getNorthEast().lat(), bounds.getSouthWest().lng());
	var topCenter = new GLatLng(topLeft.lat(), topLeft.lng() + (lngSpan / 2));
	var topRight = new GLatLng(bounds.getNorthEast().lat(), bounds.getNorthEast().lng());
	var rightCenter = new GLatLng(topRight.lat() - (latSpan / 2), topRight.lng());
	var bottomRight = new GLatLng(bounds.getSouthWest().lat(), bounds.getNorthEast().lng());
	var bottomCenter = new GLatLng(bottomRight.lat(), topLeft.lng() + (lngSpan / 2));
	var bottomLeft = new GLatLng(bounds.getSouthWest().lat(), bounds.getSouthWest().lng());
	var leftCenter = new GLatLng(topLeft.lat() - (latSpan / 2), topLeft.lng());
	var corners = {
		"topLeft": topLeft,
		"topCenter": topCenter,
		"topRight": topRight,
		"rightCenter": rightCenter,
		"bottomRight": bottomRight,
		"bottomCenter": bottomCenter,
		"bottomLeft": bottomLeft,
		"leftCenter": leftCenter
	}
	return corners;
}

// define constants
GMapUtil.CENTER = "CENTER";
GMapUtil.BOTTOMLEFT = "BOTTOMLEFT";

// add a marker using the given icon info that opens an infoWindow containing the given domHtml as xml
GMapUtil.createMarkerDom = function (point, domXml, iconUrl, shadowUrl, iconSize, iconAnchor, clickable, title) {
	//addTextLine("debug", point + "+" + domXml + "+" + iconUrl + "+" + shadowUrl + "+" + iconSize + "+" + iconAnchor);
	var icon = GMapUtil.createIcon(iconUrl, shadowUrl, iconSize, iconAnchor);
	var opts = {"icon": icon, "clickable": clickable, "title": title};
	var marker = new GMarker(point, opts);
	if (domXml != null) GEvent.addListener(marker, "click", function() {GMapUtil.map.openInfoWindow(point, domXml);});
	return marker;
}

GMapUtil.createMarker = function (point, iconUrl, shadowUrl, iconSize, iconAnchor, clickable, title) {
	var icon = GMapUtil.createIcon(iconUrl, shadowUrl, iconSize, iconAnchor);
	var opts = {"icon": icon, "clickable": clickable, "title": title};
	var marker = new GMarker(point, opts);
	return marker;
}

// create an icon and initialise it with the given values
GMapUtil.createIcon = function (iconUrl, shadowUrl, iconSize, iconAnchor) {
	var iconSizePt = GMapUtil.parseCoords(iconSize);
	var iconAnchorPt = GMapUtil.getCoords(iconSizePt, iconAnchor);
	var icon = new GIcon();
	icon.image = iconUrl;
	if (shadowUrl != null) icon.shadow = shadowUrl;
	icon.iconSize = new GSize(iconSizePt.x, iconSizePt.y);
	if (shadowUrl != null) icon.shadowSize = icon.iconSize;
	icon.iconAnchor = new GPoint(iconAnchorPt.x, iconAnchorPt.y);
	return icon;
}

// take a comma-separated string set of int or float points, and return an object with x & y properties
GMapUtil.parseCoords = function (coords) {
	var array = coords.split(",");
	return {"x": parseFloat(array[0].replace(" ", "")), "y": parseFloat(array[1].replace(" ", ""))};
}

GMapUtil.getCoords = function (iconSizePt, iconAnchor) {
	switch (iconAnchor) {
		case GMapUtil.CENTER:
			return {"x": iconSizePt.x / 2, "y": iconSizePt.y / 2};
			break;
		case GMapUtil.BOTTOMLEFT:
			return {"x": 0, "y": iconSizePt.y};
			break;
	}
}

// take either a single arg which is a comma separated string of lat,lng - or two args which are a lat and lng string
GMapUtil.getLatLng = function (pointStrOrLat, lng) {
	if (lng == null) {
		var coords = GMapUtil.parseCoords(pointStrOrLat);
		return new GLatLng(coords.x, coords.y);
	} else {
		return new GLatLng(parseFloat(pointStrOrLat), parseFloat(lng));
	}
}

// accessor for the DIV container used by the GMap2 class
GMapUtil.getGMapDiv = function () {
	return document.getElementById("GMap");
}

// accessor for the DIV container used by the GMap2 class
GMapUtil.getNoGMapDiv = function () {
	return document.getElementById("NoGMap");
}

// return the map div width
GMapUtil.getMapWidth = function() {
	return GMapUtil.getGMapDiv().clientWidth;
}

// return the map div height
GMapUtil.getMapHeight = function() {
	return GMapUtil.getGMapDiv().clientHeight;
}

// return the lat span of the map
GMapUtil.getMapLatSpan = function() {
	var bounds = GMapUtil.map.getBounds();
	return bounds.getNorthEast().lat() - bounds.getSouthWest().lat();
}

// return the lng span of the map
GMapUtil.getMapLngSpan = function() {
	var bounds = GMapUtil.map.getBounds();
	return bounds.getNorthEast().lng() - bounds.getSouthWest().lng();
}

// access inner objects within Google API - clever hacking by examining Googles cryptic source code through Javascript source code vunerabilities
GMap2.prototype.getOverlays = function() {return this.q;}
GMarker.prototype.getDIV = function(a) {return this.ch;}
GMarker.prototype.swapImage = function(newImageUrl, oldWidth, oldHeight, newWidth, newHeight, newAnchorX, newAnchorY) {
	// NOTE: looses infoWindow and clickable state, needs to be tested further or unused
	var src = this.getDIV().src;
	var outerHTML = this.getDIV().outerHTML;
	while (outerHTML.indexOf(src) > -1)	outerHTML = outerHTML.replace(src, newImageUrl);
	outerHTML = outerHTML.replace("WIDTH: " + oldWidth, "WIDTH: " + newWidth);
	outerHTML = outerHTML.replace("HEIGHT: " + oldHeight, "HEIGHT: " + newHeight);
	this.getDIV().outerHTML = outerHTML;
	this.getIcon().iconAnchor = new GPoint(newAnchorX, newAnchorY);
}

// add support for the mousewheel
GMap2.prototype.hookMouseWheelToZoom = function () {
	GMapUtil.map.enableContinuousZoom();
	function wheelZoom(a) { (a.detail || -a.wheelDelta) < 0 ? GMapUtil.map.zoomIn() : GMapUtil.map.zoomOut(); }
	GEvent.addDomListener(GMapUtil.getGMapDiv(), "DOMMouseScroll", wheelZoom); // Mozilla event
	GEvent.addDomListener(GMapUtil.getGMapDiv(), "mousewheel", wheelZoom); // IE event
}

// return the markers of the GMap instance
GMapUtil.getMarkers = function() {
	var markers = new Array();
	var overlays = GMapUtil.map.getOverlays();
	for (var i=0; i<overlays.length; i++)
	{
		if (overlays[i].getIcon != null) markers.push( overlays[i] );
	}
	return markers;
}

// return the polylines of the GMap instance
GMapUtil.getPolylines = function () {
	var polylines = new Array();
	var overlays = GMapUtil.map.getOverlays();
	for (var i=0; i<overlays.length; i++)
	{
		if (overlays[i].getVertexCount != null) polylines.push( overlays[i] );
	}
	return polylines;
}

// sort the points by lat|lng so that the line does not intersect itself
GMapUtil.SortPoints = function (points, sortType) {
	if (sortType == "lat")
	{
		points.sort(function(p1, p2) {return p1.lat() - p2.lat();});
	}
	if (sortType == "lng")
	{
		points.sort(function(p1, p2) {return p1.lng() - p2.lng();});
	}
}

//-----------------------------------------------------------------------
// A Rectangle is a simple overlay that outlines a lat/lng bounds on the
// map. It has a border of the given weight and color and can optionally
// have a semi-transparent background color.
function Rectangle(bounds, opt_weight, opt_color) {
	this.bounds_ = bounds;
	this.weight_ = opt_weight || 2;
	this.color_ = opt_color || "#888888";
}

Rectangle.prototype = new GOverlay();

// Creates the DIV representing this rectangle.
Rectangle.prototype.initialize = function(map) {
	// Create the DIV representing our rectangle
	var div = document.createElement("div");
	div.style.border = this.weight_ + "px solid " + this.color_;
	div.style.position = "absolute";

	// Our rectangle is flat against the map, so we add our selves to the
	// MAP_PANE pane, which is at the same z-index as the map itself (i.e.,
	// below the marker shadows)
	GMapUtil.map.getPane(G_MAP_MAP_PANE).appendChild(div);

	this.map_ = map;
	this.div_ = div;
}

// Remove the main DIV from the map pane
Rectangle.prototype.remove = function() {
	this.div_.parentNode.removeChild(this.div_);
}

Rectangle.prototype.color = function(newColor) {
	this.color_ = newColor;
	this.div_.style.border = this.weight_ + "px solid " + this.color_;
}

// Copy our data to a new Rectangle
Rectangle.prototype.copy = function() {
	return new Rectangle(this.bounds_, this.weight_, this.color_,
	this.backgroundColor_, this.opacity_);
}

// Redraw the rectangle based on the current projection and zoom level
Rectangle.prototype.redraw = function(force) {
	// We only need to redraw if the coordinate system has changed
	if (!force) return;

	// Calculate the DIV coordinates of two opposite corners of our bounds to
	// get the size and position of our rectangle
	var c1 = this.map_.fromLatLngToDivPixel(this.bounds_.getSouthWest());
	var c2 = this.map_.fromLatLngToDivPixel(this.bounds_.getNorthEast());

	// Now position our DIV based on the DIV coordinates of our bounds
	this.div_.style.width = Math.abs(c2.x - c1.x) + "px";
	this.div_.style.height = Math.abs(c2.y - c1.y) + "px";
	this.div_.style.left = (Math.min(c2.x, c1.x) - this.weight_) + "px";
	this.div_.style.top = (Math.min(c2.y, c1.y) - this.weight_) + "px";
}

//-----------------------------------------------------------------------
// A ProgressBar is a simple overlay that displays a filled rectangle on the
// map. It has a inner ProgressBar that indicates the progress level.
function ProgressBar(left, top, width, height, outerColor, innerColor) {
	this.left_ = left;
	this.top_ = top;
	this.width_ = width;
	this.height_ = height;
	this.innerColor_ = innerColor;
	this.outerColor_ = outerColor;
}

ProgressBar.prototype = new GOverlay();

// Creates the DIV representing this ProgressBar.
ProgressBar.prototype.initialize = function(map) {
	// Create the DIV representing our ProgressBar
	var outerDiv = document.createElement("div");
	var innerDiv = document.createElement("div");
	outerDiv.style.textAlign = "left";
	outerDiv.style.backgroundColor = this.outerColor_;
	innerDiv.style.backgroundColor = this.innerColor_;
	outerDiv.style.position = "absolute";
	innerDiv.style.position = "relative";
	outerDiv.style.height = this.height_ + "px";
	innerDiv.style.height = this.height_ + "px";
	outerDiv.style.left = this.left_ + "px";
	innerDiv.style.left = "0px";
	outerDiv.style.top = this.top_ + "px";
	innerDiv.style.top = "0px";
	outerDiv.style.width = this.width_ + "px";
	innerDiv.style.width = "5px";
	outerDiv.style.border = "1px solid " + this.outerColor_;
	//innerDiv.style.border = "1px solid " + this.outerColor_;

	this.outerDiv_ = outerDiv;
	this.innerDiv_ = innerDiv;

	// Our ProgressBar is flat against the map, so we add our selves to the
	// MAP_PANE pane, which is at the same z-index as the map itself (i.e.,
	// below the marker shadows)
	outerDiv.appendChild(innerDiv);
	GMapUtil.map.getPane(G_MAP_FLOAT_PANE).appendChild(outerDiv);

	this.map_ = map;
}

// Remove the main DIV from the map pane
ProgressBar.prototype.remove = function() {
	this.outerDiv_.parentNode.removeChild(this.outerDiv_);
}

ProgressBar.prototype.value = function(amount) {
	var amount_ = Math.min(amount, 100);
	amount_ = Math.max(amount_, 0);
	this.innerDiv_.style.width = amount_ + "%";
}

ProgressBar.prototype.outerColor = function(newColor) {
	this.outerColor_ = newColor;
	this.outerDiv_.style.backgroundColor = this.outerColor_;
	this.outerDiv_.style.border = "1px solid " + this.outerColor_;
}

ProgressBar.prototype.innerColor = function(newColor) {
	this.innerColor_ = newColor;
	this.innerDiv_.style.backgroundColor = this.innerColor_;
}

ProgressBar.prototype.redraw = function(force) {
	// We only need to redraw if the coordinate system has changed
	if (!force) return;
}