
// Functions/structures for gathering/parsing data about the feeds
 

// Enumeration for airspace types
CLASS_B = 0;
CLASS_C = 1;
CLASS_D = 2;
INTERNATIONAL = 3;

// FeedData object

function FeedData(strICAO, strIATA, latitude, longitude, strFeedName, kAirspaceClass, rgStreams)
{
	this.strICAO = strICAO;
	this.strIATA = strIATA;
	this.longitude = longitude;
	this.latitude = latitude;
	this.strFeedName = strFeedName;
	this.kAirspaceClass = kAirspaceClass;
	this.rgStreams = rgStreams;
}


// StreamData object

function StreamData(strDesc, strStreamUrl)
{
	this.strDesc = strDesc;
	this.strStreamUrl = strStreamUrl;
}


// When feed data has been succesfully loaded, it will be stored here
var vrgFeedData = null;

// Our request object
var vAsyncReq;

// Toggles fake data instead of XHR to get real data
var vfUseFakeData = false;

// Where we retrieve the feed data
//var vUrlFeedData = "http://www.liveatc.net/feedmap/feeddata.xml";
var vUrlFeedData = "feeddata.xml";
//var vUrlFeedData = "http://www.hilbert.net/liveatc/feeddata.xml";

// Each stream URL has a name. To create the actual URL, surround the name with
// these URL pieces
vStreamURLStart = "http://d.liveatc.net/";
vStreamURLEnd = ".m3u";


/******************************************************************************
	Asynchronously starts a download of the feed data
	
	When complete, calls the OnFeedDataReady function
	
	Returns false if the download could not be started
******************************************************************************/
function RetrieveMapDataAsync()
{
	vAsyncReq = false;

    if (vfUseFakeData)
    {
        PopulateFakeData();
        OnFeedDataComplete(true);
        return;
    }

    if (window.XMLHttpRequest) {
    	try {
			vAsyncReq = new XMLHttpRequest();
        } catch(e) {
			vAsyncReq = false;
        }
    // branch for IE6/Windows ActiveX version
    } else if(window.ActiveXObject) {
       	try {
            vAsyncReq = new ActiveXObject("Microsoft.XMLHTTP");
      	} catch(e) {
        	try {
                vAsyncReq = new ActiveXObject("MSXML2.XMLHTTP.3.0");
        	} catch(e) {
          		vAsyncReq = false;
        	}
		}
    }

    if (vAsyncReq)
    {
		try
		{
			vAsyncReq.onreadystatechange = OnFeedDataReady;
			vAsyncReq.open("GET", vUrlFeedData, true);
			
			if (window.XMLHttpRequest)
			{
				vAsyncReq.send(null);
			}
			else
			{
				vAsyncReq.send();			
			}
		}
		catch (e)
		{
			alert(e.message);
			OnFeedDataComplete(false);
		}
    }
    else
    {
		OnFeedDataComplete(false);
    }
}

/******************************************************************************
	Gets the text for a particular XML node; used to abstract away browser
	differences
******************************************************************************/
function StrGetNodeText(node)
{
	return (node.text != null) ? node.text : node.textContent;
}

/******************************************************************************
	Given an XML root node, gets the property node underneath it and stores
	the value in strValueByRef
	
	The value is parsed as a string
	
	Returns false if the string could not be parsed, true otherwise
******************************************************************************/
function FGetFeedString(FeedNode, strPropName, strValueByRef)
{
	nodeItem = FeedNode.selectSingleNode(strPropName);
	if (nodeItem == null)
	{
		DebugAlert("Could not parse " + strPropName);
		return false;
	}
	
	strValueByRef.setValue(StrGetNodeText(nodeItem));

	return (strValueByRef.getValue() != null);
}

/******************************************************************************
	Given an XML root node, gets the property node underneath it and stores
	the value in strValueByRef
	
	The value is parsed as a float

	Returns false if the float could not be parsed, true otherwise
******************************************************************************/
function FGetFeedFloat(FeedNode, strPropName, rValueByRef)
{
	var strValueByRef = new OutputParam;
	if (!FGetFeedString(FeedNode, strPropName, strValueByRef))
	{
		return false;
	}

	rValueByRef.setValue(parseFloat(strValueByRef.getValue()));
	if (isNaN(rValueByRef.getValue()))
	{
		DebugAlert("Could not parse " + strPropName + " as float!");
		return false;
	}

	return true;
}

/******************************************************************************
	Since a particular airport can have multiple streams, this function
	parses those streams and places them in the array of StreamData objects
	(rgStreams)
	
	Returns false if something goes horribly, true otherwise
	
	If we can't parse a particular stream, we'll just skip it
******************************************************************************/
function FGetStreams(FeedNode, rgStreamData)
{
	if (rgStreamData == null)
	{
		return false;
	}

	var rgStreams = FeedNode.getElementsByTagName("Stream");
	
	for (iStream=0; iStream<rgStreams.length; ++iStream)
	{			
		var strDescription = "";
		var strURLName = "";

		var strValueByRef = new OutputParam;
		
		if (!FGetFeedString(rgStreams[iStream], "Description", strValueByRef))
			continue;
		strDescription = strValueByRef.getValue();
		
		if (!FGetFeedString(rgStreams[iStream], "URLName", strValueByRef))
			continue;
		strURLName = vStreamURLStart + strValueByRef.getValue() + vStreamURLEnd;

		rgStreamData.push(new StreamData(strDescription, strURLName));
		
	}  // for each stream under the given XML node

	return true;
}


/******************************************************************************
	Parses a feed data XML file and stores it in vrgFeedData
	
	Expect XML to be in this form:
	<Airport>
		<ICAO>KDFW</ICAO>
		<IATA>DFW</IATA>
		<Latitude>32.8968281</Latitude>
		<Longitude>-97.0379958</Longitude>
		<FriendlyName>Dallas/Forth Worth</FriendlyName>
		<AirspaceClass>B</AirspaceClass>
		<Stream>
			<Description>Tower (East)</Description>
			<URLName>dfw_twr_east</URLName>
		</Stream>
		<Stream>
			<Description>Tower (West)</Description>
			<URLName>dfw_twr_west</URLName>
		</Stream>
	</Airport>	
******************************************************************************/
function OnFeedDataReady()
{
	if (vrgFeedData != null)
	{
		DebugAlert("Expect not to have previous feed data!");
	}

	// only if vAsyncReq shows "loaded"
	if (vAsyncReq.readyState == 4)
	{
		// only if "OK"
		if (vAsyncReq.status == 200)
		{
			var docRawFeedData = vAsyncReq.responseXML;
			
			DebugAlert(vAsyncReq.responseText);
           
			var rgAirports = docRawFeedData.getElementsByTagName("Airport");
	        
	        // Get ready for putting feed data in here
	        vrgFeedData = new Array();

			for (iAirport=0; iAirport<rgAirports.length; ++iAirport)
			{			
				var strICAO = "";
				var strIATA = "";
				var rLatitude = 0.0;
				var rLongitude = 0.0;
				var strFriendlyName = "";
				var kAirspaceClass = CLASS_B;
				var rgStreams = new Array();

				var strValueByRef = new OutputParam;
				
				if (!FGetFeedString(rgAirports[iAirport], "ICAO", strValueByRef))
					continue;
				strICAO = strValueByRef.getValue();
				
				if (!FGetFeedString(rgAirports[iAirport], "IATA", strValueByRef))
					continue;
				strIATA = strValueByRef.getValue();

				if (!FGetFeedString(rgAirports[iAirport], "FriendlyName", strValueByRef))
					continue;
				strFriendlyName = strValueByRef.getValue();

				if (!FGetFeedFloat(rgAirports[iAirport], "Latitude", strValueByRef))
					continue;
				rLatitude = strValueByRef.getValue();

				if (!FGetFeedFloat(rgAirports[iAirport], "Longitude", strValueByRef))
					continue;
				rLongitude = strValueByRef.getValue();
				
				if (!FGetFeedString(rgAirports[iAirport], "AirspaceClass", strValueByRef))
					continue;
				switch (strValueByRef.getValue())
					{
					case "B" :
						kAirspaceClass = CLASS_B;
						break;
					case "C" :
						kAirspaceClass = CLASS_C;
						break;
					case "D" :
						kAirspaceClass = CLASS_D;
						break;
					case "International" :
						kAirspaceClass = INTERNATIONAL;
						break;
					default :
						kAirspaceClass = CLASS_D;
						DebugAlert("Invalid airspace class: " + strValueByRef.getValue());
						break;
					}

				if (!FGetStreams(rgAirports[iAirport], rgStreams))
					continue;

				vrgFeedData.push(new FeedData(strICAO, strIATA, rLatitude, rLongitude, strFriendlyName, kAirspaceClass, rgStreams));
	             
			}  // for each feed in the XML doc

			DebugAlert(vrgFeedData.length);
			DebugAlert(vrgFeedData[0].strICAO);
			DebugAlert(vrgFeedData[0].strIATA);
			DebugAlert(vrgFeedData[0].longitude);
			DebugAlert(vrgFeedData[0].latitude);
			DebugAlert(vrgFeedData[0].kAirspaceClass);
			DebugAlert(vrgFeedData[0].rgStreams.length);
			DebugAlert(vrgFeedData[0].rgStreams[0].strDesc);
			DebugAlert(vrgFeedData[0].rgStreams[0].strStreamUrl);
			
			OnFeedDataComplete(vrgFeedData.length > 0);

        }  // end if the HTTP get was successful
        else
        {
			DebugAlert("There was a problem retrieving the XML data:\n" + vAsyncReq.statusText);              
            OnFeedDataComplete(false);
        }  // end if the HTTP get failed
        
    } // end if the HTTP get has finished
    
}  // end OnFeedDataReady function




function PopulateFakeData()
{
    vrgFeedData = [
	    new FeedData("KALB", "ALB", 42.7482678, -73.8016911, "Albany", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kalb.m3u")
	                 ]),
	    new FeedData("KBOS", "BOS", 42.3643611, -71.0051944, "Boston", CLASS_B,
	                 [
	                 new StreamData("Tower", "http://audio.liveatc.net:8012/bos_twr.m3u"),
	                 new StreamData("Clearance/Ground", "http://audio.liveatc.net:8012/bos_del_gnd.m3u"),
	                 new StreamData("Approach/Departure", "http://audio.liveatc.net:8012/bos_app_dep.m3u")
	                 ]),
	    new FeedData("KBDR", "BDR", 41.1634722, -73.1261667, "Bridgeport/NY App/ZBW", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kbdr.m3u")
	                 ]),
	    new FeedData("KBTV", "BTV", 44.4718611, -73.1532778, "Burlington", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kbtv.m3u")
	                 ]),
	    new FeedData("KBVI", "BVI", 40.7724808, -80.3914256, "Burlington", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kbvi.m3u")
	                 ]),
	    new FeedData("KCLE", "CLE", 41.4116897, -81.8497942, "Cleveland", CLASS_B,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/cleveland.m3u")
	                 ]),
	    new FeedData("KCLT", "CLT", 35.2140111, -80.9431258, "Charlotte Intl", CLASS_B,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kclt.m3u")
	                 ]),
	    new FeedData("KCMH", "CMH", 39.9979722, -82.8918889, "Columbus", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kcmh.m3u")
	                 ]),
	    new FeedData("KCVG", "CVG", 39.0461544, -84.6639536, "Cincinnati/Northern KY", CLASS_B,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kcvg.m3u")
	                 ]),
	    new FeedData("KDBQ", "DBQ", 42.4020000, -90.7094722, "Dubuque", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kdbq.m3u")
	                 ]),
	    new FeedData("KDFW", "DFW", 32.8968281, -97.0379958, "Dallas/Forth Worth", CLASS_B,
	                 [
	                 new StreamData("Tower (East)", "http://audio.liveatc.net:8012/dfw_twr_east.m3u"),
	                 new StreamData("Tower (West)", "http://audio.liveatc.net:8012/dfw_twr_west.m3u")
	                 ]),
	    new FeedData("KDTW", "DTW", 42.2124444, -83.3533889, "Detroit Metro", CLASS_B,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kdtw.m3u")
	                 ]),
	    new FeedData("KFRG", "FRG", 40.7287811, -73.4134072, "Farmingdale", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kfrg.m3u")
	                 ]),
	    new FeedData("KFXE", "FXE", 26.1972794, -80.1707063, "Ft. Lauderdale", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kfxe.m3u")
	                 ]),
	    new FeedData("KGSO", "GSO", 36.0977469, -79.9372975, "Greensboro", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kgso2.m3u")
	                 ]),
	    new FeedData("KJFK", "JFK", 40.6397511, -73.7789256, "New York, Kennedy", CLASS_B,
	                 [
	                 new StreamData("Delivery/Ground", "http://audio.liveatc.net:8012/jfk_del_gnd.m3u"),
	                 new StreamData("Tower", "http://audio.liveatc.net:8012/jfk_twr.m3u"),
	                 new StreamData("Approach/Departure", "http://audio.liveatc.net:8012/jfk_app_dep.m3u")
	                 ]),
	    new FeedData("KLAX", "LAX", 33.9425361, -118.4080744, "LAX/ONT/SoCal Approach", CLASS_B,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kont.m3u")
	                 ]),
	    new FeedData("KLNK", "LNK", 40.8509722, -96.7592500, "Lincoln", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/klnk.m3u")
	                 ]),
	    new FeedData("KLOU", "LOU", 38.2280000, -85.6637222, "Louisville", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/ksdf_klou.m3u")
	                 ]),
	    new FeedData("KMLB", "MLB", 28.1027528, -80.6452569, "Melbourne", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kmlb.m3u")
	                 ]),
	    new FeedData("KMHT", "MHT", 42.9325556, -71.4356667, "Manchester/Nashua", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kmht.m3u")
	                 ]),
	    new FeedData("KMKE", "MKE", 42.9472222, -87.8965833, "Milwaukee", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/milwaukee.m3u")
	                 ]),
	    new FeedData("KMSN", "MSN", 43.1398578, -89.3375136, "Madison", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kmsn.m3u")
	                 ]),
	    new FeedData("KMSP", "MSP", 44.8833247, -93.2169225, "Minneapolis", CLASS_B,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kmsp2.m3u")
	                 ]),
	    new FeedData("KOSU", "OSU", 40.0797778, -83.0730278, "Ohio State University", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kosu.m3u")
	                 ]),
	    new FeedData("KPHL", "PHL", 39.8719444, -75.2411389, "Philadelphia", CLASS_B,
	                 [
	                 new StreamData("Tower", "http://audio.liveatc.net:8012/phl_twr.m3u"),
	                 new StreamData("Approach", "http://audio.liveatc.net:8012/phl_app.m3u")
	                 ]),
	    new FeedData("KPIT", "PIT", 40.4914658, -80.2328708, "Pittsburgh", CLASS_B,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kpit.m3u")
	                 ]),
	    new FeedData("KSCK", "SCK", 37.8941667, -121.2383056, "Stockton Metropolitan", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/ksck.m3u")
	                 ]),
	    new FeedData("KSDF", "SDF", 38.1743889, -85.7360000, "Louisville Intl/KLOU", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/ksdf_klou.m3u")
	                 ]),
	    new FeedData("KSFB", "SFB", 28.7776400, -81.2374894, "Sanford/Orlando Tower", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/ksfb.m3u")
	                 ]),
	    new FeedData("KSYR", "SYR", 43.1111869, -76.1063106, "Syracuse", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/ksyr.m3u")
	                 ]),
	    new FeedData("KTTN", "TTN", 40.2766911, -74.8134683, "Trenton", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/kttn.m3u")
	                 ]),
	    new FeedData("KTVC", "TVC", 44.7414447, -85.5822350, "Traverse City", CLASS_D,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/ktvc.m3u")
	                 ]),
	    new FeedData("PAFA", "FAI", 64.8151142, -147.8562672, "Fairbanks Intl", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/pafa.m3u")
	                 ]),
	    new FeedData("PANC", "ANC", 61.1743611, -149.9963611, "Anchorage Intl", CLASS_C,
	                 [
	                 new StreamData("Feed", "http://audio.liveatc.net:8012/anchorage.m3u")
	                 ])
	    ];
}
