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