]> git.mxchange.org Git - flightgear.git/blob - src/Scripting/NasalPositioned.cxx
Canvas: Proper fix for OpenVG init handling.
[flightgear.git] / src / Scripting / NasalPositioned.cxx
1 // NasalPositioned.cxx -- expose FGPositioned classes to Nasal
2 //
3 // Written by James Turner, started 2012.
4 //
5 // Copyright (C) 2012 James Turner
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #endif
24
25 #include <string.h>
26
27 #include "NasalPositioned.hxx"
28
29 #include <boost/foreach.hpp>
30 #include <boost/algorithm/string/case_conv.hpp>
31
32 #include <simgear/sg_inlines.h>
33 #include <simgear/scene/material/mat.hxx>
34 #include <simgear/magvar/magvar.hxx>
35 #include <simgear/timing/sg_time.hxx>
36 #include <simgear/bucket/newbucket.hxx>
37
38 #include <Airports/runways.hxx>
39 #include <Airports/simple.hxx>
40 #include <Airports/dynamics.hxx>
41 #include <Airports/parking.hxx>
42 #include <Scripting/NasalSys.hxx>
43 #include <Navaids/navlist.hxx>
44 #include <Navaids/procedure.hxx>
45 #include <Main/globals.hxx>
46 #include <Main/fg_props.hxx>
47 #include <Scenery/scenery.hxx>
48 #include <ATC/CommStation.hxx>
49 #include <Navaids/FlightPlan.hxx>
50 #include <Navaids/waypoint.hxx>
51 #include <Navaids/fix.hxx>
52 #include <Autopilot/route_mgr.hxx>
53 #include <Navaids/routePath.hxx>
54 #include <Navaids/procedure.hxx>
55 #include <Navaids/airways.hxx>
56
57 using namespace flightgear;
58
59 static void positionedGhostDestroy(void* g);
60 static void wayptGhostDestroy(void* g);
61 static void legGhostDestroy(void* g);
62 static void routeBaseGhostDestroy(void* g);
63
64 naGhostType PositionedGhostType = { positionedGhostDestroy, "positioned" };
65
66 static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out);
67 naGhostType AirportGhostType = { positionedGhostDestroy, "airport", airportGhostGetMember, 0 };
68
69 static const char* navaidGhostGetMember(naContext c, void* g, naRef field, naRef* out);
70 naGhostType NavaidGhostType = { positionedGhostDestroy, "navaid", navaidGhostGetMember, 0 };
71
72 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out);
73 naGhostType RunwayGhostType = { positionedGhostDestroy, "runway", runwayGhostGetMember, 0 };
74 naGhostType TaxiwayGhostType = { positionedGhostDestroy, "taxiway", runwayGhostGetMember, 0 };
75
76 static const char* fixGhostGetMember(naContext c, void* g, naRef field, naRef* out);
77 naGhostType FixGhostType = { positionedGhostDestroy, "fix", fixGhostGetMember, 0 };
78
79 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out);
80 static void waypointGhostSetMember(naContext c, void* g, naRef field, naRef value);
81
82 naGhostType WayptGhostType = { wayptGhostDestroy, 
83   "waypoint",
84   wayptGhostGetMember,
85   waypointGhostSetMember};
86
87 static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out);
88 static void legGhostSetMember(naContext c, void* g, naRef field, naRef value);
89
90 naGhostType FPLegGhostType = { legGhostDestroy, 
91   "flightplan-leg",
92   legGhostGetMember,
93   legGhostSetMember};
94
95 static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out);
96 static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value);
97
98 naGhostType FlightPlanGhostType = { routeBaseGhostDestroy, 
99   "flightplan",
100   flightplanGhostGetMember,
101   flightplanGhostSetMember
102 };
103
104 static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out);
105 naGhostType ProcedureGhostType = { routeBaseGhostDestroy, 
106   "procedure",
107   procedureGhostGetMember,
108   0};
109
110 static void hashset(naContext c, naRef hash, const char* key, naRef val)
111 {
112   naRef s = naNewString(c);
113   naStr_fromdata(s, (char*)key, strlen(key));
114   naHash_set(hash, s, val);
115 }
116
117 static naRef stringToNasal(naContext c, const std::string& s)
118 {
119     return naStr_fromdata(naNewString(c),
120                    const_cast<char *>(s.c_str()), 
121                    s.length());
122 }
123
124 static WayptFlag wayptFlagFromString(const char* s)
125 {
126   if (!strcmp(s, "sid")) return WPT_DEPARTURE;
127   if (!strcmp(s, "star")) return WPT_ARRIVAL;
128   if (!strcmp(s, "approach")) return WPT_APPROACH;
129   if (!strcmp(s, "missed")) return WPT_MISS;
130   if (!strcmp(s, "pseudo")) return WPT_PSEUDO;
131   
132   return (WayptFlag) 0;
133 }
134
135 static naRef wayptFlagToNasal(naContext c, unsigned int flags)
136 {
137   if (flags & WPT_PSEUDO) return stringToNasal(c, "pseudo");
138   if (flags & WPT_DEPARTURE) return stringToNasal(c, "sid");
139   if (flags & WPT_ARRIVAL) return stringToNasal(c, "star");
140   if (flags & WPT_MISS) return stringToNasal(c, "missed");
141   if (flags & WPT_APPROACH) return stringToNasal(c, "approach");
142   return naNil();
143 }
144
145 static FGPositioned* positionedGhost(naRef r)
146 {
147     if ((naGhost_type(r) == &AirportGhostType) ||
148         (naGhost_type(r) == &NavaidGhostType) ||
149         (naGhost_type(r) == &RunwayGhostType) ||
150         (naGhost_type(r) == &FixGhostType))
151     {
152         return (FGPositioned*) naGhost_ptr(r);
153     }
154   
155     return 0;
156 }
157
158 static FGAirport* airportGhost(naRef r)
159 {
160   if (naGhost_type(r) == &AirportGhostType)
161     return (FGAirport*) naGhost_ptr(r);
162   return 0;
163 }
164
165 static FGNavRecord* navaidGhost(naRef r)
166 {
167   if (naGhost_type(r) == &NavaidGhostType)
168     return (FGNavRecord*) naGhost_ptr(r);
169   return 0;
170 }
171
172 static FGRunway* runwayGhost(naRef r)
173 {
174   if (naGhost_type(r) == &RunwayGhostType)
175     return (FGRunway*) naGhost_ptr(r);
176   return 0;
177 }
178
179 static FGTaxiway* taxiwayGhost(naRef r)
180 {
181   if (naGhost_type(r) == &TaxiwayGhostType)
182     return (FGTaxiway*) naGhost_ptr(r);
183   return 0;
184 }
185
186 static FGFix* fixGhost(naRef r)
187 {
188   if (naGhost_type(r) == &FixGhostType)
189     return (FGFix*) naGhost_ptr(r);
190   return 0;
191 }
192
193
194 static void positionedGhostDestroy(void* g)
195 {
196     FGPositioned* pos = (FGPositioned*)g;
197     if (!FGPositioned::put(pos)) // unref
198         delete pos;
199 }
200
201 static Waypt* wayptGhost(naRef r)
202 {
203   if (naGhost_type(r) == &WayptGhostType)
204     return (Waypt*) naGhost_ptr(r);
205   
206   if (naGhost_type(r) == &FPLegGhostType) {
207     FlightPlan::Leg* leg = (FlightPlan::Leg*) naGhost_ptr(r);
208     return leg->waypoint();
209   }
210   
211   return 0;
212 }
213
214 static void wayptGhostDestroy(void* g)
215 {
216   Waypt* wpt = (Waypt*)g;
217   if (!Waypt::put(wpt)) // unref
218     delete wpt;
219 }
220
221 static void legGhostDestroy(void* g)
222 {
223   // nothing for now
224 }
225
226
227 static FlightPlan::Leg* fpLegGhost(naRef r)
228 {
229   if (naGhost_type(r) == &FPLegGhostType)
230     return (FlightPlan::Leg*) naGhost_ptr(r);
231   return 0;
232 }
233
234 static Procedure* procedureGhost(naRef r)
235 {
236   if (naGhost_type(r) == &ProcedureGhostType)
237     return (Procedure*) naGhost_ptr(r);
238   return 0;
239 }
240
241 static FlightPlan* flightplanGhost(naRef r)
242 {
243   if (naGhost_type(r) == &FlightPlanGhostType)
244     return (FlightPlan*) naGhost_ptr(r);
245   return 0;
246 }
247
248 static void routeBaseGhostDestroy(void* g)
249 {
250   // nothing for now
251 }
252
253 static naRef airportPrototype;
254 static naRef flightplanPrototype;
255 static naRef waypointPrototype;
256 static naRef geoCoordClass;
257 static naRef fpLegPrototype;
258 static naRef procedurePrototype;
259
260 naRef ghostForAirport(naContext c, const FGAirport* apt)
261 {
262   if (!apt) {
263     return naNil();
264   }
265   
266   FGPositioned::get(apt); // take a ref
267   return naNewGhost2(c, &AirportGhostType, (void*) apt);
268 }
269
270 naRef ghostForNavaid(naContext c, const FGNavRecord* n)
271 {
272   if (!n) {
273     return naNil();
274   }
275   
276   FGPositioned::get(n); // take a ref
277   return naNewGhost2(c, &NavaidGhostType, (void*) n);
278 }
279
280 naRef ghostForRunway(naContext c, const FGRunway* r)
281 {
282   if (!r) {
283     return naNil();
284   }
285   
286   FGPositioned::get(r); // take a ref
287   return naNewGhost2(c, &RunwayGhostType, (void*) r);
288 }
289
290 naRef ghostForTaxiway(naContext c, const FGTaxiway* r)
291 {
292   if (!r) {
293     return naNil();
294   }
295   
296   FGPositioned::get(r); // take a ref
297   return naNewGhost2(c, &TaxiwayGhostType, (void*) r);
298 }
299
300 naRef ghostForFix(naContext c, const FGFix* r)
301 {
302   if (!r) {
303     return naNil();
304   }
305   
306   FGPositioned::get(r); // take a ref
307   return naNewGhost2(c, &FixGhostType, (void*) r);
308 }
309
310
311 naRef ghostForWaypt(naContext c, const Waypt* wpt)
312 {
313   if (!wpt) {
314     return naNil();
315   }
316   
317   Waypt::get(wpt); // take a ref
318   return naNewGhost2(c, &WayptGhostType, (void*) wpt);
319 }
320
321 naRef ghostForLeg(naContext c, const FlightPlan::Leg* leg)
322 {
323   if (!leg) {
324     return naNil();
325   }
326   
327   return naNewGhost2(c, &FPLegGhostType, (void*) leg);
328 }
329
330 naRef ghostForFlightPlan(naContext c, const FlightPlan* fp)
331 {
332   if (!fp) {
333     return naNil();
334   }
335   
336   return naNewGhost2(c, &FlightPlanGhostType, (void*) fp);
337 }
338
339 naRef ghostForProcedure(naContext c, const Procedure* proc)
340 {
341   if (!proc) {
342     return naNil();
343   }
344   
345   return naNewGhost2(c, &ProcedureGhostType, (void*) proc);
346 }
347
348 static const char* airportGhostGetMember(naContext c, void* g, naRef field, naRef* out)
349 {
350   const char* fieldName = naStr_data(field);
351   FGAirport* apt = (FGAirport*) g;
352   
353   if (!strcmp(fieldName, "parents")) {
354     *out = naNewVector(c);
355     naVec_append(*out, airportPrototype);
356   } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, apt->ident());
357   else if (!strcmp(fieldName, "name")) *out = stringToNasal(c, apt->name());
358   else if (!strcmp(fieldName, "lat")) *out = naNum(apt->getLatitude());
359   else if (!strcmp(fieldName, "lon")) *out = naNum(apt->getLongitude());
360   else if (!strcmp(fieldName, "elevation")) {
361     *out = naNum(apt->getElevation() * SG_FEET_TO_METER);
362   } else if (!strcmp(fieldName, "has_metar")) {
363     *out = naNum(apt->getMetar());
364   } else if (!strcmp(fieldName, "runways")) {
365     *out = naNewHash(c);
366     double minLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft");
367     for(unsigned int r=0; r<apt->numRunways(); ++r) {
368       FGRunway* rwy(apt->getRunwayByIndex(r));
369       
370     // ignore unusably short runways
371       if (rwy->lengthFt() < minLengthFt) {
372         continue;
373       }
374       
375       naRef rwyid = stringToNasal(c, rwy->ident());
376       naRef rwydata = ghostForRunway(c, rwy);
377       naHash_set(*out, rwyid, rwydata);
378     }
379
380   } else if (!strcmp(fieldName, "taxiways")) {
381     *out = naNewVector(c);
382     for(unsigned int r=0; r<apt->numTaxiways(); ++r) {
383       FGTaxiway* taxi(apt->getTaxiwayByIndex(r));
384       naRef taxidata = ghostForTaxiway(c, taxi);
385       naVec_append(*out, taxidata);
386     }
387
388   } else {
389     return 0;
390   }
391   
392   return "";
393 }
394
395 static const char* waypointCommonGetMember(naContext c, Waypt* wpt, const char* fieldName, naRef* out)
396 {
397   if (!strcmp(fieldName, "wp_name")) *out = stringToNasal(c, wpt->ident());
398   else if (!strcmp(fieldName, "wp_type")) *out = stringToNasal(c, wpt->type());
399   else if (!strcmp(fieldName, "wp_role")) *out = wayptFlagToNasal(c, wpt->flags());
400   else if (!strcmp(fieldName, "wp_lat")) *out = naNum(wpt->position().getLatitudeDeg());
401   else if (!strcmp(fieldName, "wp_lon")) *out = naNum(wpt->position().getLongitudeDeg());
402   else if (!strcmp(fieldName, "wp_parent_name")) {
403     Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
404     *out = proc ? stringToNasal(c, proc->ident()) : naNil();
405   } else if (!strcmp(fieldName, "wp_parent")) {
406     Procedure* proc = dynamic_cast<Procedure*>(wpt->owner());
407     *out = ghostForProcedure(c, proc);
408   } else if (!strcmp(fieldName, "fly_type")) {
409     if (wpt->type() == "hold") {
410       *out = stringToNasal(c, "Hold");
411     } else {
412       *out = stringToNasal(c, wpt->flag(WPT_OVERFLIGHT) ? "flyOver" : "flyBy");
413     }
414   } else {
415     return NULL; // member not found
416   }
417
418   return "";
419 }
420
421 static void waypointCommonSetMember(naContext c, Waypt* wpt, const char* fieldName, naRef value)
422 {
423   if (!strcmp(fieldName, "wp_role")) {
424     if (!naIsString(value)) naRuntimeError(c, "wp_role must be a string");
425     if (wpt->owner() != NULL) naRuntimeError(c, "cannot override wp_role on waypoint with parent");
426     WayptFlag f = wayptFlagFromString(naStr_data(value));
427     if (f == 0) {
428       naRuntimeError(c, "unrecognized wp_role value %s", naStr_data(value));
429     }
430     
431     wpt->setFlag(f, true);
432   }
433 }
434
435 static const char* wayptGhostGetMember(naContext c, void* g, naRef field, naRef* out)
436 {
437   const char* fieldName = naStr_data(field);
438   Waypt* wpt = (flightgear::Waypt*) g;
439   return waypointCommonGetMember(c, wpt, fieldName, out);
440 }
441
442 static RouteRestriction routeRestrictionFromString(const char* s)
443 {
444   string u(s);
445   boost::to_lower(u);
446   if (u == "computed") return RESTRICT_COMPUTED;
447   if (u == "at") return RESTRICT_AT;
448   if (u == "mach") return SPEED_RESTRICT_MACH;
449   if (u == "computed-mach") return SPEED_COMPUTED_MACH;
450   if (u == "delete") return RESTRICT_DELETE;
451   return RESTRICT_NONE;
452 };
453
454 naRef routeRestrictionToNasal(naContext c, RouteRestriction rr)
455 {
456   switch (rr) {
457     case RESTRICT_NONE: return naNil();
458     case RESTRICT_AT: return stringToNasal(c, "at");
459     case RESTRICT_ABOVE: return stringToNasal(c, "above");
460     case RESTRICT_BELOW: return stringToNasal(c, "below");
461     case SPEED_RESTRICT_MACH: return stringToNasal(c, "mach");
462     case RESTRICT_COMPUTED: return stringToNasal(c, "computed");
463     case SPEED_COMPUTED_MACH: return stringToNasal(c, "computed-mach");
464     case RESTRICT_DELETE: return stringToNasal(c, "delete");
465   }
466   
467   return naNil();
468 }
469
470 static const char* legGhostGetMember(naContext c, void* g, naRef field, naRef* out)
471 {
472   const char* fieldName = naStr_data(field);
473   FlightPlan::Leg* leg = (FlightPlan::Leg*) g;
474   Waypt* wpt = leg->waypoint();
475   
476   if (!strcmp(fieldName, "parents")) {
477     *out = naNewVector(c);
478     naVec_append(*out, fpLegPrototype);
479   } else if (!strcmp(fieldName, "index")) {
480     *out = naNum(leg->index());
481   } else if (!strcmp(fieldName, "alt_cstr")) {
482     *out = naNum(leg->altitudeFt());
483   } else if (!strcmp(fieldName, "alt_cstr_type")) {
484     *out = routeRestrictionToNasal(c, leg->altitudeRestriction());
485   } else if (!strcmp(fieldName, "speed_cstr")) {
486     double s = isMachRestrict(leg->speedRestriction()) ? leg->speedMach() : leg->speedKts();
487     *out = naNum(s);
488   } else if (!strcmp(fieldName, "speed_cstr_type")) {
489     *out = routeRestrictionToNasal(c, leg->speedRestriction());  
490   } else if (!strcmp(fieldName, "leg_distance")) {
491     *out = naNum(leg->distanceNm());
492   } else if (!strcmp(fieldName, "leg_bearing")) {
493     *out = naNum(leg->courseDeg());
494   } else if (!strcmp(fieldName, "distance_along_route")) {
495     *out = naNum(leg->distanceAlongRoute());
496   } else { // check for fields defined on the underlying waypoint
497     return waypointCommonGetMember(c, wpt, fieldName, out);
498   }
499   
500   return ""; // success
501 }
502
503 static void waypointGhostSetMember(naContext c, void* g, naRef field, naRef value)
504 {
505   const char* fieldName = naStr_data(field);
506   Waypt* wpt = (Waypt*) g;
507   waypointCommonSetMember(c, wpt, fieldName, value);
508 }
509
510 static void legGhostSetMember(naContext c, void* g, naRef field, naRef value)
511 {
512   const char* fieldName = naStr_data(field);
513   FlightPlan::Leg* leg = (FlightPlan::Leg*) g;
514     
515   waypointCommonSetMember(c, leg->waypoint(), fieldName, value);
516 }
517
518 static const char* flightplanGhostGetMember(naContext c, void* g, naRef field, naRef* out)
519 {
520   const char* fieldName = naStr_data(field);
521   FlightPlan* fp = (FlightPlan*) g;
522   
523   if (!strcmp(fieldName, "parents")) {
524     *out = naNewVector(c);
525     naVec_append(*out, flightplanPrototype);
526   } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, fp->ident());
527   else if (!strcmp(fieldName, "departure")) *out = ghostForAirport(c, fp->departureAirport());
528   else if (!strcmp(fieldName, "destination")) *out = ghostForAirport(c, fp->destinationAirport());
529   else if (!strcmp(fieldName, "departure_runway")) *out = ghostForRunway(c, fp->departureRunway());
530   else if (!strcmp(fieldName, "destination_runway")) *out = ghostForRunway(c, fp->destinationRunway());
531   else if (!strcmp(fieldName, "sid")) *out = ghostForProcedure(c, fp->sid());
532   else if (!strcmp(fieldName, "sid_trans")) *out = ghostForProcedure(c, fp->sidTransition());
533   else if (!strcmp(fieldName, "star")) *out = ghostForProcedure(c, fp->star());
534   else if (!strcmp(fieldName, "star_trans")) *out = ghostForProcedure(c, fp->starTransition());
535   else if (!strcmp(fieldName, "approach")) *out = ghostForProcedure(c, fp->approach());
536   else if (!strcmp(fieldName, "current")) *out = naNum(fp->currentIndex());
537   else {
538     return 0;
539   }
540   
541   return "";
542 }
543
544 static void flightplanGhostSetMember(naContext c, void* g, naRef field, naRef value)
545 {
546   const char* fieldName = naStr_data(field);
547   FlightPlan* fp = (FlightPlan*) g;
548   
549   if (!strcmp(fieldName, "id")) {
550     if (!naIsString(value)) naRuntimeError(c, "flightplan.id must be a string");
551     fp->setIdent(naStr_data(value));
552   } else if (!strcmp(fieldName, "current")) {
553     int index = value.num;
554     if ((index < 0) || (index >= fp->numLegs())) {
555       return;
556     }
557     fp->setCurrentIndex(index);
558   } else if (!strcmp(fieldName, "departure")) {
559     FGAirport* apt = airportGhost(value);
560     if (apt) {
561       fp->setDeparture(apt);
562       return;
563     }
564     
565     FGRunway* rwy = runwayGhost(value);
566     if (rwy){
567       fp->setDeparture(rwy);
568       return;
569     }
570     
571     naRuntimeError(c, "bad argument type setting departure");
572   } else if (!strcmp(fieldName, "destination")) {
573     FGAirport* apt = airportGhost(value);
574     if (apt) {
575       fp->setDestination(apt);
576       return;
577     }
578     
579     FGRunway* rwy = runwayGhost(value);
580     if (rwy){
581       fp->setDestination(rwy);
582       return;
583     }
584     
585     naRuntimeError(c, "bad argument type setting destination");
586   } else if (!strcmp(fieldName, "departure_runway")) {
587     FGRunway* rwy = runwayGhost(value);
588     if (rwy){
589       fp->setDeparture(rwy);
590       return;
591     }
592     
593     naRuntimeError(c, "bad argument type setting departure");
594   } else if (!strcmp(fieldName, "destination_runway")) {
595     FGRunway* rwy = runwayGhost(value);
596     if (rwy){
597       fp->setDestination(rwy);
598       return;
599     }
600     
601     naRuntimeError(c, "bad argument type setting departure");
602   } else if (!strcmp(fieldName, "sid")) {
603     Procedure* proc = procedureGhost(value);
604     if (proc && (proc->type() == PROCEDURE_SID)) {
605       fp->setSID((flightgear::SID*) proc);
606       return;
607     }
608     // allow a SID transition to be set, implicitly include the SID itself
609     if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
610       fp->setSID((Transition*) proc);
611       return;
612     }
613         
614     if (naIsString(value)) {
615       FGAirport* apt = fp->departureAirport();
616       fp->setSID(apt->findSIDWithIdent(naStr_data(value)));
617       return;
618     }
619     
620     naRuntimeError(c, "bad argument type setting SID");
621   } else if (!strcmp(fieldName, "star")) {
622     Procedure* proc = procedureGhost(value);
623     if (proc && (proc->type() == PROCEDURE_STAR)) {
624       fp->setSTAR((STAR*) proc);
625       return;
626     }
627     
628     if (proc && (proc->type() == PROCEDURE_TRANSITION)) {
629       fp->setSTAR((Transition*) proc);
630       return;
631     }
632     
633     if (naIsString(value)) {
634       FGAirport* apt = fp->destinationAirport();
635       fp->setSTAR(apt->findSTARWithIdent(naStr_data(value)));
636       return;
637     }
638     
639     naRuntimeError(c, "bad argument type setting STAR");
640   } else if (!strcmp(fieldName, "approach")) {
641     Procedure* proc = procedureGhost(value);
642     if (proc && Approach::isApproach(proc->type())) {
643       fp->setApproach((Approach*) proc);
644       return;
645     }
646     
647     if (naIsString(value)) {
648       FGAirport* apt = fp->destinationAirport();
649       fp->setApproach(apt->findApproachWithIdent(naStr_data(value)));
650       return;
651     }
652     
653     naRuntimeError(c, "bad argument type setting approach");
654   }
655 }
656
657
658 static naRef procedureTpType(naContext c, ProcedureType ty)
659 {
660   switch (ty) {
661     case PROCEDURE_SID: return stringToNasal(c, "sid");
662     case PROCEDURE_STAR: return stringToNasal(c, "star");
663     case PROCEDURE_APPROACH_VOR: 
664     case PROCEDURE_APPROACH_ILS: 
665     case PROCEDURE_APPROACH_RNAV: 
666     case PROCEDURE_APPROACH_NDB:
667       return stringToNasal(c, "IAP");
668     default:
669       return naNil();
670   }
671 }
672
673 static naRef procedureRadioType(naContext c, ProcedureType ty)
674 {
675   switch (ty) {
676     case PROCEDURE_APPROACH_VOR: return stringToNasal(c, "VOR");
677     case PROCEDURE_APPROACH_ILS: return stringToNasal(c, "ILS");
678     case PROCEDURE_APPROACH_RNAV: return stringToNasal(c, "RNAV");
679     case PROCEDURE_APPROACH_NDB: return stringToNasal(c, "NDB");
680     default:
681       return naNil();
682   }
683 }
684
685 static const char* procedureGhostGetMember(naContext c, void* g, naRef field, naRef* out)
686 {
687   const char* fieldName = naStr_data(field);
688   Procedure* proc = (Procedure*) g;
689   
690   if (!strcmp(fieldName, "parents")) {
691     *out = naNewVector(c);
692     naVec_append(*out, procedurePrototype);
693   } else if (!strcmp(fieldName, "id")) *out = stringToNasal(c, proc->ident());
694   else if (!strcmp(fieldName, "airport")) *out = ghostForAirport(c, proc->airport());
695   else if (!strcmp(fieldName, "tp_type")) *out = procedureTpType(c, proc->type());
696   else if (!strcmp(fieldName, "radio")) *out = procedureRadioType(c, proc->type());
697   else if (!strcmp(fieldName, "runways")) {
698     *out = naNewVector(c);
699     BOOST_FOREACH(FGRunwayRef rwy, proc->runways()) {
700       naVec_append(*out, stringToNasal(c, rwy->ident()));
701     }
702   } else if (!strcmp(fieldName, "transitions")) {
703     if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
704       *out = naNil();
705       return "";
706     }
707         
708     ArrivalDeparture* ad = static_cast<ArrivalDeparture*>(proc);
709     *out = naNewVector(c);
710     BOOST_FOREACH(string id, ad->transitionIdents()) {
711       naVec_append(*out, stringToNasal(c, id));
712     }
713   } else {
714     return 0;
715   }
716   
717   return "";
718 }
719
720 static const char* runwayGhostGetMember(naContext c, void* g, naRef field, naRef* out)
721 {
722   const char* fieldName = naStr_data(field);
723   FGRunwayBase* base = (FGRunwayBase*) g;
724   
725   if (!strcmp(fieldName, "id")) *out = stringToNasal(c, base->ident());
726   else if (!strcmp(fieldName, "lat")) *out = naNum(base->latitude());
727   else if (!strcmp(fieldName, "lon")) *out = naNum(base->longitude());
728   else if (!strcmp(fieldName, "heading")) *out = naNum(base->headingDeg());
729   else if (!strcmp(fieldName, "length")) *out = naNum(base->lengthM());
730   else if (!strcmp(fieldName, "width")) *out = naNum(base->widthM());
731   else if (!strcmp(fieldName, "surface")) *out = naNum(base->surface());
732   else if (base->type() == FGRunwayBase::RUNWAY) {  
733     FGRunway* rwy = (FGRunway*) g;
734     if (!strcmp(fieldName, "threshold")) *out = naNum(rwy->displacedThresholdM());
735     else if (!strcmp(fieldName, "stopway")) *out = naNum(rwy->stopwayM());
736     else if (!strcmp(fieldName, "ils_frequency_mhz")) {
737       *out = rwy->ILS() ? naNum(rwy->ILS()->get_freq() / 100.0) : naNil();
738     } else if (!strcmp(fieldName, "ils")) {
739       *out = ghostForNavaid(c, rwy->ILS());
740     } else {
741       return 0;
742     }
743   } else {
744     return 0;    
745   }
746   
747   return "";
748 }
749
750 static const char* navaidGhostGetMember(naContext c, void* g, naRef field, naRef* out)
751 {
752   const char* fieldName = naStr_data(field);
753   FGNavRecord* nav = (FGNavRecord*) g;
754   
755   if (!strcmp(fieldName, "id")) *out = stringToNasal(c, nav->ident());
756   else if (!strcmp(fieldName, "name")) *out = stringToNasal(c, nav->name());
757   else if (!strcmp(fieldName, "lat")) *out = naNum(nav->get_lat());
758   else if (!strcmp(fieldName, "lon")) *out = naNum(nav->get_lon());
759   else if (!strcmp(fieldName, "elevation")) {
760     *out = naNum(nav->get_elev_ft() * SG_FEET_TO_METER);
761   } else if (!strcmp(fieldName, "type")) {
762     *out = stringToNasal(c, nav->nameForType(nav->type()));
763   } else if (!strcmp(fieldName, "frequency")) {
764     *out = naNum(nav->get_freq()); 
765   } else if (!strcmp(fieldName, "range_nm")) {
766     *out = naNum(nav->get_range()); 
767   } else if (!strcmp(fieldName, "course")) {
768     if ((nav->type() == FGPositioned::ILS) || (nav->type() == FGPositioned::LOC)) {
769       double radial = nav->get_multiuse();
770       SG_NORMALIZE_RANGE(radial, 0.0, 360.0);
771       *out = naNum(radial);
772     } else {
773       *out = naNil();
774     }
775   } else {
776     return 0;
777   }
778   
779   return "";
780 }
781
782 static const char* fixGhostGetMember(naContext c, void* g, naRef field, naRef* out)
783 {
784   const char* fieldName = naStr_data(field);
785   FGFix* fix = (FGFix*) g;
786   
787   if (!strcmp(fieldName, "id")) *out = stringToNasal(c, fix->ident());
788   else if (!strcmp(fieldName, "lat")) *out = naNum(fix->get_lat());
789   else if (!strcmp(fieldName, "lon")) *out = naNum(fix->get_lon());
790   else {
791     return 0;
792   }
793   
794   return "";
795 }
796
797 static bool hashIsCoord(naRef h)
798 {
799   naRef parents = naHash_cget(h, (char*) "parents");
800   if (!naIsVector(parents)) {
801     return false;
802   }
803   
804   return naEqual(naVec_get(parents, 0), geoCoordClass);
805 }
806
807 bool geodFromHash(naRef ref, SGGeod& result)
808 {
809   if (!naIsHash(ref)) {
810     return false;
811   }
812
813   
814 // check for manual latitude / longitude names
815   naRef lat = naHash_cget(ref, (char*) "lat");
816   naRef lon = naHash_cget(ref, (char*) "lon");
817   if (naIsNum(lat) && naIsNum(lon)) {
818     result = SGGeod::fromDeg(naNumValue(lon).num, naNumValue(lat).num);
819     return true;
820   }
821   
822   if (hashIsCoord(ref)) {
823     naRef lat = naHash_cget(ref, (char*) "_lat");
824     naRef lon = naHash_cget(ref, (char*) "_lon");
825     if (naIsNum(lat) && naIsNum(lon)) {
826       result = SGGeod::fromRad(naNumValue(lon).num, naNumValue(lat).num);
827       return true;
828     }
829   }
830     
831 // check for any synonyms?
832     // latitude + longitude?
833   
834   return false;
835 }
836
837 static int geodFromArgs(naRef* args, int offset, int argc, SGGeod& result)
838 {
839   if (offset >= argc) {
840     return 0;
841   }
842   
843   if (naIsGhost(args[offset])) {
844     naGhostType* gt = naGhost_type(args[offset]);
845     if (gt == &AirportGhostType) {
846       result = airportGhost(args[offset])->geod();
847       return 1;
848     }
849     
850     if (gt == &NavaidGhostType) {
851       result = navaidGhost(args[offset])->geod();
852       return 1;
853     }
854     
855     if (gt == &RunwayGhostType) {
856       result = runwayGhost(args[offset])->geod();
857       return 1;
858     }
859     
860     if (gt == &TaxiwayGhostType) {
861       result = taxiwayGhost(args[offset])->geod();
862       return 1;
863     }
864     
865     if (gt == &FixGhostType) {
866       result = fixGhost(args[offset])->geod();
867       return 1;
868     }
869     
870     if (gt == &WayptGhostType) {
871       result = wayptGhost(args[offset])->position();
872       return 1;
873     }
874   }
875   
876   if (geodFromHash(args[offset], result)) {
877     return 1;
878   }
879   
880   if (((argc - offset) >= 2) && naIsNum(args[offset]) && naIsNum(args[offset + 1])) {
881     double lat = naNumValue(args[0]).num,
882     lon = naNumValue(args[1]).num;
883     result = SGGeod::fromDeg(lon, lat);
884     return 2;
885   }
886   
887   return 0;
888 }
889
890 // Convert a cartesian point to a geodetic lat/lon/altitude.
891 static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
892 {
893   double lat, lon, alt, xyz[3];
894   if(argc != 3) naRuntimeError(c, "carttogeod() expects 3 arguments");
895   for(int i=0; i<3; i++)
896     xyz[i] = naNumValue(args[i]).num;
897   sgCartToGeod(xyz, &lat, &lon, &alt);
898   lat *= SG_RADIANS_TO_DEGREES;
899   lon *= SG_RADIANS_TO_DEGREES;
900   naRef vec = naNewVector(c);
901   naVec_append(vec, naNum(lat));
902   naVec_append(vec, naNum(lon));
903   naVec_append(vec, naNum(alt));
904   return vec;
905 }
906
907 // Convert a geodetic lat/lon/altitude to a cartesian point.
908 static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args)
909 {
910   if(argc != 3) naRuntimeError(c, "geodtocart() expects 3 arguments");
911   double lat = naNumValue(args[0]).num * SG_DEGREES_TO_RADIANS;
912   double lon = naNumValue(args[1]).num * SG_DEGREES_TO_RADIANS;
913   double alt = naNumValue(args[2]).num;
914   double xyz[3];
915   sgGeodToCart(lat, lon, alt, xyz);
916   naRef vec = naNewVector(c);
917   naVec_append(vec, naNum(xyz[0]));
918   naVec_append(vec, naNum(xyz[1]));
919   naVec_append(vec, naNum(xyz[2]));
920   return vec;
921 }
922
923 // For given geodetic point return array with elevation, and a material data
924 // hash, or nil if there's no information available (tile not loaded). If
925 // information about the material isn't available, then nil is returned instead
926 // of the hash.
927 static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
928 {
929 #define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
930   if(argc < 2 || argc > 3)
931     naRuntimeError(c, "geodinfo() expects 2 or 3 arguments: lat, lon [, maxalt]");
932   double lat = naNumValue(args[0]).num;
933   double lon = naNumValue(args[1]).num;
934   double elev = argc == 3 ? naNumValue(args[2]).num : 10000;
935   const simgear::BVHMaterial *material;
936   SGGeod geod = SGGeod::fromDegM(lon, lat, elev);
937   if(!globals->get_scenery()->get_elevation_m(geod, elev, &material))
938     return naNil();
939   const SGMaterial *mat = dynamic_cast<const SGMaterial *>(material);
940   naRef vec = naNewVector(c);
941   naVec_append(vec, naNum(elev));
942   naRef matdata = naNil();
943   if(mat) {
944     matdata = naNewHash(c);
945     naRef names = naNewVector(c);
946     BOOST_FOREACH(const std::string& n, mat->get_names())
947       naVec_append(names, stringToNasal(c, n));
948       
949     HASHSET("names", 5, names);
950     HASHSET("solid", 5, naNum(mat->get_solid()));
951     HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
952     HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
953     HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
954     HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
955     HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
956   }
957   naVec_append(vec, matdata);
958   return vec;
959 #undef HASHSET
960 }
961
962
963 class AirportInfoFilter : public FGAirport::AirportFilter
964 {
965 public:
966   AirportInfoFilter() : type(FGPositioned::AIRPORT) {
967     minRunwayLengthFt = fgGetDouble("/sim/navdb/min-runway-length-ft", 0.0);
968   }
969   
970   bool fromArg(naRef arg)
971   {
972     const char *s = naStr_data(arg);
973     if(!strcmp(s, "airport")) type = FGPositioned::AIRPORT;
974     else if(!strcmp(s, "seaport")) type = FGPositioned::SEAPORT;
975     else if(!strcmp(s, "heliport")) type = FGPositioned::HELIPORT;
976     else
977       return false;
978     
979     return true;
980   }
981   
982   virtual FGPositioned::Type minType() const {
983     return type;
984   }
985   
986   virtual FGPositioned::Type maxType() const {
987     return type;
988   }
989     
990   virtual bool pass(FGPositioned* aPos) const
991   {
992     FGAirport* apt = (FGAirport*) aPos;
993     if ((apt->type() == FGPositioned::AIRPORT) && 
994         !apt->hasHardRunwayOfLengthFt(minRunwayLengthFt)) 
995     {
996       return false;
997     }
998
999     return true;
1000   }
1001   
1002   FGPositioned::Type type;
1003   double minRunwayLengthFt;
1004 };
1005
1006 // Returns data hash for particular or nearest airport of a <type>, or nil
1007 // on error.
1008 //
1009 // airportinfo(<id>);                   e.g. "KSFO"
1010 // airportinfo(<type>);                 type := ("airport"|"seaport"|"heliport")
1011 // airportinfo()                        same as  airportinfo("airport")
1012 // airportinfo(<lat>, <lon> [, <type>]);
1013 static naRef f_airportinfo(naContext c, naRef me, int argc, naRef* args)
1014 {
1015   SGGeod pos = globals->get_aircraft_position();
1016   FGAirport* apt = NULL;
1017   
1018   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
1019     pos = SGGeod::fromDeg(args[1].num, args[0].num);
1020     args += 2;
1021     argc -= 2;
1022   }
1023   
1024   double maxRange = 10000.0; // expose this? or pick a smaller value?
1025   
1026   AirportInfoFilter filter; // defaults to airports only
1027   
1028   if(argc == 0) {
1029     // fall through and use AIRPORT
1030   } else if(argc == 1 && naIsString(args[0])) {
1031     if (filter.fromArg(args[0])) {
1032       // done!
1033     } else {
1034       // user provided an <id>, hopefully
1035       apt = FGAirport::findByIdent(naStr_data(args[0]));
1036       if (!apt) {
1037         // return nil here, but don't raise a runtime error; this is a
1038         // legitamate way to validate an ICAO code, for example in a
1039         // dialog box or similar.
1040         return naNil();
1041       }
1042     }
1043   } else {
1044     naRuntimeError(c, "airportinfo() with invalid function arguments");
1045     return naNil();
1046   }
1047   
1048   if(!apt) {
1049     apt = FGAirport::findClosest(pos, maxRange, &filter);
1050     if(!apt) return naNil();
1051   }
1052   
1053   return ghostForAirport(c, apt);
1054 }
1055
1056 static naRef f_findAirportsWithinRange(naContext c, naRef me, int argc, naRef* args)
1057 {
1058   int argOffset = 0;
1059   SGGeod pos = globals->get_aircraft_position();
1060   argOffset += geodFromArgs(args, 0, argc, pos);
1061   
1062   if (!naIsNum(args[argOffset])) {
1063     naRuntimeError(c, "findAirportsWithinRange expected range (in nm) as arg %d", argOffset);
1064   }
1065   
1066   AirportInfoFilter filter; // defaults to airports only
1067   double rangeNm = args[argOffset++].num;
1068   if (argOffset < argc) {
1069     filter.fromArg(args[argOffset++]);
1070   }
1071   
1072   naRef r = naNewVector(c);
1073   
1074   FGPositioned::List apts = FGPositioned::findWithinRange(pos, rangeNm, &filter);
1075   FGPositioned::sortByRange(apts, pos);
1076   
1077   BOOST_FOREACH(FGPositionedRef a, apts) {
1078     FGAirport* apt = (FGAirport*) a.get();
1079     naVec_append(r, ghostForAirport(c, apt));
1080   }
1081   
1082   return r;
1083 }
1084
1085 static naRef f_findAirportsByICAO(naContext c, naRef me, int argc, naRef* args)
1086 {
1087   if (!naIsString(args[0])) {
1088     naRuntimeError(c, "findAirportsByICAO expects string as arg 0");
1089   }
1090   
1091   int argOffset = 0;
1092   string prefix(naStr_data(args[argOffset++]));
1093   AirportInfoFilter filter; // defaults to airports only
1094   if (argOffset < argc) {
1095     filter.fromArg(args[argOffset++]);
1096   }
1097   
1098   naRef r = naNewVector(c);
1099   
1100   FGPositioned::List apts = FGPositioned::findAllWithIdent(prefix, &filter, false);
1101   
1102   BOOST_FOREACH(FGPositionedRef a, apts) {
1103     FGAirport* apt = (FGAirport*) a.get();
1104     naVec_append(r, ghostForAirport(c, apt));
1105   }
1106   
1107   return r;
1108 }
1109
1110 static naRef f_airport_tower(naContext c, naRef me, int argc, naRef* args)
1111 {
1112     FGAirport* apt = airportGhost(me);
1113     if (!apt) {
1114       naRuntimeError(c, "airport.tower called on non-airport object");
1115     }
1116   
1117     // build a hash for the tower position    
1118     SGGeod towerLoc = apt->getTowerLocation();
1119     naRef tower = naNewHash(c);
1120     hashset(c, tower, "lat", naNum(towerLoc.getLatitudeDeg()));
1121     hashset(c, tower, "lon", naNum(towerLoc.getLongitudeDeg()));
1122     hashset(c, tower, "elevation", naNum(towerLoc.getElevationM()));
1123     return tower;
1124 }
1125
1126 static naRef f_airport_comms(naContext c, naRef me, int argc, naRef* args)
1127 {
1128     FGAirport* apt = airportGhost(me);
1129     if (!apt) {
1130       naRuntimeError(c, "airport.comms called on non-airport object");
1131     }
1132     naRef comms = naNewVector(c);
1133     
1134 // if we have an explicit type, return a simple vector of frequencies
1135     if (argc > 0 && naIsScalar(args[0])) {
1136         std::string commName = naStr_data(args[0]);
1137         FGPositioned::Type commType = FGPositioned::typeFromName(commName);
1138         
1139         BOOST_FOREACH(flightgear::CommStation* comm, apt->commStationsOfType(commType)) {
1140             naVec_append(comms, naNum(comm->freqMHz()));
1141         }
1142     } else {
1143 // otherwise return a vector of hashes, one for each comm station.
1144         BOOST_FOREACH(flightgear::CommStation* comm, apt->commStations()) {
1145             naRef commHash = naNewHash(c);
1146             hashset(c, commHash, "frequency", naNum(comm->freqMHz()));
1147             hashset(c, commHash, "ident", stringToNasal(c, comm->ident()));
1148             naVec_append(comms, commHash);
1149         }
1150     }
1151     
1152     return comms;
1153 }
1154
1155 static naRef f_airport_runway(naContext c, naRef me, int argc, naRef* args)
1156 {
1157   FGAirport* apt = airportGhost(me);
1158   if (!apt) {
1159     naRuntimeError(c, "airport.runway called on non-airport object");
1160   }
1161   
1162   if ((argc < 1) || !naIsString(args[0])) {
1163     naRuntimeError(c, "airport.runway expects a runway ident argument");
1164   }
1165   
1166   std::string ident(naStr_data(args[0]));
1167   boost::to_upper(ident);
1168   if (!apt->hasRunwayWithIdent(ident)) {
1169     return naNil();
1170   }
1171   
1172   return ghostForRunway(c, apt->getRunwayByIdent(ident));
1173 }
1174
1175 static naRef f_airport_taxiway(naContext c, naRef me, int argc, naRef* args)
1176 {
1177   FGAirport* apt = airportGhost(me);
1178   if (!apt) {
1179     naRuntimeError(c, "airport.taxiway called on non-airport object");
1180   }
1181   
1182   if ((argc < 1) || !naIsString(args[0])) {
1183     naRuntimeError(c, "airport.taxiway expects a taxiway ident argument");
1184   }
1185   
1186   naRef taxiways = naNewVector(c);
1187   
1188   for (unsigned int i = 0; i < apt->numTaxiways(); i++) {
1189     naVec_append(taxiways, ghostForTaxiway(c, apt->getTaxiwayByIndex(i)));
1190   }
1191   
1192   return taxiways;  
1193 }
1194
1195 static naRef f_airport_sids(naContext c, naRef me, int argc, naRef* args)
1196 {
1197   FGAirport* apt = airportGhost(me);
1198   if (!apt) {
1199     naRuntimeError(c, "airport.sids called on non-airport object");
1200   }
1201   
1202   naRef sids = naNewVector(c);
1203   
1204   FGRunway* rwy = NULL;
1205   if (argc > 0 && naIsString(args[0])) {
1206     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
1207       return naNil();
1208     }
1209
1210     rwy = apt->getRunwayByIdent(naStr_data(args[0]));
1211   } else if (argc > 0) {
1212     rwy = runwayGhost(args[0]);
1213   }
1214
1215   if (rwy) {
1216     BOOST_FOREACH(flightgear::SID* sid, rwy->getSIDs()) {
1217       naRef procId = stringToNasal(c, sid->ident());
1218       naVec_append(sids, procId);
1219     }
1220   } else {
1221     for (unsigned int s=0; s<apt->numSIDs(); ++s) {
1222       flightgear::SID* sid = apt->getSIDByIndex(s);
1223       naRef procId = stringToNasal(c, sid->ident());
1224       naVec_append(sids, procId);
1225     }
1226   }
1227   
1228   return sids;
1229 }
1230
1231 static naRef f_airport_stars(naContext c, naRef me, int argc, naRef* args)
1232 {
1233   FGAirport* apt = airportGhost(me);
1234   if (!apt) {
1235     naRuntimeError(c, "airport.stars called on non-airport object");
1236   }
1237   
1238   naRef stars = naNewVector(c);
1239   
1240   FGRunway* rwy = NULL;
1241   if (argc > 0 && naIsString(args[0])) {
1242     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
1243       return naNil();
1244     }
1245         
1246     rwy = apt->getRunwayByIdent(naStr_data(args[0]));
1247   } else if (argc > 0) {
1248     rwy = runwayGhost(args[0]);
1249   }
1250   
1251   if (rwy) {
1252     BOOST_FOREACH(flightgear::STAR* s, rwy->getSTARs()) {
1253       naRef procId = stringToNasal(c, s->ident());
1254       naVec_append(stars, procId);
1255     }
1256   } else {
1257     for (unsigned int s=0; s<apt->numSTARs(); ++s) {
1258       flightgear::STAR* star = apt->getSTARByIndex(s);
1259       naRef procId = stringToNasal(c, star->ident());
1260       naVec_append(stars, procId);
1261     }
1262   }
1263   
1264   return stars;
1265 }
1266
1267 static naRef f_airport_approaches(naContext c, naRef me, int argc, naRef* args)
1268 {
1269   FGAirport* apt = airportGhost(me);
1270   if (!apt) {
1271     naRuntimeError(c, "airport.getApproachList called on non-airport object");
1272   }
1273   
1274   naRef approaches = naNewVector(c);
1275   
1276   ProcedureType ty = PROCEDURE_INVALID;
1277   if ((argc > 1) && naIsString(args[1])) {
1278     std::string u(naStr_data(args[1]));
1279     boost::to_upper(u);
1280     if (u == "NDB") ty = PROCEDURE_APPROACH_NDB;
1281     if (u == "VOR") ty = PROCEDURE_APPROACH_VOR;
1282     if (u == "ILS") ty = PROCEDURE_APPROACH_ILS;
1283     if (u == "RNAV") ty = PROCEDURE_APPROACH_RNAV;
1284   }
1285   
1286   FGRunway* rwy = NULL;
1287   if (argc > 0 && (rwy = runwayGhost(args[0]))) {
1288     // ok
1289   } else if (argc > 0 && naIsString(args[0])) {
1290     if (!apt->hasRunwayWithIdent(naStr_data(args[0]))) {
1291       return naNil();
1292     }
1293     
1294     rwy = apt->getRunwayByIdent(naStr_data(args[0]));
1295   }
1296   
1297   if (rwy) {
1298     BOOST_FOREACH(Approach* s, rwy->getApproaches()) {
1299       if ((ty != PROCEDURE_INVALID) && (s->type() != ty)) {
1300         continue;
1301       }
1302       
1303       naRef procId = stringToNasal(c, s->ident());
1304       naVec_append(approaches, procId);
1305     }
1306   } else {
1307     // no runway specified, report them all
1308     for (unsigned int s=0; s<apt->numApproaches(); ++s) {
1309       Approach* app = apt->getApproachByIndex(s);
1310       if ((ty != PROCEDURE_INVALID) && (app->type() != ty)) {
1311         continue;
1312       }
1313       
1314       naRef procId = stringToNasal(c, app->ident());
1315       naVec_append(approaches, procId);
1316     }
1317   }
1318   
1319   return approaches;
1320 }
1321
1322 static naRef f_airport_parking(naContext c, naRef me, int argc, naRef* args)
1323 {
1324   FGAirport* apt = airportGhost(me);
1325   if (!apt) {
1326     naRuntimeError(c, "airport.parking called on non-airport object");
1327   }
1328   
1329   naRef r = naNewVector(c);
1330   std::string type;
1331   bool onlyAvailable = false;
1332   
1333   if (argc > 0 && naIsString(args[0])) {
1334     type = naStr_data(args[0]);
1335   }
1336   
1337   if ((argc > 1) && naIsNum(args[1])) {
1338     onlyAvailable = (args[1].num != 0.0);
1339   }
1340   
1341   FGAirportDynamics* dynamics = apt->getDynamics();
1342   for (int i=0; i<dynamics->getNrOfParkings(); ++i) {
1343     FGParking* park = dynamics->getParking(i);
1344   // filter out based on availability and type
1345     if (onlyAvailable && !park->isAvailable()) {
1346       continue;
1347     }
1348     
1349     if (!type.empty() && (park->getType() != type)) {
1350       continue;
1351     }
1352     
1353     const SGGeod& parkLoc = park->geod();
1354     naRef ph = naNewHash(c);
1355     hashset(c, ph, "name", stringToNasal(c, park->getName()));
1356     hashset(c, ph, "lat", naNum(parkLoc.getLatitudeDeg()));
1357     hashset(c, ph, "lon", naNum(parkLoc.getLongitudeDeg()));
1358     hashset(c, ph, "elevation", naNum(parkLoc.getElevationM()));
1359     naVec_append(r, ph);
1360   }
1361   
1362   return r;
1363 }
1364
1365 static naRef f_airport_getSid(naContext c, naRef me, int argc, naRef* args)
1366 {
1367   FGAirport* apt = airportGhost(me);
1368   if (!apt) {
1369     naRuntimeError(c, "airport.getSid called on non-airport object");
1370   }
1371   
1372   if ((argc != 1) || !naIsString(args[0])) {
1373     naRuntimeError(c, "airport.getSid passed invalid argument");
1374   }
1375   
1376   string ident = naStr_data(args[0]);
1377   return ghostForProcedure(c, apt->findSIDWithIdent(ident));
1378 }
1379
1380 static naRef f_airport_getStar(naContext c, naRef me, int argc, naRef* args)
1381 {
1382   FGAirport* apt = airportGhost(me);
1383   if (!apt) {
1384     naRuntimeError(c, "airport.getStar called on non-airport object");
1385   }
1386   
1387   if ((argc != 1) || !naIsString(args[0])) {
1388     naRuntimeError(c, "airport.getStar passed invalid argument");
1389   }
1390   
1391   string ident = naStr_data(args[0]);
1392   return ghostForProcedure(c, apt->findSTARWithIdent(ident));
1393 }
1394
1395 static naRef f_airport_getApproach(naContext c, naRef me, int argc, naRef* args)
1396 {
1397   FGAirport* apt = airportGhost(me);
1398   if (!apt) {
1399     naRuntimeError(c, "airport.getIAP called on non-airport object");
1400   }
1401   
1402   if ((argc != 1) || !naIsString(args[0])) {
1403     naRuntimeError(c, "airport.getIAP passed invalid argument");
1404   }
1405   
1406   string ident = naStr_data(args[0]);
1407   return ghostForProcedure(c, apt->findApproachWithIdent(ident));
1408 }
1409
1410 static naRef f_airport_toString(naContext c, naRef me, int argc, naRef* args)
1411 {
1412   FGAirport* apt = airportGhost(me);
1413   if (!apt) {
1414     naRuntimeError(c, "airport.tostring called on non-airport object");
1415   }
1416   
1417   return stringToNasal(c, "an airport " + apt->ident());
1418 }
1419
1420 // Returns vector of data hash for navaid of a <type>, nil on error
1421 // navaids sorted by ascending distance 
1422 // navinfo([<lat>,<lon>],[<type>],[<id>])
1423 // lat/lon (numeric): use latitude/longitude instead of ac position
1424 // type:              ("fix"|"vor"|"ndb"|"ils"|"dme"|"tacan"|"any")
1425 // id:                (partial) id of the fix
1426 // examples:
1427 // navinfo("vor")     returns all vors
1428 // navinfo("HAM")     return all navaids who's name start with "HAM"
1429 // navinfo("vor", "HAM") return all vor who's name start with "HAM"
1430 //navinfo(34,48,"vor","HAM") return all vor who's name start with "HAM" 
1431 //                           sorted by distance relative to lat=34, lon=48
1432 static naRef f_navinfo(naContext c, naRef me, int argc, naRef* args)
1433 {
1434   SGGeod pos;
1435   
1436   if(argc >= 2 && naIsNum(args[0]) && naIsNum(args[1])) {
1437     pos = SGGeod::fromDeg(args[1].num, args[0].num);
1438     args += 2;
1439     argc -= 2;
1440   } else {
1441     pos = globals->get_aircraft_position();
1442   }
1443   
1444   FGPositioned::Type type = FGPositioned::INVALID;
1445   nav_list_type navlist;
1446   const char * id = "";
1447   
1448   if(argc > 0 && naIsString(args[0])) {
1449     const char *s = naStr_data(args[0]);
1450     if(!strcmp(s, "any")) type = FGPositioned::INVALID;
1451     else if(!strcmp(s, "fix")) type = FGPositioned::FIX;
1452     else if(!strcmp(s, "vor")) type = FGPositioned::VOR;
1453     else if(!strcmp(s, "ndb")) type = FGPositioned::NDB;
1454     else if(!strcmp(s, "ils")) type = FGPositioned::ILS;
1455     else if(!strcmp(s, "dme")) type = FGPositioned::DME;
1456     else if(!strcmp(s, "tacan")) type = FGPositioned::TACAN;
1457     else id = s; // this is an id
1458     ++args;
1459     --argc;
1460   } 
1461   
1462   if(argc > 0 && naIsString(args[0])) {
1463     if( *id != 0 ) {
1464       naRuntimeError(c, "navinfo() called with navaid id");
1465       return naNil();
1466     }
1467     id = naStr_data(args[0]);
1468     ++args;
1469     --argc;
1470   }
1471   
1472   if( argc > 0 ) {
1473     naRuntimeError(c, "navinfo() called with too many arguments");
1474     return naNil();
1475   }
1476   
1477   FGNavList::TypeFilter filter(type);
1478   navlist = FGNavList::findByIdentAndFreq( pos, id, 0.0, &filter );
1479   
1480   naRef reply = naNewVector(c);
1481   for( nav_list_type::const_iterator it = navlist.begin(); it != navlist.end(); ++it ) {
1482     naVec_append( reply, ghostForNavaid(c, *it) );
1483   }
1484   return reply;
1485 }
1486
1487 static naRef f_findNavaidsWithinRange(naContext c, naRef me, int argc, naRef* args)
1488 {
1489   int argOffset = 0;
1490   SGGeod pos = globals->get_aircraft_position();
1491   argOffset += geodFromArgs(args, 0, argc, pos);
1492   
1493   if (!naIsNum(args[argOffset])) {
1494     naRuntimeError(c, "findNavaidsWithinRange expected range (in nm) as arg %d", argOffset);
1495   }
1496   
1497   FGPositioned::Type type = FGPositioned::INVALID;
1498   double rangeNm = args[argOffset++].num;
1499   if (argOffset < argc) {
1500     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1501   }
1502   
1503   naRef r = naNewVector(c);
1504   FGNavList::TypeFilter filter(type);
1505   FGPositioned::List navs = FGPositioned::findWithinRange(pos, rangeNm, &filter);
1506   FGPositioned::sortByRange(navs, pos);
1507   
1508   BOOST_FOREACH(FGPositionedRef a, navs) {
1509     FGNavRecord* nav = (FGNavRecord*) a.get();
1510     naVec_append(r, ghostForNavaid(c, nav));
1511   }
1512   
1513   return r;
1514 }
1515
1516 static naRef f_findNavaidByFrequency(naContext c, naRef me, int argc, naRef* args)
1517 {
1518   int argOffset = 0;
1519   SGGeod pos = globals->get_aircraft_position();
1520   argOffset += geodFromArgs(args, 0, argc, pos);
1521   
1522   if (!naIsNum(args[argOffset])) {
1523     naRuntimeError(c, "findNavaidByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
1524   }
1525   
1526   FGPositioned::Type type = FGPositioned::INVALID;
1527   double freqMhz = args[argOffset++].num;
1528   if (argOffset < argc) {
1529     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1530   }
1531   
1532   FGNavList::TypeFilter filter(type);
1533   nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
1534   if (navs.empty()) {
1535     return naNil();
1536   }
1537   
1538   return ghostForNavaid(c, navs.front().ptr());
1539 }
1540
1541 static naRef f_findNavaidsByFrequency(naContext c, naRef me, int argc, naRef* args)
1542 {
1543   int argOffset = 0;
1544   SGGeod pos = globals->get_aircraft_position();
1545   argOffset += geodFromArgs(args, 0, argc, pos);
1546   
1547   if (!naIsNum(args[argOffset])) {
1548     naRuntimeError(c, "findNavaidsByFrequency expectes frequency (in Mhz) as arg %d", argOffset);
1549   }
1550   
1551   FGPositioned::Type type = FGPositioned::INVALID;
1552   double freqMhz = args[argOffset++].num;
1553   if (argOffset < argc) {
1554     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1555   }
1556   
1557   naRef r = naNewVector(c);
1558   
1559   FGNavList::TypeFilter filter(type);
1560   nav_list_type navs = FGNavList::findAllByFreq(freqMhz, pos, &filter);
1561   
1562   BOOST_FOREACH(nav_rec_ptr a, navs) {
1563     naVec_append(r, ghostForNavaid(c, a.ptr()));
1564   }
1565   
1566   return r;
1567 }
1568
1569 static naRef f_findNavaidsByIdent(naContext c, naRef me, int argc, naRef* args)
1570 {
1571   int argOffset = 0;
1572   SGGeod pos = globals->get_aircraft_position();
1573   argOffset += geodFromArgs(args, 0, argc, pos);
1574   
1575   if (!naIsString(args[argOffset])) {
1576     naRuntimeError(c, "findNavaidsByIdent expectes ident string as arg %d", argOffset);
1577   }
1578   
1579   FGPositioned::Type type = FGPositioned::INVALID;
1580   string ident = naStr_data(args[argOffset++]);
1581   if (argOffset < argc) {
1582     type = FGPositioned::typeFromName(naStr_data(args[argOffset]));
1583   }
1584   
1585   FGNavList::TypeFilter filter(type);
1586   naRef r = naNewVector(c);
1587   nav_list_type navs = FGNavList::findByIdentAndFreq(pos, ident, 0.0, &filter);
1588   
1589   BOOST_FOREACH(nav_rec_ptr a, navs) {
1590     naVec_append(r, ghostForNavaid(c, a.ptr()));
1591   }
1592   
1593   return r;
1594 }
1595
1596 static naRef f_findFixesByIdent(naContext c, naRef me, int argc, naRef* args)
1597 {
1598   int argOffset = 0;
1599   SGGeod pos = globals->get_aircraft_position();
1600   argOffset += geodFromArgs(args, 0, argc, pos);
1601   
1602   if (!naIsString(args[argOffset])) {
1603     naRuntimeError(c, "findFixesByIdent expectes ident string as arg %d", argOffset);
1604   }
1605   
1606   string ident(naStr_data(args[argOffset]));
1607   naRef r = naNewVector(c);
1608   
1609   FGPositioned::TypeFilter filter(FGPositioned::FIX);
1610   FGPositioned::List fixes = FGPositioned::findAllWithIdent(ident, &filter);
1611   FGPositioned::sortByRange(fixes, pos);
1612   
1613   BOOST_FOREACH(FGPositionedRef f, fixes) {
1614     naVec_append(r, ghostForFix(c, (FGFix*) f.ptr()));
1615   }
1616   
1617   return r;
1618 }
1619
1620 // Convert a cartesian point to a geodetic lat/lon/altitude.
1621 static naRef f_magvar(naContext c, naRef me, int argc, naRef* args)
1622 {
1623   SGGeod pos = globals->get_aircraft_position();
1624   if (argc == 0) {
1625     // fine, use aircraft position
1626   } else if (geodFromArgs(args, 0, argc, pos)) {
1627     // okay
1628   } else {
1629     naRuntimeError(c, "magvar() expects no arguments, a positioned hash or lat,lon pair");
1630   }
1631   
1632   double jd = globals->get_time_params()->getJD();
1633   double magvarDeg = sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
1634   return naNum(magvarDeg);
1635 }
1636
1637 static naRef f_courseAndDistance(naContext c, naRef me, int argc, naRef* args)
1638 {
1639     SGGeod from = globals->get_aircraft_position(), to, p;
1640     int argOffset = geodFromArgs(args, 0, argc, p);
1641     if (geodFromArgs(args, argOffset, argc, to)) {
1642       from = p; // we parsed both FROM and TO args, so first was from
1643     } else {
1644       to = p; // only parsed one arg, so FROM is current
1645     }
1646   
1647     if (argOffset == 0) {
1648         naRuntimeError(c, "invalid arguments to courseAndDistance");
1649     }
1650     
1651     double course, course2, d;
1652     SGGeodesy::inverse(from, to, course, course2, d);
1653     
1654     naRef result = naNewVector(c);
1655     naVec_append(result, naNum(course));
1656     naVec_append(result, naNum(d * SG_METER_TO_NM));
1657     return result;
1658 }
1659
1660 static naRef f_greatCircleMove(naContext c, naRef me, int argc, naRef* args)
1661 {
1662   SGGeod from = globals->get_aircraft_position(), to;
1663   int argOffset = 0;
1664   
1665   // complication - don't inerpret two doubles (as the only args)
1666   // as a lat,lon pair - only do so if we have at least three args.
1667   if (argc > 2) {
1668     argOffset = geodFromArgs(args, 0, argc, from);
1669   }
1670   
1671   if ((argOffset + 1) >= argc) {
1672     naRuntimeError(c, "isufficent arguments to greatCircleMove");
1673   }
1674   
1675   if (!naIsNum(args[argOffset]) || !naIsNum(args[argOffset+1])) {
1676     naRuntimeError(c, "invalid arguments %d and %d to greatCircleMove",
1677                    argOffset, argOffset + 1);
1678   }
1679   
1680   double course = args[argOffset].num, course2;
1681   double distanceNm = args[argOffset + 1].num;
1682   SGGeodesy::direct(from, course, distanceNm * SG_NM_TO_METER, to, course2);
1683   
1684   // return geo.Coord
1685   naRef coord = naNewHash(c);
1686   hashset(c, coord, "lat", naNum(to.getLatitudeDeg()));
1687   hashset(c, coord, "lon", naNum(to.getLongitudeDeg()));
1688   return coord;
1689 }
1690
1691 static naRef f_tilePath(naContext c, naRef me, int argc, naRef* args)
1692 {
1693     SGGeod pos = globals->get_aircraft_position();
1694     geodFromArgs(args, 0, argc, pos);
1695     SGBucket b(pos);
1696     return stringToNasal(c, b.gen_base_path());
1697 }
1698
1699 static naRef f_tileIndex(naContext c, naRef me, int argc, naRef* args)
1700 {
1701   SGGeod pos = globals->get_aircraft_position();
1702   geodFromArgs(args, 0, argc, pos);
1703   SGBucket b(pos);
1704   return naNum(b.gen_index());
1705 }
1706
1707 static naRef f_route(naContext c, naRef me, int argc, naRef* args)
1708 {
1709   if (argc == 0) {
1710     FGRouteMgr* rm = static_cast<FGRouteMgr*>(globals->get_subsystem("route-manager"));  
1711     return ghostForFlightPlan(c, rm->flightPlan());
1712   }
1713   
1714   if ((argc > 0) && naIsString(args[0])) {
1715     flightgear::FlightPlan* fp = new flightgear::FlightPlan;
1716     SGPath path(naStr_data(args[0]));
1717     if (!path.exists()) {
1718       naRuntimeError(c, "flightplan, no file at path %s", path.c_str());
1719     }
1720     
1721     if (!fp->load(path)) {
1722       SG_LOG(SG_NASAL, SG_WARN, "failed to load flight-plan from " << path);
1723       delete fp;
1724       return naNil();
1725     }
1726     
1727     return ghostForFlightPlan(c, fp);
1728   }
1729   
1730   naRuntimeError(c, "bad arguments to flightplan()");
1731   return naNil();
1732 }
1733
1734 class NasalFPDelegate : public FlightPlan::Delegate
1735 {
1736 public:
1737   NasalFPDelegate(FlightPlan* fp, FGNasalSys* sys, naRef ins) :
1738     _nasal(sys),
1739     _plan(fp),
1740     _instance(ins)
1741   {
1742     SG_LOG(SG_NASAL, SG_INFO, "created Nasal delegate for " << fp);
1743     _gcSaveKey = _nasal->gcSave(ins);
1744   }
1745   
1746   virtual ~NasalFPDelegate()
1747   {
1748     SG_LOG(SG_NASAL, SG_INFO, "destroying Nasal delegate for " << _plan);
1749     _nasal->gcRelease(_gcSaveKey);
1750   }
1751   
1752   virtual void departureChanged()
1753   {
1754     callDelegateMethod("departureChanged");
1755   }
1756   
1757   virtual void arrivalChanged()
1758   {
1759     callDelegateMethod("arrivalChanged");
1760   }
1761   
1762   virtual void waypointsChanged()
1763   {
1764     callDelegateMethod("waypointsChanged");
1765   }
1766   
1767   virtual void currentWaypointChanged()
1768   {
1769     callDelegateMethod("currentWaypointChanged");
1770   }
1771 private:
1772   
1773   void callDelegateMethod(const char* method)
1774   {
1775     naRef f;
1776     naMember_cget(_nasal->context(), _instance, method, &f);
1777     if (naIsNil(f)) {
1778       return; // no method on the delegate
1779     }
1780     
1781     naRef arg[1];
1782     arg[0] = ghostForFlightPlan(_nasal->context(), _plan);
1783     _nasal->callMethod(f, _instance, 1, arg, naNil());
1784   }
1785   
1786   FGNasalSys* _nasal;
1787   FlightPlan* _plan;
1788   naRef _instance;
1789   int _gcSaveKey;
1790 };
1791
1792 class NasalFPDelegateFactory : public FlightPlan::DelegateFactory
1793 {
1794 public:
1795   NasalFPDelegateFactory(naRef code)
1796   {
1797     _nasal = (FGNasalSys*) globals->get_subsystem("nasal");
1798     _func = code;
1799     _gcSaveKey = _nasal->gcSave(_func);
1800   }
1801   
1802   ~NasalFPDelegateFactory()
1803   {
1804     _nasal->gcRelease(_gcSaveKey);
1805   }
1806   
1807   virtual FlightPlan::Delegate* createFlightPlanDelegate(FlightPlan* fp)
1808   {
1809     naRef args[1];
1810     args[0] = ghostForFlightPlan(_nasal->context(), fp);
1811     naRef instance = _nasal->call(_func, 1, args, naNil());
1812     if (naIsNil(instance)) {
1813       return NULL;
1814     }
1815     
1816     return new NasalFPDelegate(fp, _nasal, instance);
1817   }
1818 private:
1819   FGNasalSys* _nasal;
1820   naRef _func;
1821   int _gcSaveKey;
1822 };
1823
1824 static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef* args)
1825 {
1826   if ((argc < 1) || !naIsFunc(args[0])) {
1827     naRuntimeError(c, "non-function argument to registerFlightPlanDelegate");
1828   }
1829   
1830   NasalFPDelegateFactory* factory = new NasalFPDelegateFactory(args[0]);
1831   FlightPlan::registerDelegateFactory(factory);
1832   
1833   return naNil();
1834 }
1835
1836 static WayptRef wayptFromArg(naRef arg)
1837 {
1838   WayptRef r = wayptGhost(arg);
1839   if (r.valid()) {
1840     return r;
1841   }
1842   
1843   FGPositioned* pos = positionedGhost(arg);
1844   if (!pos) {
1845     // let's check if the arg is hash, coudl extra a geod and hence build
1846     // a simple waypoint
1847     
1848     return WayptRef();
1849   }
1850   
1851 // special-case for runways
1852   if (pos->type() == FGPositioned::RUNWAY) {
1853     return new RunwayWaypt((FGRunway*) pos, NULL);
1854   }
1855   
1856   return new NavaidWaypoint(pos, NULL);
1857 }
1858
1859 static naRef convertWayptVecToNasal(naContext c, const WayptVec& wps)
1860 {
1861   naRef result = naNewVector(c);
1862   BOOST_FOREACH(WayptRef wpt, wps) {
1863     naVec_append(result, ghostForWaypt(c, wpt.get()));
1864   }
1865   return result;
1866 }
1867
1868 static naRef f_airwaySearch(naContext c, naRef me, int argc, naRef* args)
1869 {
1870   if (argc < 2) {
1871     naRuntimeError(c, "airwaysSearch needs at least two arguments");
1872   }
1873   
1874   WayptRef start = wayptFromArg(args[0]), 
1875     end = wayptFromArg(args[1]);
1876   
1877   if (!start || !end) {
1878     SG_LOG(SG_NASAL, SG_WARN, "airwaysSearch: start or end points are invalid");
1879     return naNil();
1880   }
1881   
1882   bool highLevel = true;
1883   if ((argc > 2) && naIsString(args[2])) {
1884     if (!strcmp(naStr_data(args[2]), "lowlevel")) {
1885       highLevel = false;
1886     }
1887   }
1888   
1889   WayptVec route;
1890   if (highLevel) {
1891     Airway::highLevel()->route(start, end, route);
1892   } else {
1893     Airway::lowLevel()->route(start, end, route);
1894   }
1895   
1896   return convertWayptVecToNasal(c, route);
1897 }
1898
1899 static naRef f_createWP(naContext c, naRef me, int argc, naRef* args)
1900 {
1901   SGGeod pos;
1902   int argOffset = geodFromArgs(args, 0, argc, pos);
1903   
1904   if (((argc - argOffset) < 1) || !naIsString(args[argOffset])) {
1905     naRuntimeError(c, "createWP: no identifier supplied");
1906   }
1907     
1908   string ident = naStr_data(args[argOffset++]);
1909   WayptRef wpt = new BasicWaypt(pos, ident, NULL);
1910   
1911 // set waypt flags - approach, departure, pseudo, etc
1912   if (argc > argOffset) {
1913     WayptFlag f = wayptFlagFromString(naStr_data(args[argOffset++]));
1914     wpt->setFlag(f);
1915   }
1916   
1917   return ghostForWaypt(c, wpt);
1918 }
1919
1920 static naRef f_createWPFrom(naContext c, naRef me, int argc, naRef* args)
1921 {
1922   if (argc < 1) {
1923     naRuntimeError(c, "createWPFrom: need at least one argument");
1924   }
1925   
1926   FGPositioned* positioned = positionedGhost(args[0]);
1927   if (!positioned) {
1928     naRuntimeError(c, "createWPFrom: couldn;t convert arg[0] to FGPositioned");
1929   }
1930   
1931   WayptRef wpt;
1932   if (positioned->type() == FGPositioned::RUNWAY) {
1933     wpt = new RunwayWaypt((FGRunway*) positioned, NULL);
1934   } else {
1935     wpt = new NavaidWaypoint(positioned, NULL);
1936   }
1937
1938   // set waypt flags - approach, departure, pseudo, etc
1939   if (argc > 1) {
1940     WayptFlag f = wayptFlagFromString(naStr_data(args[1]));
1941     wpt->setFlag(f);
1942   }
1943   
1944   return ghostForWaypt(c, wpt);
1945 }
1946
1947 static naRef f_flightplan_getWP(naContext c, naRef me, int argc, naRef* args)
1948 {
1949   FlightPlan* fp = flightplanGhost(me);
1950   if (!fp) {
1951     naRuntimeError(c, "flightplan.getWP called on non-flightplan object");
1952   }
1953
1954   int index;
1955   if (argc == 0) {
1956     index = fp->currentIndex();
1957   } else {
1958     index = (int) naNumValue(args[0]).num;
1959   }
1960   
1961   if ((index < 0) || (index >= fp->numLegs())) {
1962     return naNil();
1963   }
1964   
1965   return ghostForLeg(c, fp->legAtIndex(index));
1966 }
1967
1968 static naRef f_flightplan_currentWP(naContext c, naRef me, int argc, naRef* args)
1969 {
1970   FlightPlan* fp = flightplanGhost(me);
1971   if (!fp) {
1972     naRuntimeError(c, "flightplan.currentWP called on non-flightplan object");
1973   }
1974   return ghostForLeg(c, fp->currentLeg());
1975 }
1976
1977 static naRef f_flightplan_nextWP(naContext c, naRef me, int argc, naRef* args)
1978 {
1979   FlightPlan* fp = flightplanGhost(me);
1980   if (!fp) {
1981     naRuntimeError(c, "flightplan.nextWP called on non-flightplan object");
1982   }
1983   return ghostForLeg(c, fp->nextLeg());
1984 }
1985
1986 static naRef f_flightplan_numWaypoints(naContext c, naRef me, int argc, naRef* args)
1987 {
1988   FlightPlan* fp = flightplanGhost(me);
1989   if (!fp) {
1990     naRuntimeError(c, "flightplan.numWaypoints called on non-flightplan object");
1991   }
1992   return naNum(fp->numLegs());
1993 }
1994
1995 static naRef f_flightplan_appendWP(naContext c, naRef me, int argc, naRef* args)
1996 {
1997   FlightPlan* fp = flightplanGhost(me);
1998   if (!fp) {
1999     naRuntimeError(c, "flightplan.appendWP called on non-flightplan object");
2000   }
2001   
2002   WayptRef wp = wayptGhost(args[0]);
2003   int index = fp->numLegs();
2004   fp->insertWayptAtIndex(wp.get(), index);
2005   return naNum(index);
2006 }
2007
2008 static naRef f_flightplan_insertWP(naContext c, naRef me, int argc, naRef* args)
2009 {
2010   FlightPlan* fp = flightplanGhost(me);
2011   if (!fp) {
2012     naRuntimeError(c, "flightplan.insertWP called on non-flightplan object");
2013   }
2014   
2015   WayptRef wp = wayptGhost(args[0]);
2016   int index = -1; // append
2017   if ((argc > 1) && naIsNum(args[1])) {
2018     index = (int) args[1].num;
2019   }
2020   
2021   fp->insertWayptAtIndex(wp.get(), index);
2022   return naNil();
2023 }
2024
2025 static naRef f_flightplan_insertWPAfter(naContext c, naRef me, int argc, naRef* args)
2026 {
2027   FlightPlan* fp = flightplanGhost(me);
2028   if (!fp) {
2029     naRuntimeError(c, "flightplan.insertWPAfter called on non-flightplan object");
2030   }
2031   
2032   WayptRef wp = wayptGhost(args[0]);
2033   int index = -1; // append
2034   if ((argc > 1) && naIsNum(args[1])) {
2035     index = (int) args[1].num;
2036   }
2037   
2038   fp->insertWayptAtIndex(wp.get(), index + 1);
2039   return naNil();
2040 }
2041
2042 static naRef f_flightplan_insertWaypoints(naContext c, naRef me, int argc, naRef* args)
2043 {
2044   FlightPlan* fp = flightplanGhost(me);
2045   if (!fp) {
2046     naRuntimeError(c, "flightplan.insertWaypoints called on non-flightplan object");
2047   }
2048   
2049   WayptVec wps;
2050   if (!naIsVector(args[0])) {
2051     naRuntimeError(c, "flightplan.insertWaypoints expects vector as first arg");
2052   }
2053
2054   int count = naVec_size(args[0]);
2055   for (int i=0; i<count; ++i) {
2056     Waypt* wp = wayptGhost(naVec_get(args[0], i));
2057     if (wp) {
2058       wps.push_back(wp);
2059     }
2060   }
2061   
2062   int index = -1; // append
2063   if ((argc > 1) && naIsNum(args[1])) {
2064     index = (int) args[1].num;
2065   }
2066
2067   fp->insertWayptsAtIndex(wps, index);
2068   return naNil();
2069 }
2070
2071 static naRef f_flightplan_deleteWP(naContext c, naRef me, int argc, naRef* args)
2072 {
2073   FlightPlan* fp = flightplanGhost(me);
2074   if (!fp) {
2075     naRuntimeError(c, "flightplan.deleteWP called on non-flightplan object");
2076   }
2077   
2078   if ((argc < 1) || !naIsNum(args[0])) {
2079     naRuntimeError(c, "bad argument to flightplan.deleteWP");
2080   }
2081   
2082   int index = (int) args[0].num;
2083   fp->deleteIndex(index);
2084   return naNil();
2085 }
2086
2087 static naRef f_flightplan_clearPlan(naContext c, naRef me, int argc, naRef* args)
2088 {
2089   FlightPlan* fp = flightplanGhost(me);
2090   if (!fp) {
2091     naRuntimeError(c, "flightplan.clearPlan called on non-flightplan object");
2092   }
2093   
2094   fp->clear();
2095   return naNil();
2096 }
2097
2098 static naRef f_flightplan_clearWPType(naContext c, naRef me, int argc, naRef* args)
2099 {
2100   FlightPlan* fp = flightplanGhost(me);
2101   if (!fp) {
2102     naRuntimeError(c, "flightplan.clearWPType called on non-flightplan object");
2103   }
2104   
2105   if (argc < 1) {
2106     naRuntimeError(c, "insufficent args to flightplan.clearWPType");
2107   }
2108   
2109   WayptFlag flag = wayptFlagFromString(naStr_data(args[0]));
2110   fp->clearWayptsWithFlag(flag);
2111   return naNil();
2112 }
2113
2114 static naRef f_flightplan_clone(naContext c, naRef me, int argc, naRef* args)
2115 {
2116   FlightPlan* fp = flightplanGhost(me);
2117   if (!fp) {
2118     naRuntimeError(c, "flightplan.clone called on non-flightplan object");
2119   }
2120   
2121   return ghostForFlightPlan(c, fp->clone());
2122 }
2123
2124 static naRef f_leg_setSpeed(naContext c, naRef me, int argc, naRef* args)
2125 {
2126   FlightPlan::Leg* leg = fpLegGhost(me);
2127   if (!leg) {
2128     naRuntimeError(c, "leg.setSpeed called on non-flightplan-leg object");
2129   }
2130   
2131   if (argc < 2) {
2132     naRuntimeError(c, "bad arguments to leg.setSpeed");
2133   }
2134   
2135   RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1]));
2136   leg->setSpeed(rr, args[0].num);
2137   return naNil();
2138 }
2139
2140 static naRef f_leg_setAltitude(naContext c, naRef me, int argc, naRef* args)
2141 {
2142   FlightPlan::Leg* leg = fpLegGhost(me);
2143   if (!leg) {
2144     naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object");
2145   }
2146   
2147   if (argc < 2) {
2148     naRuntimeError(c, "bad arguments to leg.setAltitude");
2149   }
2150   
2151   RouteRestriction rr = routeRestrictionFromString(naStr_data(args[1]));
2152   leg->setAltitude(rr, args[0].num);
2153   return naNil();
2154 }
2155
2156 static naRef f_leg_path(naContext c, naRef me, int argc, naRef* args)
2157 {
2158   FlightPlan::Leg* leg = fpLegGhost(me);
2159   if (!leg) {
2160     naRuntimeError(c, "leg.setAltitude called on non-flightplan-leg object");
2161   }
2162   
2163   RoutePath path(leg->owner());
2164   SGGeodVec gv(path.pathForIndex(leg->index()));
2165
2166   naRef result = naNewVector(c);
2167   BOOST_FOREACH(SGGeod p, gv) {
2168     // construct a geo.Coord!
2169     naRef coord = naNewHash(c);
2170     hashset(c, coord, "lat", naNum(p.getLatitudeDeg()));
2171     hashset(c, coord, "lon", naNum(p.getLongitudeDeg()));
2172     naVec_append(result, coord);
2173   }
2174
2175   return result;
2176 }
2177
2178 static naRef f_waypoint_navaid(naContext c, naRef me, int argc, naRef* args)
2179 {
2180   flightgear::Waypt* w = wayptGhost(me);
2181   if (!w) {
2182     naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
2183   }
2184   
2185   FGPositioned* pos = w->source();
2186   if (!pos) {
2187     return naNil();
2188   }
2189   
2190   switch (pos->type()) {
2191   case FGPositioned::VOR:
2192   case FGPositioned::NDB:
2193   case FGPositioned::ILS:
2194   case FGPositioned::LOC:
2195   case FGPositioned::GS:
2196   case FGPositioned::DME:
2197   case FGPositioned::TACAN: {
2198     FGNavRecord* nav = (FGNavRecord*) pos;
2199     return ghostForNavaid(c, nav);
2200   }
2201       
2202   default:
2203     return naNil();
2204   }
2205 }
2206
2207 static naRef f_waypoint_airport(naContext c, naRef me, int argc, naRef* args)
2208 {
2209   flightgear::Waypt* w = wayptGhost(me);
2210   if (!w) {
2211     naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
2212   }
2213   
2214   FGPositioned* pos = w->source();
2215   if (!pos || FGAirport::isAirportType(pos)) {
2216     return naNil();
2217   }
2218   
2219   return ghostForAirport(c, (FGAirport*) pos);
2220 }
2221
2222 static naRef f_waypoint_runway(naContext c, naRef me, int argc, naRef* args)
2223 {
2224   flightgear::Waypt* w = wayptGhost(me);
2225   if (!w) {
2226     naRuntimeError(c, "waypoint.navaid called on non-waypoint object");
2227   }
2228   
2229   FGPositioned* pos = w->source();
2230   if (!pos || (pos->type() != FGPositioned::RUNWAY)) {
2231     return naNil();
2232   }
2233   
2234   return ghostForRunway(c, (FGRunway*) pos);
2235 }
2236
2237 static naRef f_procedure_transition(naContext c, naRef me, int argc, naRef* args)
2238 {
2239   Procedure* proc = procedureGhost(me);
2240   if (!proc) {
2241     naRuntimeError(c, "procedure.transition called on non-procedure object");
2242   }
2243   
2244   if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
2245     naRuntimeError(c, "procedure.transition called on non-SID or -STAR");
2246   }
2247   
2248   ArrivalDeparture* ad = (ArrivalDeparture*) proc;
2249   Transition* trans = ad->findTransitionByName(naStr_data(args[0]));
2250   
2251   return ghostForProcedure(c, trans);
2252 }
2253
2254 static naRef f_procedure_route(naContext c, naRef me, int argc, naRef* args)
2255 {
2256   Procedure* proc = procedureGhost(me);
2257   if (!proc) {
2258     naRuntimeError(c, "procedure.route called on non-procedure object");
2259   }
2260   
2261 // wrapping up tow different routines here - approach routing from the IAF
2262 // to the associated runway, and SID/STAR routing via an enroute transition
2263 // and possibly a runway transition or not.
2264   if (Approach::isApproach(proc->type())) {
2265     WayptRef iaf;
2266     if (argc > 0) {
2267       iaf = wayptFromArg(args[0]);
2268     }
2269     
2270     WayptVec r;
2271     Approach* app = (Approach*) proc;
2272     if (!app->route(iaf, r)) {
2273       SG_LOG(SG_NASAL, SG_WARN, "procedure.route failed for Approach somehow");
2274       return naNil();
2275     }
2276     
2277     return convertWayptVecToNasal(c, r);
2278   } else if ((proc->type() != PROCEDURE_SID) && (proc->type() != PROCEDURE_STAR)) {
2279     naRuntimeError(c, "procedure.route called on unsuitable procedure type");
2280   }
2281   
2282   int argOffset = 0;
2283   FGRunway* rwy = runwayGhost(args[0]);
2284   if (rwy) ++argOffset;
2285   
2286   ArrivalDeparture* ad = (ArrivalDeparture*) proc;
2287   Transition* trans = NULL;
2288   if (argOffset < argc) {
2289     trans = (Transition*) procedureGhost(args[argOffset]);
2290   }
2291   
2292   // note either runway or trans may be NULL - that's ok
2293   WayptVec r;
2294   if (!ad->route(rwy, trans, r)) {
2295     SG_LOG(SG_NASAL, SG_WARN, "prcoedure.route failed for ArrvialDeparture somehow");
2296     return naNil();
2297   }
2298   
2299   return convertWayptVecToNasal(c, r);
2300 }
2301
2302
2303 // Table of extension functions.  Terminate with zeros.
2304 static struct { const char* name; naCFunction func; } funcs[] = {
2305   { "carttogeod", f_carttogeod },
2306   { "geodtocart", f_geodtocart },
2307   { "geodinfo", f_geodinfo },
2308   { "airportinfo", f_airportinfo },
2309   { "findAirportsWithinRange", f_findAirportsWithinRange },
2310   { "findAirportsByICAO", f_findAirportsByICAO },
2311   { "navinfo", f_navinfo },
2312   { "findNavaidsWithinRange", f_findNavaidsWithinRange },
2313   { "findNavaidByFrequency", f_findNavaidByFrequency },
2314   { "findNavaidsByFrequency", f_findNavaidsByFrequency },
2315   { "findNavaidsByID", f_findNavaidsByIdent },
2316   { "findFixesByID", f_findFixesByIdent },
2317   { "flightplan", f_route },
2318   { "registerFlightPlanDelegate", f_registerFPDelegate },
2319   { "createWP", f_createWP },
2320   { "createWPFrom", f_createWPFrom },
2321   { "airwaysRoute", f_airwaySearch },
2322   { "magvar", f_magvar },
2323   { "courseAndDistance", f_courseAndDistance },
2324   { "greatCircleMove", f_greatCircleMove },
2325   { "tileIndex", f_tileIndex },
2326   { "tilePath", f_tilePath },
2327   { 0, 0 }
2328 };
2329
2330
2331 naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
2332 {
2333     airportPrototype = naNewHash(c);
2334     hashset(c, gcSave, "airportProto", airportPrototype);
2335   
2336     hashset(c, airportPrototype, "runway", naNewFunc(c, naNewCCode(c, f_airport_runway)));
2337     hashset(c, airportPrototype, "taxiway", naNewFunc(c, naNewCCode(c, f_airport_taxiway)));
2338     hashset(c, airportPrototype, "tower", naNewFunc(c, naNewCCode(c, f_airport_tower)));
2339     hashset(c, airportPrototype, "comms", naNewFunc(c, naNewCCode(c, f_airport_comms)));
2340     hashset(c, airportPrototype, "sids", naNewFunc(c, naNewCCode(c, f_airport_sids)));
2341     hashset(c, airportPrototype, "stars", naNewFunc(c, naNewCCode(c, f_airport_stars)));
2342     hashset(c, airportPrototype, "getApproachList", naNewFunc(c, naNewCCode(c, f_airport_approaches)));
2343     hashset(c, airportPrototype, "parking", naNewFunc(c, naNewCCode(c, f_airport_parking)));
2344     hashset(c, airportPrototype, "getSid", naNewFunc(c, naNewCCode(c, f_airport_getSid)));
2345     hashset(c, airportPrototype, "getStar", naNewFunc(c, naNewCCode(c, f_airport_getStar)));
2346     hashset(c, airportPrototype, "getIAP", naNewFunc(c, naNewCCode(c, f_airport_getApproach)));
2347     hashset(c, airportPrototype, "tostring", naNewFunc(c, naNewCCode(c, f_airport_toString)));
2348   
2349     flightplanPrototype = naNewHash(c);
2350     hashset(c, gcSave, "flightplanProto", flightplanPrototype);
2351       
2352     hashset(c, flightplanPrototype, "getWP", naNewFunc(c, naNewCCode(c, f_flightplan_getWP)));
2353     hashset(c, flightplanPrototype, "currentWP", naNewFunc(c, naNewCCode(c, f_flightplan_currentWP))); 
2354     hashset(c, flightplanPrototype, "nextWP", naNewFunc(c, naNewCCode(c, f_flightplan_nextWP))); 
2355     hashset(c, flightplanPrototype, "getPlanSize", naNewFunc(c, naNewCCode(c, f_flightplan_numWaypoints)));
2356     hashset(c, flightplanPrototype, "appendWP", naNewFunc(c, naNewCCode(c, f_flightplan_appendWP))); 
2357     hashset(c, flightplanPrototype, "insertWP", naNewFunc(c, naNewCCode(c, f_flightplan_insertWP))); 
2358     hashset(c, flightplanPrototype, "deleteWP", naNewFunc(c, naNewCCode(c, f_flightplan_deleteWP))); 
2359     hashset(c, flightplanPrototype, "insertWPAfter", naNewFunc(c, naNewCCode(c, f_flightplan_insertWPAfter))); 
2360     hashset(c, flightplanPrototype, "insertWaypoints", naNewFunc(c, naNewCCode(c, f_flightplan_insertWaypoints))); 
2361     hashset(c, flightplanPrototype, "cleanPlan", naNewFunc(c, naNewCCode(c, f_flightplan_clearPlan))); 
2362     hashset(c, flightplanPrototype, "clearWPType", naNewFunc(c, naNewCCode(c, f_flightplan_clearWPType))); 
2363     hashset(c, flightplanPrototype, "clone", naNewFunc(c, naNewCCode(c, f_flightplan_clone))); 
2364   
2365     waypointPrototype = naNewHash(c);
2366     hashset(c, gcSave, "wayptProto", waypointPrototype);
2367     
2368     hashset(c, waypointPrototype, "navaid", naNewFunc(c, naNewCCode(c, f_waypoint_navaid)));
2369     hashset(c, waypointPrototype, "runway", naNewFunc(c, naNewCCode(c, f_waypoint_runway)));
2370     hashset(c, waypointPrototype, "airport", naNewFunc(c, naNewCCode(c, f_waypoint_airport)));
2371   
2372     procedurePrototype = naNewHash(c);
2373     hashset(c, gcSave, "procedureProto", procedurePrototype);
2374     hashset(c, procedurePrototype, "transition", naNewFunc(c, naNewCCode(c, f_procedure_transition)));
2375     hashset(c, procedurePrototype, "route", naNewFunc(c, naNewCCode(c, f_procedure_route)));
2376   
2377     fpLegPrototype = naNewHash(c);
2378     hashset(c, gcSave, "fpLegProto", fpLegPrototype);
2379     hashset(c, fpLegPrototype, "setSpeed", naNewFunc(c, naNewCCode(c, f_leg_setSpeed)));
2380     hashset(c, fpLegPrototype, "setAltitude", naNewFunc(c, naNewCCode(c, f_leg_setAltitude)));
2381     hashset(c, fpLegPrototype, "path", naNewFunc(c, naNewCCode(c, f_leg_path)));
2382   
2383     for(int i=0; funcs[i].name; i++) {
2384       hashset(c, globals, funcs[i].name,
2385       naNewFunc(c, naNewCCode(c, funcs[i].func)));
2386     }
2387   
2388   return naNil();
2389 }
2390
2391 void postinitNasalPositioned(naRef globals, naContext c)
2392 {
2393   naRef geoModule = naHash_cget(globals, (char*) "geo");
2394   if (naIsNil(geoModule)) {
2395     SG_LOG(SG_GENERAL, SG_WARN, "postinitNasalPositioned: geo.nas not loaded");
2396     return;
2397   }
2398   
2399   geoCoordClass = naHash_cget(geoModule, (char*) "Coord");
2400 }
2401
2402