/**
 * File - SimpleKml.js
 * Package - Geolive. http://www.geolive.ca
 * Created - Dec 18 09
 * Author - Nick Blackwell UBCO, Jon Corbett Phd.
 * 
 * License - Creative Commons Attribution-Share Alike 2.5 Generic http://creativecommons.org/licenses/by-sa/2.5/
 *
 * Description - Defines a class, SimpleParser which is a container for static kml parsing methods. 
 * This kml parser assumes Mootools and GoogleMaps API are available.
 * 
 * Dependencies - GoogleMaps API, Mootools 1.11, Debug.js (specifically mm_debug method)
 */

/**
 * SimpleParser Class parses standard kml documents and returns objects representiong it's data. 
 * the optional transformations define the data within these objects, ie, documentTransform (for Geolive)
 * will create a Layer object from its contents, and pull out the items which will be transformed aswell as MapItems.
 * 
 * 
 * 
 */ 
var SimpleParser=new Class({
	options:
	{
	/** markerParams=[name, description, coordinates, icon, data]; xmlSnippet=xmlNode*/
	markerTransform:function(markerParams,xmlSnippet)
	{
	return markerParams;
	},
	/** polygonParams=[name, description, coordinates, lineWidth, lineColor, lineOpacity, fillColor, fillOpacity]; xmlSnippet=xmlNode*/
	polygonTransform:function(polygonParams,xmlSnippet)
	{
		return polygonParams;
	},
	/** lineParams=[name, description, coordinates, lineWidth, lineColor, lineOpacity]; xmlSnippet=xmlNode*/
	lineTransform:function(lineParams,xmlSnippet)
	{
		return lineParams;
	},
	/** folderParams=[name, description, markers, polygons, lines, networklinks, data]; xmlSnippet=xmlNode*/
	folderTransform:function(folderParams,xmlSnippet){
		return folderParams;
	},
	/** networklinkParams=[name, description, url, data]; xmlSnippet=xmlNode*/
	networklinkTransform:function(networklinkParams,xmlSnippet){

		return networklinkParams;
	},
	/** documentParams=[name, description, folders, markers, polygons, lines, networklinks, data]; xmlSnippet=xmlNode*/
	documentTransform:function(documentParams,xmlSnippet){

		return documentParams;
	}
	},
	initialize:function(options){
		var me=this;
		me.setOptions(options);
	},
	parseDocuments:function(snippet){
		var me=this;
		var documents=[];
		var documentData=SimpleParser.ParseDomDocuments(snippet);
		$each(documentData, function(p){
			documents.push(me.options.documentTransform(p, snippet));
		});
		return documents;
	},
	parseFolders:function(snippet){
		var me=this;
		var folders=[];
		var folderData=SimpleParser.ParseDomFolders(snippet);
		$each(folderData, function(p){
			folders.push(me.options.folderTransform(p, snippet));
		});
		return folders;
	},
	parseMarkers:function(snippet){
		var me=this;
		var markers=[];
		var markerData=SimpleParser.ParseDomMarkers(snippet);
		$each(markerData, function(p){
			markers.push(me.options.markerTransform(p, snippet));
		});
		return markers;
	},
	parsePolygons:function(snippet){
		var me=this;
		var polygons=[];

		var polygonData=SimpleParser.ParseDomPolygons(snippet);
		$each(polygonData, function(p){
			polygons.push(me.options.polygonTransform(p, snippet));
		});
		return polygons;
	},
	parseLines:function(snippet){
		var me=this;
		var lines=[];
		var lineData=SimpleParser.ParseDomLines(snippet);
		$each(lineData, function(p){
			lines.push(me.options.lineTransform(p, snippet));
		});
		return lines;
	},
	parseNetworklinks:function(snippet){
		var me=this;
		var links=[];
		var linkData=SimpleParser.ParseDomLinks(snippet);
		$each(linkData, function(p){
			links.push(me.options.networklinkTransform(p, snippet));
		});
		return links;
	}
});

SimpleParser.implement(new Options());

SimpleParser.ParseDomDocuments=function(xmlDom){
	var docs=[];
	var docsDomNodes=xmlDom.getElementsByTagName('Document');
	var i;
	for(i=0;i<docsDomNodes.length;i++){
		var node=docsDomNodes.item(i);
		var docsData=$merge(SimpleParser.ParseDomDoc(node),SimpleParser.ParseNonSpatialDomData(node,{}));
		var transform=function(options){
			return options;
		};
		docs.push(transform(docsData));
	}
	return docs;
};

SimpleParser.ParseDomDoc=function(xmlDom){
	return {};
};

SimpleParser.ParseDomFolders=function(xmlDom){
	var folders=[];
	var folderDomNodes=SimpleParser.ParseDomItems(xmlDom,'Folder');
	var i;
	for(i=0;i<folderDomNodes.length;i++){
		var node=folderDomNodes[i];
		var folderData=$merge(SimpleParser.ParseDomFolder(node),SimpleParser.ParseNonSpatialDomData(node,{}));
		var transform=function(options){
			return options;
		};
		folders.push(transform(folderData));
	}
	return folders;
};

SimpleParser.ParseDomDoc=function(xmlDom){
	return {};
};

SimpleParser.ParseDomLinks=function(xmlDom){
	var links=[];
	var linkDomNodes=xmlDom.getElementsByTagName('NetworkLink');
	var i;
	for(i=0;i<linkDomNodes.length;i++){
		var node=linkDomNodes.item(i);
		var linkData=$merge(SimpleParser.ParseDomLink(node),SimpleParser.ParseNonSpatialDomData(node,{}));

		var transform=function(options){
			return options;
		};
		links.push(transform(linkData));
	}
	return links;
};
SimpleParser.ParseDomFolder=function(xmlDom){
	return {};
};
SimpleParser.ParseDomLink=function(xmlDom){

	var urls=xmlDom.getElementsByTagName('href');
	var link={};
	if(urls.length>0)
	{
		var url=urls.item(0);
		link.url=GXml.value(url);
	}
	return link;
};

SimpleParser.ParseDomLines=function(xmlDom){
	var lines=[];
	var lineDomNodes=SimpleParser.ParseDomItems(xmlDom,'LineString');
	var i;
	for(i=0;i<lineDomNodes.length;i++){

		var node=lineDomNodes[i];
		var coords=SimpleParser.ParseDomCoordinates(node);
		var polygonData=$merge({
			coordinates:coords	//returns an array of GLatLngs
		},SimpleParser.ParseNonSpatialDomData(node,{}));

		var icon=SimpleParser.ParseDomStyle(node);
		var style=SimpleParser.ResolveDomStyle(icon, xmlDom);
		polygonData=$merge(polygonData,style);
		var transform=function(options){
			data={};
			if(options.name)data.name=options.name;
			if(options.tags)data.tags=options.tags;
			if(options.description)data.description=options.description;
			data.coordinates=options.coordinates;
			lStr=options.lineColor+"";

			var rgb=SimpleParser.KMLConversions.KMLColorToRGB(options.lineColor);
			data.lineOpacity=rgb.opacity;
			data.lineColor=rgb.color;

			data.lineWidth=options.lineWidth;
			return data;
		};
		//mm_debug(polygonData);
		lines.push(transform(polygonData));
	}

	return lines;
};

SimpleParser.ParseDomPolygons=function(xmlDom){
	var polygons=[];
	var polygonDomNodes=SimpleParser.ParseDomItems(xmlDom,'Polygon');

	var i;
	for(i=0;i<polygonDomNodes.length;i++){

		var node=polygonDomNodes[i];
		var coords=SimpleParser.ParseDomCoordinates(node);
		var polygonData=$merge({
			coordinates:coords	//returns an array of GLatLngs
		},SimpleParser.ParseNonSpatialDomData(node,{}));
		var icon=SimpleParser.ParseDomStyle(node);
		var style=SimpleParser.ResolveDomStyle(icon, xmlDom);
		polygonData=$merge(polygonData,style);
		var transform=function(options){
			data={};
			if(options.name)data.name=options.name;
			if(options.tags)data.tags=options.tags;
			if(options.description)data.description=options.description;
			data.coordinates=options.coordinates; //need same
			
			lStr=options.lineColor+"";
			
			var lineRGB=SimpleParser.KMLConversions.KMLColorToRGB(lStr);
			
			data.lineOpacity=lineRGB.opacity;
			//invert colors (BGR to RGB) why kml does this, ?
			data.lineColor=lineRGB.color;
			data.lineWidth=options.lineWidth;
			pStr=options.polyColor+"";
			var polyRGB=SimpleParser.KMLConversions.KMLColorToRGB(pStr);
			data.polyOpacity=((true||options.fill)?polyRGB.opacity:0);
			data.polyColor=polyRGB.color;		
			return data;
		};
		polygons.push(transform(polygonData));
	}
	return polygons;
};

SimpleParser.ParseDomMarkers=function(xmlDom){
	var markers=[];
	var markerDomNodes=SimpleParser.ParseDomItems(xmlDom,'Point');
	var i;
	for(i=0;i<markerDomNodes.length;i++){
		var node=markerDomNodes[i];
		var coords=SimpleParser.ParseDomCoordinates(node);
		var marker=$merge({
			coordinates:coords[0]	//returns an array of GLatLngs
		},SimpleParser.ParseNonSpatialDomData(node,{}));
		var icon=SimpleParser.ParseDomStyle(node);
		if(icon.charAt(0)=='#'){
			icon=SimpleParser.ResolveDomStyle(icon, xmlDom).icon;
		}
		if(icon)
		{
			marker['icon']=icon; //better to not have any hint of an icon (ie: icon:null) so that default can be used by caller
		}
		markers.push(marker);	
	}
	return markers;
};


SimpleParser.ParseDomCoordinates=function(xmlDom){
	var coordNodes=xmlDom.getElementsByTagName('coordinates');
	if(coordNodes.length==0){
		mm_debug("MapItem DOM Node did not contain coordinates");
		return null;
	}
	var node=coordNodes.item(0);
	var s=GXml.value(node);
	s=s.trim();
	coordStrings=s.split(' ');
	coordinates=[];
	$each(coordStrings,function(coord){
		var c=coord.split(',');
		if(c.length>1){
			coordinates.push(new GLatLng(c[1],c[0]));
		}

	});


	return coordinates;	
};


SimpleParser.ParseNonSpatialDomData=function(xmlDom,options){
	var config=$merge({
		maxOffset:2
	},options);

	var data={
			name:"",
			description:null,
			tags:{}
	};
	var names=xmlDom.getElementsByTagName('name');
	var i;
	for(i=0;i<names.length;i++){
		if(SimpleParser.WithinOffsetDom(xmlDom, names.item(i),config.maxOffset)){
			data.name =(GXml.value(names.item(i)));
			break;
		}
	}
	var descriptions=xmlDom.getElementsByTagName('description');
	for(i=0;i<descriptions.length;i++){
		if(SimpleParser.WithinOffsetDom(xmlDom,descriptions.item(i), config.maxOffset)){
			data.description =(GXml.value(descriptions.item(i)));
			break;
		}
	}
	var tags={};
	var extendedDatas=xmlDom.getElementsByTagName('ExtendedData');
	for(i=0;i<extendedDatas.length;i++){
		if(SimpleParser.WithinOffsetDom(xmlDom,extendedDatas.item(i),config.maxOffset)){
			var j;
			for(j=0;j<extendedDatas.item(i).childNodes.length;j++){
				var c=extendedDatas.item(i).childNodes.item(j);
				var t=SimpleParser.ParseTag(c);
				data.tags[t.name]=t.value;
			}
		}
	}
	return data;
};

SimpleParser.ParseTag=function(xmlDom){
	var tags={
			name:null,
			value:{}
	};
	switch(xmlDom.nodeName){

	case "Data": //TODO: add data tags...
		break;
	case "ID": tags.name="ID";
	tags.value=GXml.value(xmlDom);
	break;
	default: 
		tags.name=xmlDom.nodeName;
		tags.value=GXml.value(xmlDom);
		break;
	}
	return tags;
};

SimpleParser.WithinOffsetDom=function(parent, child, max){
	var current=child.parentNode;
	for(var i=0;i<max;i++){
		if(current.nodeName==(typeof(parent)=='string'?parent:parent.nodeName)){
			return true;
		}
		current=current.parentNode;
	}
	mm_debug('offset false');
	return false;
};
SimpleParser.ParseDomStyle=function(xmlDom, options){

	var config=$merge({
		defaultStyle:"default"
	},options);



	var styles=xmlDom.getElementsByTagName('styleUrl');
	var style=config.defaultStyle;
	if(styles.length==0){
		mm_debug("MapItem DOM Node did not contain style");
	}else{
		var node=styles.item(0);
		style=(GXml.value(node));
	}
	return style;
};
SimpleParser.ResolveDomStyle=function(style, xmlDom){
	var data={	
	};
	var name=(style.charAt(0)=='#'?style.substring(1, style.length):style);
	var styles=xmlDom.getElementsByTagName("Style");
	var i;
	for(i=0;i<styles.length;i++){

		var node=styles.item(i);
		var id=node.getAttribute("id");
		if(id==name){
			var lineStyles=node.getElementsByTagName('LineStyle');
			var polyStyles=node.getElementsByTagName('PolyStyle');
			var iconStyles=node.getElementsByTagName('href');
			if(lineStyles.length>0)
			{
				var lineStyle=lineStyles.item(0);
				var colors=lineStyle.getElementsByTagName('color');
				if(colors.length>0){
					var color=colors.item(0);
					data.lineColor=GXml.value(color);
				}	
				var widths=lineStyle.getElementsByTagName('width');
				if(widths.length>0){
					var width=widths.item(0);
					data.lineWidth=GXml.value(width);
				}	
			}
			if(polyStyles.length>0)
			{
				var polyStyle=polyStyles.item(0);
				var colors=polyStyle.getElementsByTagName('color');
				if(colors.length>0){
					var color=colors.item(0);
					data.polyColor=GXml.value(color);
				}				
				var outlines=polyStyle.getElementsByTagName('outline');
				if(outlines.length>0){
					var outline=outlines.item(0);
					var o=GXml.value(outline);
					data.outline=(o?true:false);
				}
			}
			if(iconStyles.length>0)
			{
				var iconStyle=iconStyles.item(0);
				var icon=GXml.value(iconStyle);
				data.icon=icon;
			}
		}
	}
	return data;
};
SimpleParser.ParseDomItems=function(xmlDom, tag){
	var tagName=tag||'Point';
	var items=[];
	var markerDomNodes=xmlDom.getElementsByTagName(tagName);
	var i;
	for(i=0;i<markerDomNodes.length;i++){
		var node=markerDomNodes.item(i);
		var parent=(node.parentNode.nodeName=='Placemark'?node.parentNode:(node.parentNode.parentNode.nodeName=='Placemark'?node.parentNode.parentNode:null));
		if(parent==null){
			mm_debug('Failed to find ParentNode for Element - '+tagName);
		}else{
			items.push(parent);
		}
	}
	return items;
};

SimpleParser.KMLConversions={
		KMLColorToRGB:function(colorString)
		{
	var colorStr=colorString.replace('#','');
	while(colorStr.length<6){
		colorStr='0'+colorStr;
	} //make sure line is dark!
	while(colorStr.length<8){
		colorStr='F'+colorStr;
	}//make sure opacity is a large fraction
	if(colorStr.length>8){
		colorStr=colorStr.substring(0,8);
	}
	var color=colorStr.substring(6,8)+colorStr.substring(4,6)+colorStr.substring(2,4);		
	var opacity=((parseInt(colorStr.substring(0, 2), 16))*1.000)/(parseInt("FF", 16));
	
		rgbVal={
				color:'#'+color,opacity:opacity
		};

	return rgbVal;
		},
		RGBColorToKML:function(rgb, opacity){

			var colorStr=rgb.replace('#','');
			while(colorStr.length<6){colorStr='0'+colorStr;} //make sure line is dark!
			if(colorStr.length>6){colorStr=colorStr.substring(0,6);}

			if($defined(opacity)){
				if(opacity>=0.0&&opacity<=1.0){
					var opacityNum=opacity;	
				}else if(parseInt(opacity)>=0.0&&parseInt(opacity)<=1.0){
					var opacityNum=parseInt(opacity);	
				}
			}
			if(!$defined(opacityNum)){
				var opacityNum=1.0;	
			}

			var opacityNum=(opacityNum*255.0);
			var opacityStr=opacityNum.toString(16);

			var kmlStr= opacityStr.substring(0,2)+""+colorStr.substring(4,6)+colorStr.substring(2,4)+colorStr.substring(0,2);
		
			return kmlStr;
		}

};

/**
 * Dynamic Marker Icon Loading Function
 * Currently, this is not used by Geolive (becuase this function is defined in GeoliveFactory),
 * however, this function takes marker data and creates a Google Marker (returned to callback)
 * with its icon size set dynamically after the icon image has loaded. and a linearly scaled
 * size can be calculated from the image.
 * 
 * note: the marker data may not (probly does though) match exactly with SimpleKml's 
 * parseMarker results. if not, define markerTransform (or transform it on your own)
 * 
 * 
 */
SimpleParser.Images={}; //stores {key}:img elements, or key:"loading" while waiting for an icon to load

SimpleParser.CreateMarker=function(data, callback){

	//default name description, note that description = null will trigger Content Module to query the server for data
	//this will allow kml files to be stripped down to nothing but coordinates names and styles. then content
	//will be fetched when needed.


	var config=$merge({
		name:MapFactory.ItemDisplayType("marker"), 
		description:null,
		icon:"http://maps.gstatic.com/intl/en_ca/mapfiles/ms/micons/yellow.png" /*default to yellow icon*/
	},data);
	/*draggable must be true to ever enable dragging*/
	var markerOptions = {
			draggable:true,
			title:config.name
	}; 
	//create a generic marker the actual marker details will be adjusted dynamically.
	var kmlIcon = new GIcon(G_DEFAULT_ICON);
	//check static icon cache (defined above CreateMarker function) for icon, if null then load it
	if(SimpleParser.Images[config.icon]==null){
		SimpleParser.Images[config.icon]="loading";	//set to loading to block concurrent calls for the same marker
		mm_debug("retrieving marker asset - "+config.icon);
		var image=new Asset.image(config.icon,{onload:function(){
			SimpleParser.Images[config.icon]=image;
		}});	
	}

	//function to run if/when icon is loaded
	var initMarker=function(){
		mm_debug("Marker - loading");

		kmlIcon.image=config.icon;	

		//dynamically adjust size of to match image file
		var w=SimpleParser.Images[config.icon].width;
		var h=SimpleParser.Images[config.icon].height;
		//scale GIcon so that it has a standard hieght and dynamic width
		//**stretched icons look horible
		kmlIcon.iconSize=new GSize(((w/h)*32),32);
		//call to new GIcon with updated (old) GIcon->kmlIcon will use kmlIcons sizes
		kmlIcon=new GIcon(kmlIcon);
		markerOptions.icon=kmlIcon; 

		marker = new GMarker(config.coordinates, markerOptions); //actually create the marker
		marker.description=config.description;
		marker.style=config.icon;	/*attach to marker so that i can check for it when saving and editing*/
		marker.disableDragging(); /*so markers aren't movable by anyone, call to enableDragging() restores functionality*/	
		callback(marker);
	};

	//initMarker is called imediatly if icon has been previously loaded, otherwise if checks every 100ms until it is loaded
	if(SimpleParser.Images[config.icon]=="loading"){
		var wait=function(){
			if(SimpleParser.Images[config.icon]!="loading"){
				mm_debug("Marker check - success");
				initMarker();
				return true;
			}
			//mm_debug("Marker check - failed");
			return false;			
		};
		runLater(wait, 100); 
	}else{
		//must already be loaded.
		initMarker();
	}
};




