// Obtained from http://mapki.com/wiki/KMLparser on 03/08/07 - Ei


//Created by Fernando F. Gallego (ferdy182@gmail.com ~ www.forgottenprojects.com)
//You are free to use, modify and distribute this code.
//Mentioning the author would be appreciated :)

//usage: add the KMLparser.js file to the document. Call KMLparser(url,map); at the bottom of the script code of your map.

// Please set gMaxWidth before calling KMLparser. This is used to define
// the maximum width of the popup window.
var gMaxWidth = 300;
var gMinZoomForLabel = 1;

// If you need to customize the placemark description, turn
// custom_description_defined to true and
// define function custom_description(Placemark)
var custom_description_defined = false;
var run_external_by_placemark_defined = false;

// Store all polygons in the array to manage them.
var polygons = [];
var polygon_names = [];
var polygonlabels = [];
var kml_styles = [];
var kml_stylemaps = [];

// Point markers are added to GMarkerManager
// GMarkerManager is not currently used because it doesn't have moreveMarkers method.
// Instead, all the markers are added to markers array
var markermgr;
var markers = [];

var KmlParser = {
	Parameters: {IconWidth: 16, IconHeight: 18, MaxWidth: 350, MaxHeight: 100},
	TopContainer: null,
	OnFinish: null,
	KmlTree: [],
	
	load: function (map, kml_url, parameters, onFinish) {
		this.Kml = kml_url;
		if (parameters != "") {
			this.Kml += "&" + parameters
		}
		this.map = map;
		this.OnFinish = onFinish;
		GDownloadUrl(this.Kml, this.parseKml.bind(KmlParser));		
	},
	
	parseKml: function (xml_text, readyState) {
		if (readyState == 200){
			var data = GXml.parse(xml_text);

			// Parse style maps
			stylemaps = data.getElementsByTagName("StyleMap");

			for(var i = 0; i < stylemaps.length; i++) {
				var style_id = stylemaps[i].getAttribute('id');

				var stylemap_elements = [];
				var Pairs = stylemaps[i].getElementsByTagName("Pair");
				for (var j = 0; j < Pairs.length; j++) {
					if (getXmlNodeValue(Pairs[j], "key" == 'normal')) {
						var StyleUrl = getXmlNodeValue(Pairs[j], "styleUrl");
						stylemap_elements['styleUrl'] = StyleUrl.replace("#", "");

						kml_stylemaps[style_id] = stylemap_elements;
					}
				}
			}
			this.KmlStyleMaps = {};
			
			// Parse styles
			styles = data.getElementsByTagName("Style");
			var kml_styles = [];
			for(var i=0; i<styles.length; i++) {
				var style_id = styles[i].getAttribute('id');
				if (style_id) {		// process only if <Style> has ID defined.
					kml_styles[style_id] = this.parseStyle(styles[i]);
				}
			}
			this.KmlStyles = kml_styles;
					
			// Loop through Folders: at this moment, top level folders are supported.
			var folders = data.getElementsByTagName("Folder");

			for (var i = 0; i < folders.length; i++) {
				var folder = folders[i];

				// Add it to Folder tree
				// Center location can be pulled from <LookAt>
				var obj_folder = {name: getXmlNodeValue(folder, "name"),
						lon: getXmlNodeValue(folder, "longitude"),
						lat: getXmlNodeValue(folder, "latitude"),
						desc: getXmlNodeValue(folder, 'description').replace(/&amp;#60;/g, "&lt;")};
				this.addFolder(obj_folder);

				// Add placemarks
				var placemark_collection = folder.getElementsByTagName("Placemark");
				var placemarks = $A(placemark_collection).collect(function (item) {
					var placemark = {
						name: getXmlNodeValue(item, 'name'),
						coordinates: getXmlNodeValue(item, 'coordinates'),
						geometry_type: KmlParser.geometryType(item),
						desc: getXmlNodeValue(item, 'description').replace(/&amp;#60;/g, "&lt;"),
						styleUrl: getXmlNodeValue(item, 'styleUrl')
					}

					return placemark;
				});
				placemarks = this.addPlacemarks(placemarks);
				obj_folder["placemarks"] = placemarks;
				this.KmlTree.push(obj_folder);
			}
			
			if (this.OnFinish != null) {
				this.OnFinish();
			}
		}
	},
	
	addFolder: function (obj_folder) {
		// Do nothing for the time being.
	},
	
	addPlacemarks: function (placemarks) {
		for (var i = 0; i < placemarks.length; i++) {
			// Add placemark to Google Map as an overlay
			var placemark = placemarks[i];
			var coordinates = placemark.coordinates;
			var points = this.buildPoints(coordinates);
			
			if (placemark.styleUrl != "") {
				var style_url = placemark.styleUrl.replace("#", "");
			} else {
				//style_url = "_in_place_style_";
				//kml_styles[style_url] = parse_style(placemarks[i].getElementsByTagName("Style")[0]);
			}
			if (this.KmlStyleMaps[style_url]) {
				if (this.KmlStyleMaps[style_url]["styleUrl"]) {
					style_elements = this.KmlStyles[this.KmlStyleMaps[style_url]["styleUrl"]];
				}
			} else if (this.KmlStyles[style_url]) {
				style_elements = this.KmlStyles[style_url];
			}
			
			var opt = this.map.getInfoWindow();
			opt.maxWidth = this.Parameters.MaxWidth;
			opt.maxHeight = this.Parameters.MaxHeight;
			opt.autoScroll = true;
			switch (placemark.geometry_type) {
				case "Point":
					var point = points[0];
					var icon = this.pointIcon("", this.Parameters.IconWidth, this.Parameters.IconHeight);	// Added by Ei
					var marker = this.createMarker(point, placemark.desc, icon, opt);
					break;
				case "LineString":
					color_style = style_elements.LineStyle.color;
					opacity = parseInt(color_style.substr(0,2), 16) / 255;
					color = "#" + color_style.substr(6,2) + color_style.substr(4,2) + color_style.substr(2,2);
					width = style_elements.LineStyle.width;
					var marker = new GPolyline(points, color, parseFloat(width), opacity);
					this.polyClick(marker, placemark.desc, opt);
					break;
				case "Polygon":
					color_style = style_elements.PolyStyle.color;
					opacity = parseInt(color_style.substr(0,2), 16) / 255;
					color = "#" + color_style.substr(6,2) + color_style.substr(4,2) + color_style.substr(2,2);
					var marker = new GPolygon(points, color, 1, 0.9, color, opacity);
					this.polyClick(marker, placemark.desc, opt);
					break;
			}
			placemark.placemark = marker;
			placemarks[i] = placemark;
			this.map.addOverlay(marker);
		}
		return placemarks;
	},	

	buildPoints: function (coordinates_string) {
		var points = [];
		var coordinates = coordinates_string.split(" ");
		for(var j=0; j<coordinates.length; j++){
			var point = coordinates[j].split(",");
			points[j] = new  GLatLng(parseFloat(point[1]), parseFloat(point[0]));
		}
		return points;
	},
	
	createMarker: function (point, desc, icon, opt){
		var marker = new GMarker(point, icon);
		GEvent.addListener(marker, 'click', function() {
			marker.openInfoWindowHtml(desc, opt);
		});
		return marker;
	},
	
	polyClick: function (marker, desc, opt) {
		GEvent.addListener(marker, 'click', function(latlng) {
			if (typeof(latlng) == "undefined") {
				latlng = marker.getBounds().getCenter();
			}
			map.openInfoWindowHtml(latlng, desc, opt);
		});					
	},

	pointIcon: function  (icon_href, width, height) {
		var icon = new GIcon();

		if (icon_href != "") {
			icon.image = icon_href;
		} else {
			// Default icon
			icon.image = "/seamap2/icons/ball_ge.png";
		}

		if (width == 0)  {
			width = gIconWidth;
		}
		if (height == 0)  {
			height = gIconHeight;
		}
		icon.iconSize = new GSize(width, height);
		icon.iconAnchor = new GPoint(width / 2, height / 2);
		icon.infoWindowAnchor = new GPoint(width / 2, height / 2);
		return icon;
	},
		
	geometryType: function (placemark) {
		var geomerties = ["Polygon", "LineString", "Point"];
		
		var geometry_type = $A(geomerties).find(function (item){
			var geometry = placemark.getElementsByTagName(item);
			return geometry.length > 0;
		});
		return geometry_type;
	},
	
	parseStyle: function (style_kml) {
		var style_elements = {};
	
		// Get color of <PolyStyle> and <LineStyle>
		var arry_styles = ["IconStyle", "PolyStyle", "LineStyle"];
		for (j = 0; j < arry_styles.length; j++) {
			var styles = style_kml.getElementsByTagName(arry_styles[j]);
			if (styles.length > 0) {
				var style = styles[0];
				
				switch (arry_styles[j]) {
					case "IconStyle":
						var style_dic = {'scale': getXmlNodeValue(style, "scale"), icon: getXmlNodeValue(style, "Icon")};
						break;
					case "LineStyle":
						var style_dic = {'width': getXmlNodeValue(style, "width")};
						break;
				}
				style_dic['color'] = getXmlNodeValue(style, "color");
				
				style_elements[arry_styles[j]] = style_dic;
			}
		}
		return style_elements;
	}	
}

function KMLparser(path, map){ //path: url to the kml file. map: div where the map is
   var request = GXmlHttp.create();
   request.open('GET', path, true);
   request.onreadystatechange = function(){
		if (request.readyState == 4){
			var data = request.responseXML;

			// Parse style maps
			stylemaps = data.documentElement.getElementsByTagName("StyleMap");

			for(var i = 0; i < stylemaps.length; i++) {
				var style_id = stylemaps[i].getAttribute('id');

				var stylemap_elements = [];
				var Pairs = stylemaps[i].getElementsByTagName("Pair");
				for (var j = 0; j < Pairs.length; j++) {
					if (getXmlNodeValue(Pairs[j], "key" == 'normal')) {
						var StyleUrl = getXmlNodeValue(Pairs[j], "styleUrl");
						stylemap_elements['styleUrl'] = StyleUrl.replace("#", "");

						kml_stylemaps[style_id] = stylemap_elements;
					}
				}
			}


			// Parse styles
			styles = data.documentElement.getElementsByTagName("Style");

			for(var i=0; i<styles.length; i++) {
				var style_id = styles[i].getAttribute('id');
				if (style_id) {		// process only if <Style> has ID defined.
					kml_styles[style_id] = parse_style(styles[i]);
				}
			}

			// Parse placemarks
			placemarks = data.documentElement.getElementsByTagName("Placemark");
			for(var i=0; i<placemarks.length; i++){

				// Get basic elements such as name and description.
				var name = getXmlNodeValue(placemarks[i], "name");
				var desc = "";
				var placemark_geometry = "";

				if (custom_description_defined) {
					desc = custom_description(placemarks[i]);
				} else {
					if (placemarks[i].getElementsByTagName("description").length > 0) {
						// In Firefox, if there is a CR just next to <description>,
						// it counts on childNodes (more than one childNodes.
						// So need to loop through all the childNodes to get the whole description.
						var desc_node = placemarks[i].getElementsByTagName("description");
						desc = "";
						for (j =0; j < desc_node[0].childNodes.length; j++) {
							desc += desc_node[0].childNodes[j].nodeValue;
						}
					}
				}

				if (placemarks[i].getElementsByTagName("styleUrl").length > 0) {
					var style_url = getXmlNodeValue(placemarks[i], "styleUrl");
					style_url = style_url.replace("#", "");
				} else if (placemarks[i].getElementsByTagName("Style").length > 0) {
					style_url = "_in_place_style_";
					kml_styles[style_url] = parse_style(placemarks[i].getElementsByTagName("Style")[0]);
				}

				for (p = 0; p < placemarks[i].getElementsByTagName("coordinates").length; p++) {
					var coordinates;
					coordinates = getXmlNodeValue(placemarks[i], "coordinates");
					for(var chunk=1;  chunk<placemarks[i].getElementsByTagName("coordinates")[p].childNodes.length;chunk++){
						coordinates+=placemarks[i].getElementsByTagName("coordinates")[p].childNodes[chunk].nodeValue;
					}

					// Handling white spaces. Added by Ei
					// In KML the lat/lon should be comma-delimited with no space between them.
					// Sets of coordinates are separated with a space.
					// May use CR as separator, so first replace CRs with spaces.
					coordinates = coordinates.strip();
					coordinates = coordinates.replace(/\n/, " ");

					coordinates = coordinates.split(" ");
					for(var j=0; j<coordinates.length;j++){
						coordinates[j] = coordinates[j].split(",");
					}

					// Point or Line/Polygon
					if(coordinates.length == 1){		// Point
						placemark_geometry = "Point";
						var point = new GLatLng(parseFloat(coordinates[0][1]),parseFloat(coordinates[0][0]));

						legend_line_color = "0";
						legend_poly_color = "0";
						var icon_href = "";		// Default
						if (placemarks[i].getElementsByTagName("IconStyle").length > 0) {
							var icon_style = placemarks[i].getElementsByTagName("IconStyle")[0];
							if (icon_style.getElementsByTagName("Icon").length > 0) {
								var icon = icon_style.getElementsByTagName("Icon")[0];
								if (icon.getElementsByTagName("href").length > 0) {
									icon_href = getXmlNodeValue(icon, "href");
								}
							}
						}
						var icon = point_icon(icon_href, 0, 0);	// Added by Ei

						var opt = map.getInfoWindow();
						opt.maxWidth = gMaxWidth;
						var marker = createMarker(point, name, desc, icon, opt);

						map.addOverlay(marker);
						markers.push(marker);
					} else {		// Line or Polygon
						var points = [];
						var line_color = "#FF0000"; 	// Default line color.
						var poly_color = "#FF0000"; 	// Default polygon fill color.

						for(var j=0; j<coordinates.length; j++){
							points[j] = new  GLatLng(parseFloat(coordinates[j][1]), parseFloat(coordinates[j][0]));
						}

						// Get line/polygon colors if defined in styleUrl
						if (kml_stylemaps[style_url]) {
							if (kml_stylemaps[style_url]["styleUrl"]) {
								style_elements = kml_styles[kml_stylemaps[style_url]["styleUrl"]];
							}
						} else if (kml_styles[style_url]) {
							// Style is embedded in <Placemark>
							style_elements = kml_styles[style_url];
						}

						// As Google Maps GPolygon doesn't support inner boundaries (a doughnut-shape polygon),
						// need to approximate them to be polygons with land color.
						if (placemarks[i].getElementsByTagName("coordinates")[p].parentNode.parentNode.tagName == "innerBoundaryIs") {
							poly_opacity = 1.0;
							line_color = "#17174D";
							poly_color = "#516B4E";
						} else {
							poly_color_style = style_elements["PolyStyleColor"];
							poly_opacity = parseInt(poly_color_style.substr(0,2), 16) / 255;
							poly_color = "#" + poly_color_style.substr(6,2) + poly_color_style.substr(4,2) + poly_color_style.substr(2,2);
							line_color_style = style_elements["LineStyleColor"];
							line_opacity = parseInt(line_color_style.substr(0,2), 16) / 255;
							line_color = "#" + line_color_style.substr(6,2) + line_color_style.substr(4,2) + line_color_style.substr(2,2);
							legend_line_color = line_color;
							legend_poly_color = poly_color;
						}
						// Call appropriate constructor depending on feature type.

						if (placemarks[i].getElementsByTagName("Polygon").length > 0) {
							placemark_geometry = "Polygon";
							var polygon = new GPolygon(points, line_color, 1, 0.9, poly_color, poly_opacity);
							map.addOverlay(polygon);
							polygons.push(polygon);
							polygon_names.push(name);
							polygonlabels.push("<h3>" + name + "</h3>\n" + desc);
						} else {
							placemark_geometry = "Polyline";
							var polyline = new GPolyline(points, line_color, 4 , line_opacity);
							map.addOverlay(polyline);
						}
					}
				}

				add_legend_item(placemarks[i], placemark_geometry, icon, legend_line_color, legend_poly_color);
			}

			draw_legend();
		}
	}
	request.send(null);
}

function createMarker(point, place, desc, icon, opt){
		var marker = new GMarker(point, icon);
	var html = '<p><B>'+place+'</B></p><p>'+desc+'</p>'; //put here the contents of the HtmlInfoWindow
	GEvent.addListener(marker, 'click', function() {
		marker.openInfoWindowHtml(html,opt);
	});
	return marker;
}


function parse_style(style) {
	var style_elements = [];

	// Get color of <PolyStyle> and <LineStyle>
	var arry_styles = new Array("PolyStyle", "LineStyle");
	for (j = 0; j < arry_styles.length; j++) {
		var theStyle = style.getElementsByTagName(arry_styles[j]);
		if (theStyle.length > 0) {
			var color = theStyle[0].getElementsByTagName("color");
			if (color.length > 0) {
				color_value = color[0].childNodes[0].nodeValue;
			} else {
				color_value = "ffffffff";
			}
		} else {
			color_value = "ffffffff";
		}
		style_elements[arry_styles[j] + 'Color'] = color_value;
	}
	return style_elements;

}
