//==================================================
//
//	A2EKMLImporter.js
//	copyright 2007 Arc2Earth LLC
//
//	This is for use with Arc2Earth viewers only
//
//==================================================


function A2EClient()
{
	this.Documents = [];
	this.NetworkLinks = [];
	this.IsLoading = false;
	this.Transport = new A2ETransport(this, this.AddKMLDocumentCallback);
	this.AddCallback = null;
	
	//setup timer (every second)
	this.IsPaused = false;	//possible concurrency issue
	//window.setInterval(this.TimerCallback(this, this.OnTimer), 1000);
}

A2EClient.prototype.AddKMLDocument = function(url, callback)
{
	//query the server for the KML, this may contain 
	this.AddCallback = callback;
	this.Transport.Execute(url);
	
}

A2EClient.prototype.ParseXML = function(xml)
{
	
    if(typeof ActiveXObject!="undefined"&&typeof GetObject!="undefined")
	{
		var xmldoc = null;
        xmldoc=new ActiveXObject("Microsoft.XMLDOM");
        xmldoc.loadXML(xml);
		return xmldoc;
    }
    if(typeof DOMParser!="undefined"){
		return(new DOMParser).parseFromString(xml,"text/xml");

    }
	return null;
}

A2EClient.prototype.ParseXMLValue = function(node)
{
    if(!node){return"";}

    var v="";
    if(node.nodeType==3||node.nodeType==4||node.nodeType==2)
	{
        v+=node.nodeValue
    }
    else if(node.nodeType==1||node.nodeType==9||node.nodeType==11)
	{
		var c = 0;
		if (node.childNodes != null){c=node.childNodes.length;}
        for(var i=0;c<c;i++)
		{
            v+=arguments.callee(node.childNodes[i])
        }
            
    }
    return v;
}


A2EClient.prototype.AddKMLDocumentCallback = function(callback)
{
	//parse the resulting KML
	this.IsPaused = true;
	//alert("Len: " + callback.Request.responseText.length + "  " + callback.Request.responseText);
	
	var xmlDoc = this.ParseXML(callback.Request.responseText);
	
	var docs = xmlDoc.documentElement.getElementsByTagName("Document");
	if (docs.length > 0) 
	{
		for (var i=0; i<docs.length; i++) 
		{
			var doc = new A2EFolder();
			doc.Create(docs[i], null);
			this.Documents.push(doc);
		}
		
	} else {
		
		//no KML Document tag, use first Folder
		//alert("no kml " + xmlDoc.firstChild.tagName + " Val: " + xmlDoc.firstChild.firstChild.tagName);
		var rootNode = xmlDoc.firstChild;
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			
			if (node.tagName == "Folder")
			{
				var doc = new A2EFolder();
				doc.Create(node, null);
				this.Documents.push(doc);					
				break;
			}
		}
	}
	this.IsPaused = false;
	
	this.AddCallback();
}


A2EClient.prototype.RemoveKMLDocument = function(index)
{
	var doc = this.Documents[index];
	doc.Dispose();
	this.Documents.pop();
}

A2EClient.prototype.ClearKMLDocuments = function(index)
{
	for (var i=this.Documents.length-1; i>=0; i--)
	{
		var doc = this.Documents[i];
		doc.Dispose();
		this.Documents.pop();
	}
	
}


A2EClient.prototype.TimerCallback = function(instance, delegate)
{
    var wrap=function(){return delegate.apply(instance,arguments)}
    return wrap;	
}
A2EClient.prototype.OnTimer = function()
{
	//called every second, decide which NetworkLinks need refreshing
	if ((!this.IsLoading) && (!this.IsPaused))
	{
		this.IsLoading = true;
		for (var i=0; i<this.NetworkLinks.length; i++)
		{
			var nl = this.NetworkLinks[i];
			if (nl.Url != null) {
				if ((nl.Url.RefreshMode == "onInterval") && (nl.Url.ViewRefreshMode != "onStop")) 
				{
					nl.Update();
				}
			}
		}
		
		for (var i=0; i<this.Documents.length; i++)
		{
			var doc = this.Documents[i];
			doc.Update(false);
		}
		
		this.IsLoading = false;
	}
}


//================================================
//KML Document
function A2EFolder()
{

	this.Name = "";
	this.Description = "";
	this.Visibility = true;
	this.LookAt = null;
	this.HasLookedAt = false;
	this.Styles = [];
	this.StyleMaps = [];
	this.Placemarks = [];
	this.Folders = [];
	this.NetworkLinks = [];
	this.MarkerLoader = new A2EMarkerLoader();
	
}

A2EFolder.prototype.Create = function(node, document)
{
	this.Document = document;
	this.IsDocument = (this.Document == null);
	this.Init(node);	
}

A2EFolder.prototype.Init = function(rootNode)
{
	//try {
		var pmc = 0;
		var doc = this.Document;
		if (this.IsDocument) {doc = this;}	
		
		
		//===================
		//Process Styles first
		var node;
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "Style": 
					this.Styles.push(new A2EStyle(node)); 
					break;
				case "StyleMap": 
					this.StyleMaps.push(new A2EStyleMap(node));
					break;			
			}
		}
		
		
		//=====================
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "name": this.Name=node.firstChild.nodeValue; break;
				case "description": this.Description=node.firstChild.nodeValue; break;
				case "visibility": this.Visibility=A2EParseBool(node.firstChild.nodeValue); break;
			

					
				case "Placemark": 
					pmc = pmc+1;
					var pm = new A2EPlacemark(doc, node);
					this.Placemarks.push(pm); 
					doc.Placemarks.push(pm);
					break;
					
				case "Folder": 
					var f = new A2EFolder();
					f.Create(node, doc, null, this.ServerUrl);
					this.Folders.push(f); 
					break;
					
				case "NetworkLink": 
					//this.NetworkLinks.push(new A2ENetworkLink(doc.ServerUrl, doc.Map, node)); 
					break;
			}
		
		}		
		
	
	//} catch (exp) 
	//{
	//	alert("A2EFolder.Init: " + exp);
	//}
}

A2EFolder.prototype.Dispose = function()
{
	for (var i=0; i<this.NetworkLinks.length; i++)
	{
		var nl = this.NetworkLinks[i];
		nl.Dispose();
	}
	
	for (var i=0; i<this.Folders.length; i++)
	{
		var f = this.Folders[i];
		f.Dispose();
	}	
	
	alert("A2EFolder.Dipose Placemarks: " + this.Placemarks.length);
	for (var i=0; i<this.Placemarks.length; i++) 
	{
		var pm = this.Placemarks[i];
		pm.RemoveMarkers(this.GetMap());
		pm.Dispose();

	}	
	
	this.Document = null;
	this.Map = null;	
}


A2EFolder.prototype.AddStyle = function(style)
{
	this.Styles.push(style);
}
A2EFolder.prototype.GetStyleByName = function(id, state)
{
	var name = id;
	
	//try stylemaps first
	for (var i=0; i<this.StyleMaps.length; i++)
	{	
		
		var sm = this.StyleMaps[i];
		if (sm.ID == id)
		{
			for (var k=0; k<sm.Pairs.length; k++) 
			{
				var p = sm.Pairs[k];
				
				//alert(sm.ID + "  " + state + "  " + p.Key);
				if (state == "undefined")
				{
					//normal state
					if (p.Key == "normal")
					{
						name = p.StyleUrl.replace("#","");
						alert(id + "  " + state + "  " + p.Key + " name: " + name);
						break;
					}
				} else {
					if (p.Key == state)
					{
						name = p.StyleUrl.replace("#","");
						break;
					}
				}
			}
			break;
		}
	}
	
	
	//now look in the real styles
	//alert("GetStyleByName " + name + "  " + state);
	for (var i=0; i<this.Styles.length; i++)
	{
		var s = this.Styles[i];
		if (s.ID == name)
		{
			return s;
		}
	}
	return null;
}

A2EFolder.prototype.Update = function(isViewBased)
{
	for (var i=0; i<this.NetworkLinks.length; i++)
	{
		var nl = this.NetworkLinks[i];
		if (isViewBased) 
		{
			if (nl.Url.ViewRefreshMode == "onStop") 
			{
				nl.Update();
			}
		} else {
			if (nl.Url != null) {
				if ((nl.Url.RefreshMode == "onInterval") && (nl.Url.ViewRefreshMode != "onStop")) {
					nl.Update();
				}
			}		
		}
	}
	
	for (var i=0; i<this.Folders.length; i++)
	{
		var f = this.Folders[i];
		f.Update(isViewBased);
	}
	
}

A2EFolder.prototype.FindPlacemark = function(searchVal, useDescriptions)
{
	var result = null;
	for (var i=0; i<this.Placemarks.length; i++)
	{
		var pm = this.Placemarks[i];
		if (pm.Name.indexOf(searchVal) > -1) 
		{
			result = pm;
			break;
		}
		
		if ((useDescriptions) && (pm.Description != ""))
		{
			if (pm.Description.indexOf(searchVal) > -1) 
			{
				result = pm;
				break;
			}			
		}
	}
	return result;
}

function A2EMarkerLoader()
{
	this.Markers = [];
}
A2EMarkerLoader.prototype.addOverlay = function(marker)
{
	this.Markers.push(marker);
}
A2EMarkerLoader.prototype.Dispose = function()
{
	this.Markers = [];
}



//============================================
//KMLPlacemark
function A2EPlacemark(doc, pmNode)
{
	this.Document = doc;
	this.Markers = [];	//allow multigeometry
	this.Name = "";
	this.Description = "";
	this.StyleUrl = "";
	this.Style = null;	//usually null
	this.StyleMap = null;	
	this.LookAt = null;

	this.Points = [];
	
	
	this.Init(pmNode);
	

}

A2EPlacemark.prototype.Init = function(rootNode)
{
	try {
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "name": this.Name=node.firstChild.nodeValue; break;
				case "description": this.Description=node.firstChild.nodeValue; break;
				case "styleUrl": this.StyleUrl=node.firstChild.nodeValue; break;
				case "Style": this.Style = new A2EStyle(node); break;
				case "StyleMap": this.Style = new A2EStyleMap(node); break;
				//case "LookAt": this.LookAt = new A2ELookAt(node); break;
				case "Point": this.FromPoint(node); break;
				//case "LineString": this.FromLineString(node, false); break;
				//case "Polygon": this.FromLineString(node, true); break;
				//case "GeometryCollection": this.FromMultiGeometry(node); break;
				//case "MultiGeometry": this.FromMultiGeometry(node); break;
			}
		
		}		
	
	} catch (exp) 
	{
		alert(exp);
	}
}

A2EPlacemark.prototype.Dispose = function()
{
	this.Document = null;
	this.Style = null;
	this.Markers = {};
}

A2EPlacemark.prototype.toString = function()
{
	return this.Name;
}

A2EPlacemark.prototype.FromPoint = function(rootNode)
{
	var pts = null;
	for (var i=0; i<rootNode.childNodes.length; i++)
	{
		var node = rootNode.childNodes[i];
		switch (node.tagName)
		{	
			case "coordinates": pts = this.FromCoordinates(node.firstChild.nodeValue); break;
		}
		
	}			
		
	if (pts != null) 
	{
	
		var style = this.Style; //localstyle, rare
		var icon = null;
		//this.StyleUrl = "A";
		if ((style == null) && (this.StyleUrl != ""))
		{
			style = this.Document.GetStyleByName(this.StyleUrl.replace("#", ""), "normal");
			if (style != null)
			{
				icon = style.IconStyle.Icon;
				
			}
		} else {
			icon = style.IconStyle.Icon;
		}
	
		
		var marker = new A2EMarker(pts[0], icon.Href, this, style);
		this.Markers.push(marker);
	}
}


A2EPlacemark.prototype.FromCoordinates = function(coords)
{
	//input - KML coordinates string
	//output - array of GPoint objects
	//weird carriage returns and missing white space in 
	//some KML coordinate feeds
	var pts = [];
	//alert(coords);
	coords = coords.replace(/[\r\n]+/g, " ");
	
	var vals = coords.split(" ");
	for (var i=0; i<vals.length; i++)
	{
		var c = vals[i].split(",");
		var pt = new A2EPoint(c[0],c[1]);
		if ((pt.x != 0) && (pt.y != 0))
		{
			pts.push(pt);
		}
	}
	return pts;
}


function A2EPoint(x,y)
{
	this.x = x;
	this.y = y;
}

function A2EMarker(pt, iconUrl, pm, style)
{
	this.Point = pt;
	this.IconUrl = iconUrl;
	this.Placemark = pm;
	this.Style = style;
}


//================================================================================
//	Styles

//============================================
//KMLStyleMap
function A2EStyleMap(node)
{
	this.ID = "";
	this.Pairs = [];
	this.Init(node);
}

A2EStyleMap.prototype.Init = function(rootNode)
{
	try {
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "Pair": 
					this.Pairs.push(new A2EStylePair(node)); 
					break;
			}
		}		
		
		this.ID = rootNode.getAttribute("id");
		
	} catch (exp) 
	{
		alert(exp);
	}
}

//============================================
//KMLStyleMap
function A2EStylePair(node)
{
	this.StyleUrl = "";
	this.Key = "normal";
	
	this.Init(node);
	
}

A2EStylePair.prototype.Init = function(rootNode)
{

	try 
	{
		for (var k=0; k<rootNode.childNodes.length; k++)
		{
			var n2 = rootNode.childNodes[k];
			switch (n2.tagName)
			{
				case "styleUrl": this.StyleUrl=n2.firstChild.nodeValue;break;
				case "key": this.Key=n2.firstChild.nodeValue;break;
			}
		}
		
	} catch (exp) 
	{
		//alert(exp);
	}
}


//============================================
//KMLStyle
function A2EStyle(node)
{
	this.ID = "";
	this.IconStyle = null;
	this.LabelStyle = null;
	this.LineStyle = null;
	this.PolyStyle = null;
	this.StyleType = "";
	this.Tag = null;
	this.Init(node);
}

A2EStyle.prototype.Init = function(rootNode)
{
	try {
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "IconStyle": this.IconStyle = new A2EIconStyle(node); this.StyleType="icon"; break;
				case "LabelStyle": this.LabelStyle = new A2ELabelStyle(node); this.StyleType="label"; break;
				case "LineStyle": this.LineStyle = new A2ELineStyle(node); this.StyleType="line"; break;
				case "PolyStyle": this.FillStyle = new A2EPolyStyle(node); this.StyleType="poly"; break;
			}
		}		
		this.ID = rootNode.getAttribute("id");
		
	} catch (exp) 
	{
		alert(exp);
	}
}
A2EStyle.ConvertColor = function(kmlColor)
{	
	//KML color is in AABBGGRR format
	//HTML color is #RRGGBB
	var c = kmlColor.substring(2);
	var r = c.substring(4);
	var g = c.substring(2,4);
	var b = c.substring(0,2);
	c = "#" + r + g + b;
	return c;
}


//============================================
//KMLIconStyle
function A2EIconStyle(node)
{
	this.Color = "fffffff";
	this.ColorMode = "random";
	this.Scale = 1;
	this.Heading = 0;
	this.Icon = null;
	this.Init(node);
	
}



A2EIconStyle.prototype.Init = function(rootNode)
{
	try {
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "color": this.Color = node.firstChild.nodeValue; break;
				case "colorMode": this.ColorMode = node.firstChild.nodeValue; break;
				case "scale": this.Scale = parseFloat(node.firstChild.nodeValue); break;
				case "heading": this.Heading = parseFloat(node.firstChild.nodeValue); break;
				case "Icon": this.Icon = new A2EIcon(node); break;
			}
		}		
	} catch (exp) 
	{
		alert(exp);
	}
}

//==========================================
//KMLIcon
function A2EIcon(node)
{
	this.Href = "";
	this.X = 0;
	this.Y = 0;
	this.Width = 16;
	this.Height = 16;
	this.GIcon = null;
	this.Init(node);
}

A2EIcon.prototype.Init = function(rootNode)
{
	try 
	{
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "href": this.Href = node.firstChild.nodeValue; break;
				case "x": this.X = parseInt(node.firstChild.nodeValue); break;
				case "y": this.Y = parseInt(node.firstChild.nodeValue); break;
				case "w": this.Width = parseInt(node.firstChild.nodeValue); break;
				case "h": this.Height = parseInt(node.firstChild.nodeValue); break;
			}
		}		
		this.ParseHref(this.Href);
		
	} catch (exp) 
	{
		alert(exp);
	}
}

A2EIcon.prototype.ParseHref = function(href)
{
	var root = "root://";
	if (href.indexOf(root) > -1) 
	{
		//we need to redirect the url
		href = A2EClient.ServerUrl + "/A2EGateway.ashx?method=GetIcon&x=" + this.X + "&y=" + this.Y + "&file=" + href;
	}
	//alert(href);
	this.Href = href;
	
	var w2 = this.Width/2;
	var h2 = this.Height/2;
	
	//var icon = new GIcon();
	//icon.image = this.Href;
	//icon.shadow = "http://www.google.com/mapfiles/shadow50.png";
	//icon.shadow = A2EClient.ServerUrl + "/Images/shadow.gif";	//no shadow
	//icon.iconSize = new GSize(this.Width, this.Height);
	//icon.shadowSize = new GSize(1, 1);
	//icon.iconAnchor = new GPoint(w2, h2);
	//icon.infoWindowAnchor = new GPoint(w2, h2);

	
	//this.GIcon = icon;
}

A2EIcon.prototype.GetStyleString = function()
{
	return null;
}

//============================================
//KMLLabelStyle
function A2ELabelStyle(node)
{
	this.Color = "fffffff";
	this.ColorMode = "random";
	this.Scale = 1;
	this.Init(node);
	
}

A2ELabelStyle.prototype.Init = function(rootNode)
{
	try {
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "color": this.Color = node.firstChild.nodeValue; break;
				case "colorMode": this.ColorMode = node.firstChild.nodeValue; break;
				case "scale": this.Scale = parseFloat(node.firstChild.nodeValue); break;
			}
		}		
	} catch (exp) 
	{
		alert(exp);
	}
}
A2ELabelStyle.prototype.GetStyleString = function()
{
	return null;
}

//============================================
//KMLLineStyle
function A2ELineStyle(node)
{
	this.Color = "fffffff";
	this.Width = 1;
	this.Init(node);
	
}

A2ELineStyle.prototype.Init = function(rootNode)
{
	try {
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "color": this.Color = node.firstChild.nodeValue; break;
				case "width": this.Width = parseFloat(node.firstChild.nodeValue); break;
			}
		}		
	} catch (exp) 
	{
		alert(exp);
	}
}
A2ELineStyle.prototype.GetStyleString = function()
{
	var ss = { color: ' + A2EStyle.ConvertColor(this.Color) + ' };	//width: " + this.Width +
	
	var c = A2EStyle.ConvertColor(this.Color);
	ss = {color: c, width: 1};
	return ss;
}



//============================================
//KMLFillStyle
function A2EPolyStyle(node)
{
	this.Color = "fffffff";
	this.ColorMode = "random";
	this.Fill = false;
	this.Outline = true;
	this.Init(node);
	
}

A2EPolyStyle.prototype.Init = function(rootNode)
{
	try {
		for (var i=0; i<rootNode.childNodes.length; i++)
		{
			var node = rootNode.childNodes[i];
			switch (node.tagName)
			{
				case "color": this.Color = node.firstChild.nodeValue; break;
				case "colorMode": this.ColorMode = node.firstChild.nodeValue; break;
				case "fill": this.Fill = A2EParseBool(node.firstChild.nodeValue); break;
				case "outline": this.Outline = A2EParseBool(node.firstChild.nodeValue); break;
			}
		}		
	} catch (exp) 
	{
		alert(exp);
	}
}
A2EPolyStyle.prototype.GetStyleString = function()
{
	return null;
}








//=============================================
//helpers
function A2EParseBool(val)
{
	if (val == "0"){return false;}
	return true;
}