/* ------------------------------------------------------------------

File:		route.js
Abstract:	handling of routes, route display function
Version:	1.0
Authors:	Pascal Dreer / Joze Senegacnik

------------------------------------------------------------------ */


var gRt = null;
var routeWindow = null;
var gHeading = 0;



function routeTest()
{
    try {
	gRt = new RouteList();
	
	gRt.add(1,90,7,5,0,0);
	gRt.add(2,90,7,5,0,0);
	alert("Bearing 1 > 2:" + roundNumber(gRt.find(2).getBearing(),2));
	
	gRt.add(3,90,7.5,0,0);
	
	
	gRt.display();	
	gRt.add(4,90,7.5,0,0,gRt.find(2));
	gRt.display();
	gRt.add(5,90,7.5,0,0);
	gRt.display();
	gRt.remove(gRt.find(2));
	
	gRt.display();
	gRt.redraw();
	
	var dist = gRt.getDistance();
	var distText = "Distanz: " +  Math.round(dist / 1000) + " km (" + Math.round(dist * 0.0005399568) + " NM)";
	
	alert("Dist=" + distText + "/Dur=" + formatTimeHHMM(gRt.getDuration()));
	
//	gRt.deleteList();
	
//	gRt.display();
    } catch ( e ) {
        alert("An exception occurred in the script. Error name: " + e.name + ". Error message: " + e.message);
    }
}




function routeStarted()
{
	if (gRt)
		return true;
	else
		return false;
}



function resetRoute()
{
	if (gRt)
		gRt.deleteList();
	gRt = null;
	initRouting();
	if (routeWindow) {
		routeWindow.close();
		routeWindow = null;
	}
}




function openRouteWindow()
{
	if (routeWindow) {
		if (!routeWindow.closed) {
			showRoute();
			routeWindow.focus();
			return;		
		}
	}
	var routeWinWidth = 700;
	var routeWinLeft = getWindowWidth() + 10;
	
	if ((routeWinLeft + routeWinWidth) > screen.width)
		routeWinLeft = screen.width - routeWinWidth;
	
	routeWindow = window.open("","Route","width=" + routeWinWidth + ",height=400,left=" + routeWinLeft + ",top=50, menubar");	// this.href
	routeWindow.name = "Routing-Window";
	routeWindow.focus();
	showRoute()	;
	return;


	if (!routeWindow) {
		var routeWinWidth = 700;
		var routeWinLeft = getWindowWidth() + 10;
	
		if ((routeWinLeft + routeWinWidth) > screen.width)
			routeWinLeft = screen.width - routeWinWidth;
	
		routeWindow = window.open("","Route","width=" + routeWinWidth + ",height=400,left=" + routeWinLeft + ",top=50, menubar");	// this.href
		routeWindow.name = "Routing-Window";
		routeWindow.focus();
		showRoute()	;
	}
	else {
		showRoute();
		routeWindow.focus();
	}
}



function updateRouteWindow()
{
	
	if (routeStarted() && routeWindow)
		if (!routeWindow.closed)
			showRoute();
}




function showRoute()
{
/*
        var coordinates = new Array();
        
        if (gRt) {
                var rp = gRt.display();
		while (rp != null) {
                     coordinates[ coordinates.length ] = gMarkers[rp.prev.mp_id].getPoint();
		     rp = gRt.display(rp);
		}
           topoDrawGraph( document.getElementById("graph"), coordinates );
        }

*/
	var HTMLstring='<html>\n';
	HTMLstring+='<head>\n';
	HTMLstring+='<title>Route</title>\n';
	HTMLstring+='<style type="text/css">@import "flightmap.css";</style>';
	HTMLstring+='</head>\n';
	HTMLstring+='<body><form="editUserForm">\n';

	if (gRt) {

                var txt="";
		var txt = "<table border='0' width='100%' cellpadding='0' cellspacing='1'>";
                // header
		txt += "<tr><td><span class=\"routeTextHead\">Waypoint</span></td><td align=right><span class=\"routeTextHead\">Course</span></td><td align=right><span class=\"routeTextHead\">Heading</span></td><td align=right><span class=\"routeTextHead\">WCA</span></td><td align=right><span class=\"routeTextHead\">Wind</span></td><td align=right><span class=\"routeTextHead\">Dist</span></td><td align=right><span class=\"routeTextHead\">Elev (ft)</span></td><td align=right><span class=\"routeTextHead\">TAS</span></td><td align=right><span class=\"routeTextHead\">GS</span></td><td align=right><span class=\"routeTextHead\">Fuel</span></td><td align=right><span class=\"routeTextHead\">EET</span></td><td align=right><span class=\"routeTextHead\">&Sigma; EET</span></td></tr>";
		var rp = gRt.display();

		var eto = 0;
		var eet = 0;

                var efuel = 0;
                var etfuel = 0;

		while (rp != null) {
                     if( rp.dist > 0.0 ) {
			eet = Math.round((rp.dist * 0.0005399568) / rp.gs * 60);
                     } else { 
                        eet = 0;
                     } 
                     efuel= eet*( rp.fuel_consumption / 60 );
                     etfuel += efuel;
		     eto = eto + eet;
                     //text line
	             txt += "<tr><td><span class=\"routeText\">" + gMappoints[rp.mp_id].name + "</span></td><td align=right><span class=\"routeText\">" + formatBearing(rp.bearing) + "</span></td><td align=right><span class=\"routeText\">" + formatBearing(rp.heading) + "</span></td><td align=right><span class=\"routeText\">" + rp.wca + "</span></td><td align=right><span class=\"routeText\">" + rp.wind_dir + "@" + rp.wind_speed + "</span></td><td align=right><span class=\"routeText\">" + Math.round(rp.dist * 0.0005399568) + "</span></td><td align=right><span class=\"routeText\">" + gMappoints[rp.mp_id].elev + "</span></td><td align=right><span class=\"routeText\">" + Math.round(rp.tas) + "</span></td><td align=right><span class=\"routeText\">" + Math.round(rp.gs) + "</span></td><td align=right><span class=\"routeText\">" + Math.round(efuel*10)/10 + "</span></td><td align=right><span class=\"routeText\">" + formatTimeHHMM(eet) + "</span></td><td align=right><span class=\"routeText\">" + formatTimeHHMM(eto) + "</span></td></tr>";
		     rp = gRt.display(rp);
		}

		txt += "</table><br>";
		HTMLstring+=txt;

		var dist = gRt.getDistance();
//		var distText = "<span class=\"routeText\">Distance: " + Math.round(dist * 0.0005399568) + " NM (" + Math.round(dist / 1000) + " km)</span>";
		var distText = "<span class=\"routeText\">Distance: " + Math.round(dist * 0.0005399568) + " NM (" + Math.round(dist) + " m)</span>";
		HTMLstring+=distText;

	}
	else {
		
		HTMLstring+='<span class=\"routetitle\">Routing<br/></span>';
		HTMLstring+='<span class=\"route-text\">There is no route defined.</span>';
	}
	
	HTMLstring+='<br><p><a href="javascript: self.close();" class="routeText">Close Window</a></p>';
	HTMLstring+='</form></body>\n';
	HTMLstring+='</html>';
	
//	routeWindow.document.open();

	routeWindow.document.write(HTMLstring);
	routeWindow.document.close();
/*
    } catch ( e ) {
        alert("An exception occurred in the script. Error name: " + e.name + ". Error message: " + e.message);
    }
*/
}



function RoutePoint( mp_id, tas, fc, wd, ws)
{
	this.id = gMappoints[mp_id].id;
	this.mp_id = mp_id;
	this.tas = tas;
        this.gs = 0;
	this.dist = 0;
	this.bearing = 0;
        this.heading = 0;
        this.wca = 0;
        this.wind_dir = wd;
        this.wind_speed = ws;
        this.fuel_consumption = fc;
	this.line = null;
	this.prev = null;
	this.next = null;
	
	
	this.insertAfter = function(newPoint) 
	{
		if (this == newPoint)
			return;
		newPoint.extract();
		newPoint.prev = this;
		if (this.next) {
			newPoint.next = this.next;
			this.next.prev = newPoint;
		}
		this.next = newPoint;
	}
	
	
	this.extract = function()
	{
		if (this.prev && this.next) {
			if (this.next.mp_id) {
				var points = [];
				points.push(gMarkers[this.prev.mp_id].getPoint());
				points.push(gMarkers[this.next.mp_id].getPoint());
				this.next.dist = points[0].distanceFrom(points[1]);
				points.pop();
				points.pop();
			}
		}
		if (this.line)
			gMap.removeOverlay(this.line);
	
		if (this.prev)
			this.prev.next = this.next;
		
		if (this.next) 
			this.next.prev = this.prev;
			
		
		
		this.prev = this.next = null;
	}
	
	this.getBearing = function() 
	{

		return this.bearing;
	}

}


function RouteList() 
{
   try{
	this.prev = this.next = this;
	
	
	this.insertAfter = function(newPoint) 
	{
		
		if (this == newPoint)
			return;
		newPoint.extract();
		newPoint.prev = this;
		if (this.next) {
			newPoint.next = this.next;
			this.next.prev = newPoint;
		}
		this.next = newPoint;
	}
	
	this.remove = function(node)
	{
		node.extract();
	}
	
	
	this.add = function(mp_id, tas, fc, wd, ws, afterNode)
	{
		var newNode = new RoutePoint( mp_id, tas, fc, wd, ws );
		if (!afterNode) {
			if (this.prev)
				afterNode = this.prev;
			else
				afterNode = this;
		}
		afterNode.insertAfter(newNode);
		
		if (newNode.prev.mp_id) {
			var points = [];
			points.push(gMarkers[newNode.prev.mp_id].getPoint());
			points.push(gMarkers[newNode.mp_id].getPoint());
			newNode.dist = points[0].distanceFrom(points[1]);
			newNode.bearing = calcBearing(points[0].lat(),points[0].lng(),points[1].lat(),points[1].lng());
                        heading( newNode.bearing, newNode.wind_dir, newNode.wind_speed, newNode.tas ); 
                        newNode.gs = gGS;
                        newNode.heading = gHeading;
                        newNode.wca = gWCA;
	
			var line = new GPolyline(points,"#f2bf03",4,0.7);
			newNode.line = line;
			gMap.addOverlay(line);
		
//			points.pop();
		}
		return newNode;
	}
	
	this.find = function (mp_id,startNode) {
		if (!startNode) 
			startNode = this;
			
		for (var node = startNode.next; node != this;node = node.next) {
			if (node.mp_id == mp_id)
				return node;
		}
	}
	
	this.display = function(startPoint)
	{
		var list = "";
		
		if (!startPoint) 
			startPoint = this;
		
		if (!startPoint.next)
			return;
		
		for (var node = startPoint.next; node != this;node = node.next) {
			return node;
//			list = list + node.tas + " ";
//			if (func(node.elem)) {return node;}
		}
//		alert(list);
	}
	
	
	this.deleteList = function()
	{
		var startPoint = this;
		var node, tmp;
		
		for (node = startPoint.next; node != this;null) {
			tmp = node.next;
			node.prev.next = null;
			if (node.line)
				gMap.removeOverlay(node.line);		
			delete node;
			node = tmp;
		}

	}
		
	
	this.getDistance = function()
	{
		var dist = 0;
		var startPoint = this;
			
		for (var node = startPoint.next; node != this;node = node.next) {
			dist = dist + node.dist;
		}
		return dist;
	}
	
	this.getDuration = function()
	{
		var duration = 0;
		var startPoint = this;
			
		for (var node = startPoint.next; node != this;node = node.next) {
                    if( node.dist > 0 ) {
                        heading( node.bearing, node.wind_dir, node.wind_speed, node.tas ); 
                        node.gs = gGS;
                        node.heading = gHeading;
                        node.wca = gWCA;
			duration = duration + Math.round(((node.dist * 0.0005399568) / node.gs * 60));
                    } else {
                        node.gs = node.tas;
                        node.wca = 0;
                        node.heading = node.bearing;
                   }
		}
		return Math.round(duration);
	}
	
	
	this.updatePosition = function(mp_id,startNode)
	{
		if (!startNode) 
			startNode = this;
			
		for (var node = startNode.next; node != this;node = node.next) {
				if (node.mp_id == mp_id && startNode.prev) {
					var points = [];
					points.push(gMarkers[node.prev.mp_id].getPoint());
					points.push(gMarkers[node.mp_id].getPoint());
					node.dist = points[0].distanceFrom(points[1]);
					node.bearing = calcBearing(points[0].lat(),points[0].lng(),points[1].lat(),points[1].lng());
				}
		}
	}
	
	
	this.redraw = function()
	{
		var startPoint = this;

		for (var node = startPoint.next; node != this;node = node.next) {
			if (node.line)
				gMap.removeOverlay(node.line);		
		}

		var startPoint = this;
		
		for (var node = startPoint.next; node != this;node = node.next) {
			if (node.prev.mp_id) {
				var points = [];
				points.push(gMarkers[node.prev.mp_id].getPoint());
				points.push(gMarkers[node.mp_id].getPoint());
				node.dist = points[0].distanceFrom(points[1]);
				node.bearing = calcBearing(points[0].lat(),points[0].lng(),points[1].lat(),points[1].lng());
	
				var line = new GPolyline(points,"#f2bf03",4,0.7);
				node.line = line;
				gMap.addOverlay(line);
				points.pop();
				points.pop();
			}		
		}	
	}
     } catch (e) {
        alert("An exception occurred in the script. Error name: " + e.name + ". Error message: " + e.message);
     } 
}





function initRouting()
{
     try {
	document.getElementById("route_display").innerHTML = "";
	document.getElementById("form_gs").value = gTAS;
	document.getElementById("form_fuel").value = gFuelConsumption;
	document.getElementById("form_wind_dir").value = gWind;
	document.getElementById("form_wind_speed").value = gWindSpeed;
	displayRouteInfo(0,0,0.0);
     } catch (e) {
        alert("An exception occurred in the script. Error name: " + e.name + ". Error message: " + e.message);
     } 
}



function addTrackLeg(i)
{
      try {
	if (!gRt) {
		gRt = new RouteList();
		displayRouteInfo(0,0,0);
	}
	
	gRt.add(i, gTAS, gFuelConsumption, gWind, gWindSpeed );
	displayRouteInfo(gRt.getDistance(),gRt.getDuration(),0);
	gMap.closeInfoWindow();
	updateRouteWindow();
     } catch (e) {
        alert("An exception occurred in the script. Error name: " + e.name + ". Error message: " + e.message);
     } 
}





function formatTimeHHMM(time)
{
	if (time > 59) {
		var hrs = Math.floor(time / 60);
		var mins = Math.round(time - (hrs * 60));
	}
	else {
		var hrs = 0;
		var mins = Math.round(time);
	}
	
	if (hrs < 10) 
		hrsTxt = "0" + hrs;
	else
		hrsTxt = hrs;
		
	if (mins < 10)
		minsTxt = "0" + mins;
	else
		minsTxt = mins;
		
	return (hrsTxt + ":" + minsTxt);
}



function formatBearing(brng)
{
	var b = Math.round(brng);
	
	if (b < 10)
		return "00" + b;
	if (b < 100)
		return "0" + b;

	return b;
}




function displayRouteInfo(dist,time,fuel)
{
	var timeText = formatTimeHHMM(Math.round(time));
		
	var fuelText = "";
	if (fuel > 0)
		var fuelText = ", Fuel: " + Math.round(fuel);
		
	var timeText = "Time: " + timeText + fuelText;
	var distText = "Distance: " + Math.round(dist * 0.0005399568) + " NM (" + Math.round(dist / 1000) + " km)";
	
	
	document.getElementById("route_display").innerHTML = timeText + "<br>" + distText;
}







/* Source for bearing calculation: http://www.movable-type.co.uk/scripts/latlong.html */
function calcBearing(lat0,lng0,lat1,lng1)
{
	var dLon = (lng1 - lng0).toRad(); 
	var y = Math.sin(dLon) * Math.cos(lat1.toRad());
	var x = Math.cos(lat0.toRad())*Math.sin(lat1.toRad()) - Math.sin(lat0.toRad())*Math.cos(lat1.toRad())*Math.cos(dLon);
	return Math.atan2(y, x).toBrng();
}


Number.prototype.toBrng = function() {  // convert radians to degrees (as bearing: 0...360)
  return (this.toDeg()+360) % 360;
}

Number.prototype.toRad = function() {  // convert degrees to radians
  return this * Math.PI / 180;
}

Number.prototype.toDeg = function() {  // convert radians to degrees (signed)
  return this * 180 / Math.PI;
}


function heading( course, windDir, windSpd, TAS ) 
{
  crs = (Math.PI/180) * course;
  wd  = (Math.PI/180) * windDir;
  swc = (windSpd/TAS) *  Math.sin(wd - crs);
  if (Math.abs(swc) > 1){
    alert("Danger!... Course should not be flown... Wind is too strong");
    return;
    }
  hd = crs + Math.asin(swc);
  if (hd < 0) {
    hd = hd + 2 * Math.PI;
    }
  if (hd > 2*Math.PI) {
    hd = hd - 2 * Math.PI;
    }
  gHeading = Math.round((180/Math.PI) * hd);
  gGS  = Math.round( TAS * Math.sqrt(1 - Math.pow(swc, 2)) -  (windSpd * Math.cos(wd - crs)));
  var wca = Math.atan2( windSpd * Math.sin(hd-wd),TAS-windSpd * Math.cos(hd-wd));
  gWCA = Math.round((180/Math.PI) * (wca * -1));
}
