/*
 *
 */

var fsdmap = {
		map : null,
		doDebug : false,
		debugDuration : 100,
		updateCount : 0,
		useTracks : false,
		isGeodesic : true,
		isFirstRound : true,
		//doFollowPilot : false,
		clientList : new Object(),
		updateIntervall : 10,
		updateIntervallMin : 9,
		iconSize : 32,
		clientDataUrl : "fsd_data.php",
		fpDataUrl : "",
		trackToggleId : "toggleTracks",
		doShowTracks : false,
		userlevels : Array(
				'disabled',
				'Observer',
				'Junior Student',
				'Student',
				'Senior Student',
				'Junior Controller',
				'Controller',
				'Senior Controller',
				'Junior Instructor',
				'Instructor',
				'Senior Instructor',
				'Supervisor',
				'Administrator'
		)
};

function initialize() {
	if (GBrowserIsCompatible()) {
		fsdmap.map = new GMap2(document.getElementById("map_canvas"));
		fsdmap.map.addMapType(G_PHYSICAL_MAP );
		fsdmap.map.setMapType(G_PHYSICAL_MAP );
		fsdmap.map.addControl(new GScaleControl() );
		fsdmap.map.addControl(new GHierarchicalMapTypeControl() );
		// fsdmap.map.addControl(new GOverviewMapControl() );
        /*
		fsdmap.map.addControl(new GNavLabelControl(),
				new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7,7))
		);
		fsdmap.map.addControl(new GLargeMapControl3D(),
				new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(7,30))
		);
        */
        fsdmap.map.addControl(new GLargeMapControl3D());
		fsdmap.map.enableScrollWheelZoom();
		// fsdmap.map.enableContinuousZoom();
		new GKeyboardHandler(fsdmap.map);
		fsdmap.map.setCenter(new GLatLng(0, 0), 1);
		

		GEvent.addListener(fsdmap.map, "zoomend", function(oldZ, newZ) {
		  debug("zoomed to " + newZ);
		});
		
		debug("basic map loaded");
		parseUrlParams();
		addEventHandlers();
		loadClients();
	}
}

function addEventHandlers() {
	var trackToggle = document.getElementById(fsdmap.trackToggleId);
	try {
		if (fsdmap.useTracks) {		
			trackToggle.addEventListener("change", function(e){
				debug(e.target.checked);
				fsdmap.doShowTracks = e.target.checked;
				showTracks();
			},true);
		} else {
			trackToggle.parentNode.style.display = "none";
		}
	} catch (e) {
		// TODO: handle exception
	}
}

function parseUrlParams() {
	debug("TODO parsing url params ...");

	fsdmap.doDebug = getParam("d") == "yes";
	
	try {
		fsdmap.useTracks = getParam("t") == "yes";
	} catch (e) {
		// TODO: handle exception
		fsdmap.useTracks = false;
	}
	
	try {
		fsdmap.updateIntervall = parseInt(getParam("i"));
	} catch (e) {
		// TODO: handle exception
		fsdmap.updateIntervall = fsdmap.updateIntervallMin;
	}
	
	if (isNaN(fsdmap.updateIntervall) ||fsdmap.updateIntervall < fsdmap.updateIntervallMin) {
		debug("fixing update intervall ...");
		fsdmap.updateIntervall = fsdmap.updateIntervallMin;
	}
}

function loadClients(){
	if (fsdmap.doDebug) {
		fsdmap.updateCount ++;
	}
	debug("(re)loadClients ... " + fsdmap.updateCount);
	GDownloadUrl( fsdmap.clientDataUrl + '?time=' + (new Date().getTime()), parseClientData);
}

function parseClientData(clientData) {
	debug("parseClientData ... " + fsdmap.updateCount);
	var bounds = null;
	parseClientDataFunc:
	{
		try {
			debug("<textarea style='width:100%'>"+clientData+"</textarea>", true);
			if (fsdmap.isFirstRound) bounds = new GLatLngBounds();
			var xml = GXml.parse(clientData);
			debug("<textarea style='width:100%'>"+xml.lastChild+"</textarea>", true);
			
			if (xml.lastChild != null && xml.lastChild.nodeName == "clients") {
				//var cl = xml.documentElement.getElementsByTagName("client");
				
				debug("loaded data for " + xml.lastChild.childNodes.length + " active clients");
				
				//collect obsolete clients
				var obsoleteClients = new Object();
				for ( var c in fsdmap.clientList) {
					obsoleteClients[c] = true;
				}				

				for ( var i = 0; i < xml.lastChild.childNodes.length; i++) {
					var clientElem = xml.lastChild.childNodes[i];
					
					if (clientElem.nodeType == 1) {						
						
						if (clientIsValid(clientElem)) {
							var client = new ClientClass(clientElem);
							var latLng = new GLatLng(client.lat, client.lng);
							obsoleteClients[client.cs] = false;
							//client.trackData.push(latLng);
							if (fsdmap.clientList[client.cs] == null) {
								debug("adding new client " + client.cs);
								if (fsdmap.useTracks && client.type == "P") { //controllers need no track
									client.trackPoints.push(latLng); 
										//new GPolyline(new Array(latLng), "#FF00FF", 3, 0.5, {geodesic:fsdmap.isGeodesic});
								}
								fsdmap.clientList[client.cs] = client;
								fsdmap.map.addOverlay( fsdmap.clientList[client.cs].marker );
								if (fsdmap.isFirstRound) bounds.extend(latLng);
							} else {
								debug("updating existing client " + client.cs);
								fsdmap.clientList[client.cs].update(client);
								if (fsdmap.useTracks && client.type == "P") { //no track for controllers
									if (fsdmap.clientList[client.cs].trackPolyLine != null) {
										fsdmap.map.addOverlay(fsdmap.clientList[client.cs].trackPolyLine);
										fsdmap.clientList[client.cs].trackPolyLine.hide();
									}
								}
							}
							if (fsdmap.clientList[client.cs].followOnMap){
								debug("following " + client.cs + " at " + latLng);
								fsdmap.map.setCenter(latLng);
							}
							if (fsdmap.clientList[client.cs].trackPolyLine != null) { //no track for controllers
								debug("track length: " + fsdmap.clientList[client.cs].trackPolyLine.getVertexCount());
							}
						} else {
							debug("TODO remove invalid client if existing");
						}
					}
					
				}
				
				//remove obsolete clients
				for ( var cr in obsoleteClients) {
					if (obsoleteClients[cr]==true) {
						removeClient(cr);
					}
				}
				
			} else {
				debug("invalid data");
			}

		} catch (e) {
			// TODO: handle exception
			debug("Error in parseClientData: " + e.toString());
		}
		if (fsdmap.doDebug && fsdmap.updateCount > fsdmap.debugDuration) {
			debug("stop updating");
			break parseClientDataFunc;
		}
		
		updatePilotList();
		showTracks();
		debug("see you in " + fsdmap.updateIntervall + " seconds");
		window.setTimeout(loadClients, fsdmap.updateIntervall * 1000);
	}
	if (fsdmap.isFirstRound){
		fsdmap.isFirstRound = false;
		if (bounds != null){
			fsdmap.map.setZoom(fsdmap.map.getBoundsZoomLevel(bounds));
			fsdmap.map.setCenter(bounds.getCenter());
		}
	}
}


function removeClient(callSign) {
	var client = fsdmap.clientList[callSign];
	if (client != null) {
		if (client.trackPolyLine!=null) {
			fsdmap.map.removeOverlay(client.trackPolyLine);
		}
		if (client.marker!=null) {
			fsdmap.map.removeOverlay(client.marker);			
		}
		if (client.flightPlan != null) {
			try {
				fsdmap.map.removeOverlay(client.flightPlan.route);
				fsdmap.map.removeOverlay(client.flightPlan.from);
				fsdmap.map.removeOverlay(client.flightPlan.to);

			} catch (e) {
				// one of the flightplan elements did not exist?
			}
		}
		
		delete fsdmap.clientList[callSign];
	}
}

function showTracks() {
	if (fsdmap.doShowTracks) {
		debug("update track display");
	} else {
		debug("remove track display");
	}
	for ( var cs in fsdmap.clientList) {
		var client = fsdmap.clientList[cs];
		if (client.trackPolyLine != null) {
			if (client.trackPolyLine.getVertexCount() > 0) {
				if (fsdmap.doShowTracks) {
					client.trackPolyLine.show();
				} else {
					client.trackPolyLine.hide();
				}
			}			
		} else {
			debug("no track for " + cs);
		}
	}
}


function clientIsValid(c){
	if (c.getAttribute("lat")=="" || c.getAttribute("lng")=="") {
		return false;
	} else if(isNaN(c.getAttribute("lat")) || isNaN(c.getAttribute("lng"))) {
		return false;
	} else {
		return true;
	}
}


function createMarker(c) {
	var planeIcon = getMarkerIcon(c);

	var latlng = new GLatLng(parseFloat(c.lat), parseFloat(c.lng));
	var marker = new GMarker(latlng, {icon:planeIcon});
	marker.value = c.cs;
	GEvent.addListener(marker, "click", function() {
		var html = "<div class=\"myMarker\">";
		html += "<p><label>Callsign:</label>&nbsp;" + c.cs + "<p>";
		if (c.type == "P"){

			html += "<p><label>Aircraft:</label>&nbsp;" + c.acf + "</p>";
			html += "<p><label>KTas:</label>&nbsp;" + c.gs + "</p>";
			html += "<p><label>Alt:</label>&nbsp;" + c.alt + "</p>";
			if (c.hdg != null) {
				html += "<p><label>Hdg:</label>&nbsp;" + c.hdg + "</p>";
			}
			if (c.to != "" && c.from != "") {
				html += "<p><label>Flight Plan:</label>&nbsp;<a href='#' onclick=\"toggleFP('" + c.cs + "','" + c.from + "','" + c.to + "','" + c.route + "',false)\">" + c.from + " - " + c.to + "</a></p>";
			}
			if (c.route != ""){
				html += "<p><label></label>&nbsp;" + c.route + "</p>";
			}
		}
		//myHtml += "<p><label></label></p>";
		html += "</div>";

		marker.openInfoWindowHtml(html);
		// map.openInfoWindowHtml(latlng, html);
	});
	return marker;
}

/**
* return a GIcon fitting the client object
*
*/
function getMarkerIcon(c){

	var planeIcon = new GIcon(G_DEFAULT_ICON);
	planeIcon.image = getIconImage(c);

	planeIcon.iconSize = new GSize(fsdmap.iconSize,fsdmap.iconSize);
	planeIcon.shadow="";
	planeIcon.iconAnchor=new GPoint(fsdmap.iconSize/2, fsdmap.iconSize/2);
	planeIcon.infoWindowAnchor=new GPoint(fsdmap.iconSize/2, fsdmap.iconSize/2);

	return planeIcon;
}

function getIconImage(c) {
	var img = "icons/plane.png";
	if(c.type=="P"){

		var hdgSuffix = getHdgSuffix(c.hdg);
		var typeSuffix = "";

		// 1/E170/H-S/C
		// 1/C182/L-S/C
		// GFV1008 (FP), B/MD11/F
		var planeParams = c.acf.split('/');
		if (planeParams.length > 2) {
			typeSuffix = planeParams[2].charAt(0);
		} else {
			typeSuffix="D";
		}
		if (typeSuffix == "F"){
			typeSuffix = "H";
		}
		img = "icons/plane" + typeSuffix + hdgSuffix + ".png";
	}else{
		img="icons/tower.png";
	}
	return img;
}

function getHdgSuffix(hdg) {
	if (hdg < 22.5 || hdg > 337.5) {
		return "0";
	} else if (hdg >= 22.5 && hdg < 67.5) {
		return "45";
	} else if (hdg >= 67.5 && hdg < 112.5) {
		return "90";
	} else if (hdg >= 112.5 && hdg < 157.5) {
		return "135";
	} else if (hdg >= 157.5 && hdg < 202.5) {
		return "180";
	} else if (hdg >= 202.5 && hdg < 247.5) {
		return "225";
	} else if (hdg >= 247.5 && hdg < 282.5) {
		return "270";
	} else if (hdg >= 282.5 && hdg < 337.5) {
		return "315";
	}
	return "0";
}

function getFromIcon() {
	var icon = new GIcon(G_DEFAULT_ICON);
	icon.iconSize = new GSize(fsdmap.iconSize, fsdmap.iconSize);
	icon.iconAnchor=new GPoint(fsdmap.iconSize/2, fsdmap.iconSize);
	icon.image = "icons/iconStart.png";
	return icon;
}

function getToIcon() {
	var icon = new GIcon(G_DEFAULT_ICON);
	icon.iconSize = new GSize(fsdmap.iconSize, fsdmap.iconSize);
	icon.iconAnchor=new GPoint(fsdmap.iconSize/2, fsdmap.iconSize);
	icon.image = "icons/iconFinish.png";
	return icon;
}


function updatePilotList() {
	var pilotList = document.getElementById("pilotlist");
	var atcList = document.getElementById("atclist");
	var phtml = "";
	var chtml = "";
	try {
		for (var client in fsdmap.clientList){
			var c = fsdmap.clientList[client];
			if (c.type == "P"){
				var checked = c.followOnMap ? "checked='checked'" : "";
				phtml += "<form>";
				phtml += "<li><input type='checkbox' name='c' onchange='setFollowOn(\"" + c.cs + "\",this);' " + checked + " title='check to follow this pilot'/><a href='#' onclick='centerPilot(" + c.lat + "," +c.lng + ");' title='Click to center map on this pilot'>" + c.cs + "</a>";
				if (c.to != "" && c.from != "") {
					phtml += " (<a href='#' onclick=\"toggleFP('" + c.cs + "','" + c.from + "','" + c.to + "','" + c.route + "',true)\" title='click to show flight plan on map'>FP</a>)";
				}
				phtml += ", " + c.acf + " at " + c.alt + "ft, " + c.gs + "kt";
				if (c.hdg != null) {
					phtml += ", hdg " + c.hdg;
				}
				if (c.to != "" && c.from != "") {
					phtml += ", " + c.from + " - " + c.to;
				}
				phtml += "</li>";
			} else if (c.type == "C") {
				chtml += "<li><a href='#' onclick='centerPilot(" + c.lat + "," +c.lng + ");'>" + c.cs + "</a>";
				chtml += ", rating " + fsdmap.userlevels[c.rating];
				chtml += ", freq " + c.freq;
			} else{
				debug("unknown type " + c.type);
			}
		}
	} catch (e) {
		alert(e.message);
	}
	pilotList.innerHTML = phtml;
	atcList.innerHTML = chtml;
}

function toggleFP(callSign, from, to, route, center) {
	//centerOnClick = center;

	var debugCmd = "";
	if (fsdmap.doDebug) {
		debugCmd = "&debug";
	}
	var client = fsdmap.clientList[callSign];
	if (client != null) {
		if (client.flightPlan != null) {
			if (client.flightPlan.route.isHidden()) {
				debug("updating flightplan for " + callSign);
				GDownloadUrl("data.php?" + debugCmd + "&action=track&cs=" + callSign + "&from=" + from + "&to=" + to + "&route=" + route.replace(/ /g, '|'), loadFlightplan);
			} else {
				debug("hide flightplan for " + callSign);
				try {
					client.flightPlan.route.hide();
					client.flightPlan.from.hide();
					client.flightPlan.to.hide();
					
				} catch (e) {
					// one of the flightplan elements did not exist?
				}
			}
			
		} else {
			debug("loading new flightplan for " + callSign);
			GDownloadUrl("data.php?" + debugCmd + "&action=track&cs=" + callSign + "&from=" + from + "&to=" + to + "&route=" + route.replace(/ /g, '|'), loadFlightplan);
		}
	}
}

function loadFlightplan(fpData) {
	var jsonData = eval('(' + fpData + ')');
	debug(jsonData, true);

	if (jsonData.lines.length > 0) {
		//anything to show at all?
		
		var route = new Object();
		
		for (var i=0; i<jsonData.lines.length; i++) {
			//currently this is supposed to be only one line
			if (jsonData.lines[i].points.length > 1) {
				var pts = [];
				for (var j=0; j<jsonData.lines[i].points.length; j++) {
					var ltLng = new GLatLng(parseFloat(jsonData.lines[i].points[j].lat), parseFloat(jsonData.lines[i].points[j].lng));
					if (j==0){
						route["from"] = new GMarker(ltLng, {icon:getFromIcon()});
					}
					if (j==jsonData.lines[i].points.length-1){
						route["to"] = new GMarker(ltLng, {icon:getToIcon()});				
					}
					pts[j] = ltLng;
					//bounds.extend(ltLng)
				}
				route["route"] = new GPolyline(pts, jsonData.lines[i].colour, jsonData.lines[i].width, 0.5, {geodesic:fsdmap.isGeodesic});
				route["cs"] = jsonData.lines[i].cs;
			}
		}
		
		fsdmap.clientList[route.cs].flightPlan = route;
		fsdmap.map.addOverlay(fsdmap.clientList[route.cs].flightPlan.route);
		fsdmap.map.addOverlay(fsdmap.clientList[route.cs].flightPlan.from);
		fsdmap.map.addOverlay(fsdmap.clientList[route.cs].flightPlan.to);
	}
}

function centerPilot(lat, lng){
	debug("centering at " + lat + " " + lng);
	fsdmap.map.setCenter(new GLatLng(parseFloat(lat), parseFloat(lng)), 10);
}

function setFollowOn(cs, e) {
	for(var c in fsdmap.clientList){
		if (fsdmap.clientList[c].cs == cs) {
			fsdmap.clientList[c].followOnMap = e.checked;
			centerPilot(fsdmap.clientList[c].lat, fsdmap.clientList[c].lng);
		} else{
			fsdmap.clientList[c].followOnMap = false;
		}
	}
	updatePilotList();
}

function getParam(name) {
    var q = location.search.substring(1);
    var r = null;

    if (q.length > 0) { 
        var arr = q.split("&");
        for (var i=0; i < arr.length; i++) {
             var p = arr[i].split("=");
             if (p[0] == name && p.length == 2) {
                r = p[1];
             }
        }   
    }
    return r;
}


function debug(msg, asHtml) {
	if (fsdmap.doDebug) {
		if (asHtml) {
			GLog.writeHtml(msg);
		} else {
			GLog.write(msg);
		}
	}
}

function ClientClass(inObj){
	this.type = inObj.getAttribute("type");
	this.cs = inObj.getAttribute("cs");
	this.name = inObj.getAttribute("name");
	this.rating = inObj.getAttribute("rating");
	this.cid = inObj.getAttribute("cid");
	this.acf = inObj.getAttribute("acf");
	this.from = inObj.getAttribute("from");
	this.to = inObj.getAttribute("to");
	this.route = inObj.getAttribute("route");
	this.lat = inObj.getAttribute("lat");
	this.lng = inObj.getAttribute("lng");
	this.alt = inObj.getAttribute("alt");
	this.gs = inObj.getAttribute("gs");
	this.hdg = inObj.getAttribute("hdg");
	this.freq = inObj.getAttribute("freq");
	this.marker = createMarker(this);
	this.trackPolyLine = null;
	this.trackPoints = new Array();
	this.followOnMap = false;
	this.flightPlan = null;

	this.update = function(inObj){
		if ( (this.lat - inObj.lat != 0) && (this.lng - inObj.lng != null)) {
			//update Heading, Marker position and fligh path (track) only when position has changed
			var newHdg = LatLon.bearing(parseFloat(this.lat), parseFloat(this.lng), parseFloat(inObj.lat), parseFloat(inObj.lng));
			this.hdg = newHdg.toFixed(0);
			
			this.lat = inObj.lat;
			this.lng = inObj.lng;
			
			this.marker.setLatLng(new GLatLng(parseFloat(this.lat), parseFloat(this.lng)));
			
			this.trackPoints.push(this.marker.getLatLng());
			
			if (this.trackPoints.length > 1){
				if (this.trackPolyLine == null) {
					this.trackPolyLine = new GPolyline(this.trackPoints, "#FF00FF", 3, 0.5, {geodesic:fsdmap.isGeodesic});
				} else {
					try {
						debug("try to add new node to track: " + this.marker.getLatLng());
						this.trackPolyLine.insertVertex(this.trackPolyLine.getVertexCount(), this.marker.getLatLng());				
					} catch (e) {
						// TODO: handle exception
						debug("error on adding to the track: " + e.message);
					}					
				}
			}
			
		}
		this.type = inObj.type;
		// this.cs = inObj.cs; //don't change the callsign, its the identifier
		this.name = inObj.name;
		if (this.acf != inObj.acf){
			this.acf = inObj.acf;
		}

		this.from = inObj.from;
		this.to = inObj.to;
		this.route = inObj.route;
		this.alt = inObj.alt;
		this.gs = inObj.gs;
		this.freq = inObj.freq;

		try {
			this.marker.setImage(getIconImage(this));
		} catch (e) {
			// TODO: handle exception
			debug("error on setting marker image: " + e.message, true);
		}
		//this.trackData.push(inObj.trackData);
	};

}
