From 5affa1334856c4b20718a823bf9865e6b4eb30e8 Mon Sep 17 00:00:00 2001
From: James Turner <zakalawe@mac.com>
Date: Wed, 9 Jan 2019 00:02:46 +0000
Subject: [PATCH] Nasal: expose airways data and via options

---
 src/Scripting/NasalPositioned.cxx | 223 ++++++++++++++++++++++++++++--
 1 file changed, 215 insertions(+), 8 deletions(-)

diff --git a/src/Scripting/NasalPositioned.cxx b/src/Scripting/NasalPositioned.cxx
index e4227950f..ef4a94b96 100644
--- a/src/Scripting/NasalPositioned.cxx
+++ b/src/Scripting/NasalPositioned.cxx
@@ -18,9 +18,7 @@
 // along with this program; if not, write to the Free Software
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
-#ifdef HAVE_CONFIG_H
-#  include "config.h"
-#endif
+#include "config.h"
 
 #include <cstring>
 
@@ -111,6 +109,12 @@ static naGhostType ProcedureGhostType = { routeBaseGhostDestroy,
   procedureGhostGetMember,
   0};
 
+static const char* airwayGhostGetMember(naContext c, void* g, naRef field, naRef* out);
+static naGhostType AirwayGhostType = { routeBaseGhostDestroy,
+  "airway",
+  airwayGhostGetMember,
+  0};
+
 static void hashset(naContext c, naRef hash, const char* key, naRef val)
 {
   naRef s = naNewString(c);
@@ -260,6 +264,13 @@ static FlightPlan* flightplanGhost(naRef r)
   return 0;
 }
 
+static Airway* airwayGhost(naRef r)
+{
+  if (naGhost_type(r) == &AirwayGhostType)
+    return (Airway*) naGhost_ptr(r);
+  return 0;
+}
+
 static void routeBaseGhostDestroy(void* g)
 {
     RouteBase* r = (RouteBase*) g;
@@ -272,6 +283,7 @@ static naRef flightplanPrototype;
 static naRef geoCoordClass;
 static naRef fpLegPrototype;
 static naRef procedurePrototype;
+static naRef airwayPrototype;
 
 naRef ghostForAirport(naContext c, const FGAirport* apt)
 {
@@ -373,6 +385,17 @@ naRef ghostForProcedure(naContext c, const Procedure* proc)
   return naNewGhost2(c, &ProcedureGhostType, (void*) proc);
 }
 
+naRef ghostForAirway(naContext c, const Airway* awy)
+{
+  if (!awy) {
+    return naNil();
+  }
+
+  Airway::get(awy); // take a ref
+  return naNewGhost2(c, &AirwayGhostType, (void*) awy);
+}
+
+
 static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out)
 {
   const char* fieldName = naStr_data(field);
@@ -432,6 +455,9 @@ static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRe
 static naRef waypointNavaid(naContext c, Waypt* wpt)
 {
   FGPositioned* pos = wpt->source();
+  if (pos && pos->type() == FGPositioned::FIX) {
+     return ghostForFix(c, fgpositioned_cast<FGFix>(pos));
+  }
 
   if (!FGPositioned::isNavaidType(pos)) {
     return naNil();
@@ -476,10 +502,28 @@ static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char*
   else if (!strcmp(fieldName, "wp_lon") || !strcmp(fieldName, "lon")) *out = naNum(wpt->position().getLongitudeDeg());
   else if (!strcmp(fieldName, "wp_parent_name")) {
     Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
-    *out = proc ? stringToNasal(c, proc->ident()) : naNil();
+    if (proc) {
+      *out = stringToNasal(c, proc->ident());
+    } else {
+      Airway* airway = dynamic_cast<Airway*>(wpt->owner());
+      if (airway) {
+        *out = stringToNasal(c, airway->ident());
+      } else {
+        *out = naNil();
+      }
+    }
   } else if (!strcmp(fieldName, "wp_parent")) {
     Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
-    *out = ghostForProcedure(c, proc);
+    if (proc) {
+      *out = ghostForProcedure(c, proc);
+    } else {
+      Airway* airway = dynamic_cast<Airway*>(wpt->owner());
+      if (airway) {
+        *out = ghostForAirway(c, airway);
+      } else {
+        *out = naNil();
+      }
+    }
   } else if (!strcmp(fieldName, "fly_type")) {
     if (wpt->type() == "hold") {
       *out = stringToNasal(c, "Hold");
@@ -494,6 +538,14 @@ static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char*
     *out = waypointAirport(c, wpt);
   } else if (!strcmp(fieldName, "runway")) {
     *out = waypointRunway(c, wpt);
+  } else if (!strcmp(fieldName, "airway")) {
+    if (wpt->type() == "via") {
+      AirwayRef awy = static_cast<Via*>(wpt)->airway();
+      assert(awy);
+      *out = ghostForAirway(c, awy);
+    } else {
+      *out = naNil();
+    }
   } else if (wpt->type() == "hold") {
     // hold-specific properties
     const auto hold = static_cast<Hold*>(wpt);
@@ -941,6 +993,30 @@ static const char* procedureGhostGetMember(naContext c, void* g, naRef field, na
   return "";
 }
 
+static const char* airwayGhostGetMember(naContext c, void* g, naRef field, naRef* out)
+{
+  const char* fieldName = naStr_data(field);
+  Airway* awy = (Airway*) g;
+
+  if (!strcmp(fieldName, "parents")) {
+    *out = naNewVector(c);
+    naVec_append(*out, airwayPrototype);
+  } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, awy->ident());
+  else if (!strcmp(fieldName, "level")) {
+    const auto level = awy->level();
+    switch (level) {
+    case Airway::HighLevel:       *out = stringToNasal(c, "high"); break;
+    case Airway::LowLevel:        *out = stringToNasal(c, "low"); break;
+    case Airway::Both:            *out = stringToNasal(c, "both"); break;
+    default:                      *out = naNil();
+    }
+  } else {
+    return 0;
+  }
+
+  return "";
+}
+
 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out)
 {
   const char* fieldName = naStr_data(field);
@@ -1114,6 +1190,11 @@ static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
       result = wayptGhost(args[offset])->position();
       return 1;
     }
+      
+      if (gt == &FPLegGhostType) {
+          result = fpLegGhost(args[offset])->waypoint()->position();
+          return 1;
+      }
   }
 
   if (geodFromHash(args[offset], result)) {
@@ -2350,6 +2431,64 @@ static naRef f_airwaySearch(naContext c, naRef me, int argc, naRef* args)
   return convertWayptVecToNasal(c, route);
 }
 
+static FGPositionedRef positionedFromArg(naRef ref)
+{
+  if (!naIsGhost(ref)) 
+    return {};
+
+  naGhostType* gt = naGhost_type(ref);
+  if (gt == &AirportGhostType)
+    return airportGhost(ref);
+
+  if (gt == &NavaidGhostType)
+    return navaidGhost(ref);
+
+  if (gt == &RunwayGhostType)
+    return runwayGhost(ref);
+
+  if (gt == &TaxiwayGhostType)
+    return taxiwayGhost(ref);
+
+  if (gt == &FixGhostType)
+    return fixGhost(ref);
+
+  if ((gt == &WayptGhostType) || (gt == &FPLegGhostType))
+    return wayptGhost(ref)->source();
+
+  return {};
+}
+
+static naRef f_findAirway(naContext c, naRef me, int argc, naRef* args)
+{
+  if ((argc < 1) || !naIsString(args[0])) {
+    naRuntimeError(c, "findAirway needs at least one string arguments");
+  }
+
+  std::string ident = naStr_data(args[0]);
+  FGPositionedRef pos;
+  Airway::Level level = Airway::Both;
+  if (argc >= 2) {
+    pos = positionedFromArg(args[1]);
+    if (naIsString(args[1])) {
+      // level spec, 
+    }
+  }
+
+  AirwayRef awy;
+  if (pos) {
+    SG_LOG(SG_NASAL, SG_INFO, "Pevious navaid for airway():" << pos->ident());
+    awy = Airway::findByIdentAndNavaid(ident, pos);
+  } else {
+    awy = Airway::findByIdent(ident, level);
+  }
+
+  if (!awy)
+    return naNil();
+
+  return ghostForAirway(c, awy.get());
+}
+
+
 static naRef f_createWP(naContext c, naRef me, int argc, naRef* args)
 {
   SGGeod pos;
@@ -2412,9 +2551,10 @@ static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args)
     }
 
     std::string airwayName = naStr_data(args[0]);
-    Airway* airway = Airway::findByIdent(airwayName, Airway::UnknownLevel);
+    AirwayRef airway = Airway::findByIdent(airwayName, Airway::Both);
     if (!airway) {
-        naRuntimeError(c, "createViaTo: couldn't find airway with provided name");
+        naRuntimeError(c, "createViaTo: couldn't find airway with provided name: %s", 
+          naStr_data(args[0]));
     }
 
     FGPositionedRef nav;
@@ -2437,7 +2577,50 @@ static naRef f_createViaTo(naContext c, naRef me, int argc, naRef* args)
         naRuntimeError(c, "createViaTo: navaid not on airway");
     }
 
-    Via* via = new Via(NULL, airwayName, nav);
+    Via* via = new Via(nullptr, airway, nav);
+    return ghostForWaypt(c, via);
+}
+
+static naRef f_createViaFromTo(naContext c, naRef me, int argc, naRef* args)
+{
+    if (argc != 3) {
+        naRuntimeError(c, "createViaFromTo: needs exactly three arguments");
+    }
+
+    auto from = positionedFromArg(args[0]);
+    if (!from) {
+      naRuntimeError(c, "createViaFromTo: from wp not found");
+    }
+
+    std::string airwayName = naStr_data(args[1]);
+    AirwayRef airway = Airway::findByIdentAndNavaid(airwayName, from);
+    if (!airway) {
+        naRuntimeError(c, "createViaFromTo: couldn't find airway with provided name: %s from wp %s", 
+          naStr_data(args[0]), 
+          from->ident().c_str());
+    }
+
+    FGPositionedRef nav;
+    if (naIsString(args[2])) {
+        WayptRef enroute = airway->findEnroute(naStr_data(args[2]));
+        if (!enroute) {
+            naRuntimeError(c, "unknown waypoint on airway %s: %s",
+                           naStr_data(args[1]), naStr_data(args[2]));
+        }
+
+        nav = enroute->source();
+    } else {
+        nav = positionedFromArg(args[2]);
+        if (!nav) {
+            naRuntimeError(c, "createViaFromTo: arg[2] is not a navaid");
+        }
+    }
+
+    if (!airway->containsNavaid(nav)) {
+        naRuntimeError(c, "createViaFromTo: navaid not on airway");
+    }
+
+    Via* via = new Via(nullptr, airway, nav);
     return ghostForWaypt(c, via);
 }
 
@@ -2926,6 +3109,24 @@ static naRef f_procedure_route(naContext c, naRef me, int argc, naRef* args)
   return convertWayptVecToNasal(c, r);
 }
 
+static naRef f_airway_contains(naContext c, naRef me, int argc, naRef* args)
+{
+  Airway* awy = airwayGhost(me);
+  if (!awy) {
+    naRuntimeError(c, "airway.contains called on non-airway object");
+  }
+
+  if (argc < 1) {
+    naRuntimeError(c, "missing arg to airway.contains");
+  }
+
+  auto pos = positionedFromArg(args[0]);
+  if (!pos) {
+    return naNum(0);
+  }
+
+  return naNum(awy->containsNavaid(pos));
+}
 
 // Table of extension functions.  Terminate with zeros.
 static struct { const char* name; naCFunction func; } funcs[] = {
@@ -2953,8 +3154,10 @@ static struct { const char* name; naCFunction func; } funcs[] = {
   { "createWP", f_createWP },
   { "createWPFrom", f_createWPFrom },
   { "createViaTo", f_createViaTo },
+  { "createViaFromTo", f_createViaFromTo },
   { "createDiscontinuity", f_createDiscontinuity },
   { "airwaysRoute", f_airwaySearch },
+  { "airway", f_findAirway },
   { "magvar", f_magvar },
   { "courseAndDistance", f_courseAndDistance },
   { "greatCircleMove", f_greatCircleMove },
@@ -3021,6 +3224,10 @@ naRef initNasalPositioned(naRef globals, naContext c)
     hashset(c, fpLegPrototype, "path", naNewFunc(c, naNewCCode(c, f_leg_path)));
     hashset(c, fpLegPrototype, "courseAndDistanceFrom", naNewFunc(c, naNewCCode(c, f_leg_courseAndDistanceFrom)));
 
+    airwayPrototype = naNewHash(c);
+    naSave(c, airwayPrototype);
+    hashset(c, airwayPrototype, "contains", naNewFunc(c, naNewCCode(c, f_airway_contains)));
+
     for(int i=0; funcs[i].name; i++) {
       hashset(c, globals, funcs[i].name,
       naNewFunc(c, naNewCCode(c, funcs[i].func)));