# A3XX FMGC Waypoint database
# Copyright (c) 2022 Josh Davidson (Octal450) and Jonathan Redpath (legoboyvdlp)

var nilTree = {
	"latitude": 0,
	"longitude": 0,
	"ident": "",
};

var WaypointDatabase = {
	waypointsVec: [],
	confirm: [0, 0],
	# addWP - adds pilot waypoint to waypoints vector
	# arg: wpObj - passed pilot waypoint object
	# return: 
	#  0 - not allowed
	#  2 - accepted
	#  4 - database full
	addWP: func(wpObj) {
		# validate ghost
		if (wpObj.wpGhost == nil) {
			return 0;
		}
		
		# check size of database
		if (me.getCount() >= 20) { 
			return 4;
		}
		
		if (wpObj.index >= me.getSize()) {
			# add to end, since index doesn't exist
			append(me.waypointsVec, wpObj);
			me.write();
			return 2;
		} elsif (me.waypointsVec[wpObj.index] == nil) {
			# add at passed index
			me.waypointsVec[wpObj.index] = wpObj;
			me.write();
			return 2;
		} else {
			# fall back to end
			logprint(4, "pilotWaypoint constructor claims index " ~ wpObj.index ~ " is nil, but it isn't!");
			append(me.waypointsVec, wpObj);
			me.write();
			return 2;
		}
		
	},
	# delete - empties waypoints vector
	# callerIdx is the calling mcdu
	delete: func(callerIdx) {
		var noDel = 0;
		for (var i = 0; i < me.getSize(); i = i + 1) {
			if (me.waypointsVec[i] != nil) {
				if (fmgc.flightPlanController.flightplans[2].indexOfWP(me.waypointsVec[i].wpGhost) == -1) { # docs says only checks active and secondary
					me.waypointsVec[i] = nil;
				}
			}
		}
		me.write();
		if (me.getCount() != 0) {
			mcdu.mcdu_message(callerIdx, "PILOT ELEMENT RETAINED");
		}
	},
	# deleteAtIndex - delete at specific index. Set to nil, so it still exists in vector
	deleteAtIndex: func(index) {
		if (index < 0 or index >= me.getSize() or index >= 20) {
			return;
		}
		me.waypointsVec[index] = nil;
		me.write();
	},
	# getNilIndex - find the first nil
	# post 2020.1 use dedicated function vecindex()
	getNilIndex: func() {
		for (var i = 0; i < me.getSize(); i = i + 1) {
			if (me.waypointsVec[i] == nil) {
				return i;
			}
		}
		return -1;
	},
	# getNonNilIndex - find the first non-nil
	# post 2020.1 use dedicated function vecindex()
	getNonNilIndex: func() {
		for (var i = 0; i < me.getSize(); i = i + 1) {
			if (me.waypointsVec[i] != nil) {
				return i;
			}
		}
		return -1;
	},
	# getNextFromIndex - find the next non-nil after a passed index
	getNextFromIndex: func(index) {
		for (var i = (index + 1); i < me.getSize(); i = i + 1) {
			if (me.waypointsVec[i] != nil) {
				return i;
			}
		}
		
		for (var i = 0; i <= index; i = i + 1) {
			if (me.waypointsVec[i] != nil) {
				return i;
			}
		}
		return index;
	},
	# getPreviousFromIndex - find the next non-nil before a passed index
	getPreviousFromIndex: func(index) {
		for (var i = (index - 1); i >= 0; i = i - 1) {
			if (me.waypointsVec[i] != nil) {
				return i;
			}
		}
		
		for (var i = (me.getSize() - 1); i >= index; i = i - 1) {
			if (me.waypointsVec[i] != nil) {
				return i;
			}
		}
		return index;
	},
	# getNoOfIndex - return what number passed item is in list, neglecting "nil"
	getNoOfIndex: func(index) {
		var count = 0;
		for (var i = 0; i <= index; i = i + 1) {
			if (me.waypointsVec[i] == nil) {
				continue;
			}
			count += 1;
		}
		return count;
	},
	# getCount - return size, neglecting "nil"
	getCount: func() {
		var count = 0;
		for (var i = 0; i < me.getSize(); i = i + 1) {
			if (me.waypointsVec[i] == nil) {
				continue;
			}
			count += 1;
		}
		return count;
	},
	# getSize - return maximum size of vector
	getSize: func() {
		return size(me.waypointsVec);
	},
	# getWP - try to find waypoint whose name matches passed argument
	getWP: func(text) {
		for (var i = 0; i < me.getSize(); i = i + 1) {
			if (me.waypointsVec[i] == nil) {
				continue;
			}
			if (text == me.waypointsVec[i].wpGhost.wp_name) {
				return me.waypointsVec[i].wpGhost;
			}
		}
		return nil;
	},
	# write - write to file, as a hash structure
	write: func() {
		var path = pts.Sim.fgHome.getValue() ~ "/Export/A320SavedWaypoints.xml";
		var tree = {
			waypoints: {
			
			},
		};
		
		for (var i = 0; i < me.getSize(); i = i + 1) {
			if (me.waypointsVec[i] != nil) {
				tree.waypoints["waypoint" ~ i] = me.waypointsVec[i].tree;
			}
		}
		
		io.writexml(path, props.Node.new(tree)); # write the data
	},
	# read - read from a file, extract using props interface
	read: func() {
		var path = pts.Sim.fgHome.getValue() ~ "/Export/A320SavedWaypoints.xml";
		# create file if it doesn't exist
		if (io.stat(path) == nil) {
			me.write();
			return;
		}
		var data = io.readxml(path).getChild("waypoints");
		var pilotWP = nil;
		for (var i = 0; i < 20; i = i + 1) {
			pilotWP = nil;
			var childNode = data.getChild("waypoint" ~ i); 
			if (childNode == nil) { 
				continue; 
			}
			
			var wpt = createWP({lat: num(childNode.getChild("latitude").getValue()), lon: num(childNode.getChild("longitude").getValue())},childNode.getChild("ident").getValue());
			
			if (left(childNode.getChild("ident").getValue(), 3) == "PBD") {
				pilotWP = pilotWaypoint.newAtPosition(wpt, "PBD", right(childNode.getChild("ident").getValue(), 1));
			} else {
				pilotWP = pilotWaypoint.newAtPosition(wpt, "LL", right(childNode.getChild("ident").getValue(), 1));
			}
			me.addWPToPos(pilotWP, right(childNode.getChild("ident").getValue(), 1));
		}
	},
	# addWPToPos - helper for reading - inserts at specific index
	# will create nil for intermediates
	addWPToPos: func(wpObj, position) {
		if (me.getSize() >= position) {
			me.waypointsVec[position - 1] = wpObj;
		} else {
			var numToIns = position - me.getSize();
			while (numToIns >= 1) {
				append(me.waypointsVec, nil);
				numToIns -= 1;
			}
			me.waypointsVec[position - 1] = wpObj;
		}
	},
};

var pilotWaypoint = {
	new: func(positioned, typeStr) {
		var pilotWp = { parents:[pilotWaypoint] };
		
		# Figure out what the first index is we can use
		var nilIndex = WaypointDatabase.getNilIndex();
		var position = nil;
		
		if (nilIndex == -1) {
			position = WaypointDatabase.getSize() + 1;
		} else {
			position = nilIndex + 1
		}
		
		pilotWp.setId(typeStr ~ sprintf("%s", position));
		pilotWp.index = position - 1;
		
		# set ghost to created waypoint
		pilotWp.wpGhost = createWP(positioned, pilotWp.id);
		
		pilotWp.tree = {
			"latitude": pilotWp.wpGhost.wp_lat,
			"longitude": pilotWp.wpGhost.wp_lon,
			"ident": pilotWp.id,
		};
		
		return pilotWp;
	},
	newAtPosition: func(positioned, typeStr, position) {
		var pilotWp = { parents:[pilotWaypoint] };
		
		pilotWp.setId(typeStr ~ sprintf("%s", position));
		pilotWp.index = position - 1;
		
		# set ghost to created waypoint
		pilotWp.wpGhost = positioned;
		
		pilotWp.tree = {
			"latitude": pilotWp.wpGhost.wp_lat,
			"longitude": pilotWp.wpGhost.wp_lon,
			"ident": pilotWp.id,
		};
		
		return pilotWp;
	},
	setId: func(id) {
		if (typeof(id) == "scalar") { me.id = id; }
	},
	getId: func() {
		if (me.id != nil) { return id; }
	},
};

setlistener("/MCDU[0]/page", func() {
	if (getprop("/MCDU[0]/page") != "PILOTWP" and getprop("/MCDU[0]/page") != "STATUS") {
		WaypointDatabase.confirm[0] = 0;
	}
}, 0, 0);

setlistener("/MCDU[1]/page", func() {
	if (getprop("/MCDU[1]/page") != "PILOTWP" and getprop("/MCDU[1]/page") != "STATUS") {
		WaypointDatabase.confirm[1] = 0;
	}
}, 0, 0);