#!/usr/bin/perl
# NAME: findap03.pl
# AIM: Read FlightGear apt.dat, and find an airport given the name,
# 16/11/2011 - A little more information if -v9
# 16/09/2011 - Fix bug $$g_acnt to $g_acnt about line 650
# 02/05/2011 - Add the tile INDEX to the fg=<chunk>/<tile>/index
# 21/02/2011 - Add AMSL (Airport altitude) to airport output
# 20/02/2011 - If VERB1, add runway center, if VERB2, add runway ends
# 09/02/2011 - Add more information about runways.
# 25/01/2011 - If the raw input looks like a 4 letter ICAO, then search for that
# 29/12/2010 - Add altitude, and frequencies, to airport display ($aalt)
# 15/12/2010 - Fix in the display of the FG CHUNK - now show chunk/tile path, and rwy opp nums
# 26/11/2010 - No show of navs if search a/p, and none found
# 12/11/2010-11/11/2010 - Rel 03 - check out... reduce noise...
# 09/11/2010 - Some UI enhancements... Skip NAV version line, ... FIX20101109
# 17/08/2010 - Fix for windows command -latlon=5,10 becomes -latlon 5 10
# 13/02/2010 - Change to using C:\FGCVS\FLightGear\data files...
# 18/11/2009 - Added Bucket2.pm, to show bucket details - OOPS, would NOT work
# 18/12/2008 - Used tested include 'fg_wsg84.pl' for distance services
# 12/12/2008 - Switch to using DISTANCE, rather than DEGREES, for searching
# for close NAVAIDS ... Add a -range=nn Kilometers
# 19/11/2008 - Added $tryharder, when NO navaid found
# updated 20070526 fixes to run from command line
# Updated 20070405 to parse inputs, added help, 
# 20061127 - Use gz (gzip) files directly from $FG_ROOT
# geoff mclane - http://geoffmclane.com/mperl/index.htm - 20061127
use strict;
use warnings;
use Time::HiRes qw( gettimeofday tv_interval );
my $perl_dir = "C:\\GTools\\perl"; 
unshift(@INC, $perl_dir);
require 'logfile.pl' or die "Error: Unable to locate logfile.pl ...\n";
require 'fg_wsg84.pl' or die "Unable to load fg_wsg84.pl ...\n";
require "Bucket2.pm" or die "Unable to load Bucket2.pm ...\n";
my $CDATROOT="C:/FGCVS/FlightGear/data";
# my $CDATROOT="C:/FGCVS/FlightGear/data";
# =============================================================================
# This NEEDS to be adjusted to YOUR particular default location of these files.
my $FGROOT = (exists $ENV{'FG_ROOT'})? $ENV{'FG_ROOT'} : $CDATROOT;
#my $FGROOT = (exists $ENV{'FG_ROOT'})? $ENV{'FG_ROOT'} : "C:/FG/27/data";
# file spec : http://data.x-plane.com/file_specs/Apt810.htm
my $APTFILE 	  = "$FGROOT/Airports/apt.dat.gz";	# the airports data file
my $NAVFILE 	  = "$FGROOT/Navaids/nav.dat.gz";	# the NAV, NDB, etc. data file
# add these files
my $FIXFILE 	  = "$FGROOT/Navaids/fix.dat.gz";	# the FIX data file
my $AWYFILE       = "$FGROOT/Navaids/awy.dat.gz";   # Airways data
# =============================================================================
my $VERS="Feb 10, 2011. version 1.0.2";

# log file stuff
my ($LF);
my $pgmname = $0;
if ($pgmname =~ /\w{1}:\\.*/) {
    my @tmpsp = split(/\\/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $perl_dir."\\temp.$pgmname.txt";
open_log($outfile);
my $t0 = [gettimeofday];

# program variables - set during running
# different searches -icao=LFPO, -latlon=1,2, or -name="airport name"
# KSFO San Francisco Intl (37.6208607739872,-122.381074803838)
my $aptdat = $APTFILE;
my $navdat = $NAVFILE;
my $g_fixfile = $FIXFILE;
my $g_awyfile = $AWYFILE;

my $SRCHICAO = 0;	# search using icao id ... takes precedence
my $SRCHONLL = 0;	# search using lat,lon
my $SRCHNAME = 0;	# search using name
my $SHOWNAVS = 0;	# show navaids around airport found

my $g_max_name_len = 24; # was 32; # was 24
my $aptname = "strasbourg";
my $apticao = 'KSFO';
my $g_center_lat = 0; # 37.6;
my $g_center_lon = 0; # -122.4;
my $maxlatd = 0.5;
my $maxlond = 0.5;
my $nmaxlatd = 0.1;
my $nmaxlond = 0.1;
my $max_cnt = 0;	# maximum airport count - 0 = no limit
my $max_range_km = 5;   # range search using KILOMETERS
my $g_fix_name = "ASKIK";
# features
my $tryharder = 0;  # Expand the search for NAVAID, until at least 1 found
my $usekmrange = 0; # search using KILOMETER range - see $max_range_km
my $sortbyfreq = 0; # sort NAVAIDS by FREQUENCY
my $sort_by_distance = 1; # sort NAVAIDS by DISTANCE from CENTER
my $verbosity = 0; # just info neeeded...
my $vor_only = 0;
my $loadlog = 0;
my $check_sg_dist = 1; # do calc again, and show...
my $use_full_list = 0; # seems better to GROUP frequencies

# variables for range using distance calculation
my $PI = 3.1415926535897932384626433832795029;
my $D2R = $PI / 180;
my $R2D = 180 / $PI;
my $ERAD = 6378138.12;
my $DIST_FACTOR = $ERAD;
my $SG_EPSILON = 0.0000001;
my $SG_FEET_TO_METER = 0.3048; # /** Feet to Meters */

# debug tests
# ===================
my $test_name = 0;	# to TEST a NAME search
my $def_name = "hong kong";
my $test_ll = 0;	# to TEST a LAT,LON search
my $def_lat = 37.6;
my $def_lon = -122.4;
my $test_icao = 0;	# to TEST an ICAO search
my $def_icao = 'VHHH'; ## 'KHAF';  ## LFPO'; ## 'KSFO';
my $dbg1 = 0;	# show airport during finding ...
my $dbg_fa02 = 0;	# show navaid during finding ...
my $dbg3 = 0;	# show count after finding
my $verb3 = 0;
my $dbg_fa04 = 0; # show NAV center search...

# ===================

my $av_apt_lat = 0;	# later will be $tlat / $ac;
my $av_apt_lon = 0; # later $tlon / $ac;

# apt.dat.gz CODES - see http://x-plane.org/home/robinp/Apt810.htm for DETAILS
my $aln =     '1';	# airport line
my $rln =    '10';	# runways/taxiways line
my $sealn =  '16'; # Seaplane base header data.
my $heliln = '17'; # Heliport header data.  
my $twrln =  '14'; # Tower view location. 
my $rampln = '15'; # Ramp startup position(s) 
my $bcnln =  '18'; # Airport light beacons  
my $wsln =   '19'; # windsock

# Radio Frequencies # AWOS (Automatic Weather Observation System), ASOS (Automatic Surface Observation System)
my $minatc = '50'; # ATIS (Automated Terminal Information System). AWIS (Automatic Weather Information Service)
my $unicom = '51'; # Unicom or CTAF (USA), radio (UK) - open channel for pilot position reporting at uncontrolled airports.
my $cleara = '52'; # Clearance delivery.
my $goundf = '53'; # ground
my $twrfrq = '54';	# like 12210 TWR
my $appfrq = '55';  # like 11970 ROTTERDAM APP
my $maxatc = '56'; # Departure.
my %off2name = (
    0 => 'ATIS',
    1 => 'Unicom',
    2 => 'Clearance',
    3 => 'Ground',
    4 => 'Tower',
    5 => 'Approach',
    6 => 'Departure'
);

# offset 10 in runway array
my %runway_surface = (
    1  => 'Asphalt',
    2  => 'Concrete',
    3  => 'Turf/grass',
    4  => 'Dirt',
    5  => 'Gravel',
    6  => 'H-Asphalt', # helepad (big 'H' in the middle).
    7  => 'H-Concrete', # helepad (big 'H' in the middle).
    8  => 'H-Turf', # helepad (big 'H' in the middle).
    9  => 'H-Dirt', # helepad (big 'H' in the middle). 
    10 => 'T-Asphalt', # taxiway - with yellow hold line across long axis (not available from WorldMaker).
    11 => 'T-Concrete', # taxiway - with yellow hold line across long axis (not available from WorldMaker).
    12 => 'Dry Lakebed', # (eg. at KEDW Edwards AFB).
    13 => 'Water' # runways (marked with bobbing buoys) for seaplane/floatplane bases (available in X-Plane 7.0 and later). 
);

# =====================================================================================================


my $lastln = '99'; # end of file

# nav.dat.gz CODES
my $navNDB = '2';
my $navVOR = '3';
my $navILS = '4';
my $navLOC = '5';
my $navGS  = '6';
my $navOM  = '7';
my $navMM  = '8';
my $navIM  = '9';
my $navVDME = '12';
my $navNDME = '13';
my @navset = ($navNDB, $navVOR, $navILS, $navLOC, $navGS, $navOM, $navMM, $navIM, $navVDME, $navNDME);
my @navtypes = qw( NDB VOR ILS LOC GS OM NM IM VDME NDME );

# set lengths for common outputs
my $maxnnlen = 4;
my $g_maxnaltl = 5;
my $g_maxnfrql = 5;
my $g_maxnrngl = 5;
my $g_maxnfq2l = 10;
my $g_maxnnidl = 4;
my $g_maxnlatl = 12;
my $g_maxnlonl = 13;
my $g_nav_hdr = "Type  Latitude     Logitude        Alt.  Freq.  Range  Frequency2    ID  Name";

# global program variables
my $actnav = '';
my @g_aptlist = (); # ALL airports found, for research, if needed
#                 0      1      2      3      4      5   6  7  8  9      10     11    12     13   14        15
#push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@runways,$aalt]);
my @aptlist2 = ();
my @navlist = ();
my @navlist2 = ();
my @g_navlist3 = ();
my $totaptcnt = 0;
my $g_acnt = 0;
my $outcount = 0;
my @tilelist = ();
my $in_input_file = 0;

#program variables
my @warnings = ();
my %g_dupe_shown = ();
my $nav_file_version = 0;
my $got_center_latlon = 0; # 1 if latlon given in command, or ...
my $g_total_aps = 0; # push(@g_aptlist, [$diff, $icao, $name, $alat, $alon]);

sub VERB1() { return $verbosity >= 1; }
sub VERB2() { return $verbosity >= 2; }
sub VERB5() { return $verbosity >= 5; }
sub VERB9() { return $verbosity >= 9; }

sub prtw {
    my ($tx) = shift;
    $tx =~ s/\n$//;
    prt("$tx\n");
    push(@warnings,$tx);
}

sub show_warnings {
	my ($dbg) = shift;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS ...\n" );
        foreach my $line (@warnings) {
            prt("$line\n" );
        }
        prt("\n");
    } elsif ($dbg) {
        prt("\nNo warnings issued.\n\n");
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    show_warnings(0);
    if (length($msg)) {
        $msg =~ s/\n$//;
        prt("$msg\n");
    }
    $loadlog = 1 if ($outcount > 30);
    close_log($outfile,$loadlog);
    ### unlink($outfile);
    exit($val);
}

sub get_bucket_info {
   my ($lon,$lat) = @_;
   my $b = Bucket2->new();
   $b->set_bucket($lon,$lat);
   return $b->bucket_info();
}

sub show_scenery_tiles() {
    my ($name);
    my $cnt = scalar @tilelist;
    if ($cnt) {
        if (VERB9()) {
            prt( "Scenery Tile" );
            if ($cnt > 1) {
                prt( "s" );
            }
            prt( ": " );
            foreach $name (@tilelist) {
                prt( "$name " );
            }
            prt( "\n" );
        } elsif (VERB5()) {
            prt( "Scenery Tile Count $cnt\n" );
        }
    }
}

sub load_gzip_file($) {
    my ($fil) = shift;
	prt("[v2] Loading [$fil] file... moment...\n") if (VERB2());
	mydie("ERROR: Can NOT locate [$fil]!\n") if ( !( -f $fil) );
	open NIF, "gzip -d -c $fil|" or mydie( "ERROR: CAN NOT OPEN $fil...$!...\n" );
	my @arr = <NIF>;
	close NIF;
    prt("[v9] Got ".scalar @arr." lines to scan...\n") if (VERB9());
    return \@arr;
}

sub load_fix_file { return load_gzip_file($g_fixfile); }
sub load_awy_file { return load_gzip_file($g_awyfile); }

########################################################################
### ONLY SUBS BELOW HERE
sub elim_the_dupes($) {
    my ($name) = @_;
    my @arr = split(/\s+/,$name);
    my %dupes = ();
    my @narr = ();
    my ($itm);
    foreach $itm (@arr) {
        if (!defined $dupes{$itm}) {
            $dupes{$itm} = 1;
            push(@narr,$itm);
        }
    }
    return join(" ",@narr);
}

sub ctr_latlon_stg() {
    return "$g_center_lat,$g_center_lon";
}

sub get_fg_dist_dir($$$$) {
    my ($lat1,$lon1,$lat2,$lon2) = @_;
    my ($sg_az1,$sg_az2,$sg_dist);
    my $res = fg_geo_inverse_wgs_84 ($lat1,$lon1,$lat2,$lon2,\$sg_az1,\$sg_az2,\$sg_dist);
    my $sg_km = $sg_dist / 1000;
    my $sg_im = int($sg_dist);
    my $sg_ikm = int($sg_km + 0.5);
    # if (abs($sg_pdist) < $CP_EPSILON)
    my $dist_hdg = ""; # or say "(SG: ";
    $sg_az1 = int(($sg_az1 * 10) + 0.05) / 10;
    if (abs($sg_km) > $SG_EPSILON) { # = 0.0000001; # EQUALS SG_EPSILON 20101121
        if ($sg_ikm && ($sg_km >= 1)) {
            $sg_km = int(($sg_km * 10) + 0.05) / 10;
            $dist_hdg .= "$sg_km km";
        } else {
            $dist_hdg .= "$sg_im m, <1km";
        }
    } else {
        $dist_hdg .= "0 m";
    }
    $dist_hdg .= " on $sg_az1 d.";
    #$dist_hdg .= ")";
    return $dist_hdg;
}

##                 0      1      2      3      4      5      6
#push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, $aalt, \@f]);
sub get_g_aptlist_off($) {
    my ($icao) = @_;
    my $max = scalar @g_aptlist;
    my ($i,$t);
    for ($i = 0; $i < $max; $i++) {
        $t = $g_aptlist[$i][1];
        if ($icao eq $t) {
            return ($i + 1);
        }
    }
    return 0;
}

sub get_atis_info($$$$$$$) {
    my ($ii,$scnt,$raptlist2,$gaoff,$alat,$alon,$aalt) = @_;
    my $info = '';
    my $rfa = $g_aptlist[$gaoff-1][6]; # get the ATIS, Tower, ..., frequecies array (ref)
    my $rfc = scalar @{$rfa};   # get count for this airport
    my $rj = 0;
    my ($rfna);
    my %names = ();
    $info = '';
    $info .= "\n" if (VERB1());
    if ($rfc) {
        $info .= " rt:".$rfc." [";
        for ($rj = 0; $rj < $rfc; $rj++) {
            my $ev = ${$rfa}[$rj][0]; # number in file 50, 51, ...., 56
            my $fr = (${$rfa}[$rj][1] / 100); # frequency x 100
            my $fn = ${$rfa}[$rj][2];   # type AWIS, CTAF, ...
            # prepare information
            my $evnm = 'UNK'.$ev.'?';
            my $ftyp = $ev - 50;
            if (($ftyp >= 0)&&($ftyp <= 6)) {
                $evnm = $off2name{$ftyp};
            }
            #$info .= " $ev $fr $fn";
            #$info .= " $evnm $fn $fr";
            if ($use_full_list) {
                $info .= " $evnm $fr ($fn)";
            } else {
                if (!defined $names{$evnm}) {
                    $names{$evnm} = [ ];
                }
                $rfna = $names{$evnm};
                push(@{$rfna}, "$fr ($fn)");
            }
        }
        if (!$use_full_list) {
            my ($key,$val);
            foreach $key (sort keys %names) {
                $rfna = $names{$key};
                $info .= " $key:";
                foreach $val (@{$rfna}) {
                    $info .= " $val";
                }
            }
        }
        $info .= ']';
    } else {
        $info .= " [No freq. info]";
    }
    return $info;
}

#                  0=typ, 1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name, 9=off, 10=dist, 11=az);
#push(@g_navlist3, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid,  $name,  $off,  $dist,   $az]);
sub show_airports_found {
	my ($mx) = shift;	# limit the AIRPORT OUTPUT
	my $scnt = $g_acnt;
	my $tile = '';
    my ($dist,$az,$adkm,$ahdg,$alat,$alon,$line,$diff,$icao,$name,$msg);
    my ($rrwys,$aalt);
    my $c_lat = $g_center_lat;
    my $c_lon = $g_center_lon;
    my ($dlat,$dlon);
    $msg = '[v1] ';
	if ($mx && ($mx < $scnt)) {
		$scnt = $mx;
		$msg .= "Listing $scnt of $g_acnt aiports ";
	} else {
		$msg .= "Listing $scnt aiport(s) ";
	}

	if ($SRCHICAO) {
		$msg .= "with ICAO [$apticao] ...";
	} elsif ($SRCHONLL) {
		$msg .= "around lat,lon [".ctr_latlon_stg()."], using diff [$maxlatd,$maxlond] ...";
	} else {
		$msg .= "matching [$aptname] ...";
	}
    prt("$msg\n") if (VERB1());
    # =========================================================================================================
    # search the airport list
    #                 0      1      2      3      4      5   6  7  8  9      10     11    12     13   14        15
    #push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@runways,$aalt]);
    @aptlist2 = sort mycmp_decend_ap_dist @aptlist2 if ($got_center_latlon);
    my ($i,$ra,$rtyp,$info,$rwycnt,$hdg,$rhdg,$gaoff,$tmp,$surf);
	for ($i = 0; $i < $scnt; $i++) {
		$diff = $aptlist2[$i][0];
		$icao = $aptlist2[$i][1];
		$name = $aptlist2[$i][2];
        $name = elim_the_dupes($name);

        # locations
		$dlat = $aptlist2[$i][3];   # LAT
		$dlon = $aptlist2[$i][4];   # LON
        $aalt = $aptlist2[$i][15];  # ALT (AMSL)

        $alat = $dlat;
        $alon = $dlon;

        # from center point, if there is one
        $dist = $aptlist2[$i][12];
        $az   = $aptlist2[$i][13];

        # get airport reference from main @g_aptlist
        $gaoff = get_g_aptlist_off($icao);

        if ($SRCHONLL) {
            if (int($az) == 400) {
            }
        }

        # @runways reference
        # 0   1=lat      2=lon      3=s 4=hdg  5=len 6=offsets 7=stopway 8=wid 9=lights 10=surf 11 12 13   14 15
        # 10  36.962213  127.031071 14x 131.52  8208 1595.0620 0000.0000 150   321321   1       0  3  0.25 0  0300.0300
        # 11=shoulder 12=marks 13=smooth 14=signs 15=GS angles
        # 0           3        0.25      0        0300.0300
        $rrwys = $aptlist2[$i][14];
        $rwycnt = scalar @{$rrwys};
        $info = "rwy:$rwycnt: ";
        foreach $ra (@{$rrwys}) {
            $tmp = scalar @{$ra};
            if ($tmp < 15) {
                foreach $hdg (@{$ra}) {
                    $info .= "[$hdg] ";
                }
                pgm_exit(1,"ERROR: Invalid runway array cnt $tmp! $info\n");
            }
            $rtyp = ${$ra}[3];
            $hdg  = ${$ra}[4];
            $rtyp =~ s/x+$//;
            if ($rtyp =~ /^\d+$/) {
                $rhdg = $rtyp * 10; # 2010-12-15 - get opposite end numbers
            } else {
                $rhdg = $hdg; # get opp heading, but may NOT be per numbers
            }
            $rhdg += 180; # reverse it
            $rhdg -= 360 if ($rhdg >= 360); # drop wrap

            # display it
            #$rtyp .= ' ' while (length($rtyp) < 3);
            $rhdg = int($rhdg / 10);
            $rhdg = "0$rhdg" if ($rhdg < 10);
            $info .= "\n" if (VERB1()); # new line
            $info .= " $rtyp/$rhdg ($hdg) ";
            $info .= ${$ra}[5]." ft.";
            $surf = ${$ra}[10]; # add surface type
            if (defined $runway_surface{$surf}) {
                $info .= " (s=".$runway_surface{$surf}.")";
            }
            if (VERB1()) {
                $info .= " ".${$ra}[1].",".${$ra}[2];
                if (VERB2()) {
                    # show ENDS of runway
                    my $rwlen2 = (${$ra}[5] * $SG_FEET_TO_METER) / 2;
                    my ($elat1,$elon1,$eaz1,$elat2,$elon2,$eaz2);
                    my $hdgr = $hdg + 180;
                    $hdgr -= 360 if ($hdgr >= 360);
                    fg_geo_direct_wgs_84( ${$ra}[1], ${$ra}[2], $hdg , $rwlen2, \$elat1, \$elon1, \$eaz1 );
                    fg_geo_direct_wgs_84( ${$ra}[1], ${$ra}[2], $hdgr, $rwlen2, \$elat2, \$elon2, \$eaz2 );
                    $info .= "\n $rtyp: $elat1,$elon1 $rhdg: $elat2,$elon2";
                }
            }
        }

		$tile = get_bucket_info( $alon, $alat );
        $adkm = sprintf( "%0.2f", ($dist / 1000.0));    # get kilometers
        $ahdg = sprintf( "%0.1f", $az );    # and azimuth

		while (length($icao) < 4) {
			$icao .= ' ';
		}

        # start the OUTPUT of an airport
		#$line = $diff;
		$line = $aalt;  # start with ALTITUDE (AMSL) feet
		$line = ' '.$line while (length($line) < 6);

        # name length = 'Chavenay Villepreux ' - say 24 for unique
        $name .= " " while (length($name) < 24);

        # expand to standard
        $alat = sprintf("%2.9f",$alat);
        $alon = sprintf("%3.9f",$alon);
        #$alat = ' '.$alat while (length($alat) < 12);
        #$alon = ' '.$alon while (length($alon) < 13);
		#$line .= ' '.$icao.' '.$name.' ('.$alat.','.$alon.") tile=$tile";
		$line .= ' '.$icao.' '.$name.' '.$alat.','.$alon;
        $line .= " ";
        if ($SRCHONLL) {
            # more information on a/p found...
            # $line .= ", ".$adkm."Km on $ahdg";
            $line .= get_fg_dist_dir($c_lat,$c_lon,$dlat,$dlon)." ";
        }
        $line .= "\n" if (VERB1());
        $line .= $info;
        if ($gaoff) {
            $info = get_atis_info($i,$scnt,\@aptlist2,$gaoff,$dlat,$dlon,$aalt);
            $line .= $info;
        }
        $line .= " fg=".get_tile($alon,$alat); # +/or? ." (".get_tile_calc($alon,$alat).")";
        # $line .= ")"; # close
        # show it
		prt("$line\n"); # print

		$outcount++;
		add_2_tiles($tile);
	}
	prt( "[v2] Done $scnt list ...\n" ) if (VERB2());
    return $scnt;
}

# 14/12/2010 - Switch to using the Bucket2.pm
# 02/05/2011 - add the tile INDEX
sub get_tile { # $alon, $alat
	my ($lon, $lat) = @_;
    my $b = Bucket2->new();
    $b->set_bucket($lon,$lat);
    return $b->gen_base_path()."/".$b->gen_index();
}

# 14/12/2010 - Add a fix for latitude south
# that is -30 is s40
sub get_tile_calc { # $alon, $alat
	my ($lon, $lat) = @_;
	my $tile = 'e';
	if ($lon < 0) {
		$tile = 'w';
		$lon = -$lon;
	}
	my $ilon = int($lon / 10) * 10;
	if ($ilon < 10) {
		$tile .= "00$ilon";
	} elsif ($ilon < 100) {
		$tile .= "0$ilon";
	} else {
		$tile .= "$ilon"
	}
	if ($lat < 0) {
		$tile .= 's';
		$lat = -$lat;
        $lat += 10;
	} else {
		$tile .= 'n';
	}
	my $ilat = int($lat / 10) * 10;
	if ($ilat < 10) {
		$tile .= "0$ilat";
	} elsif ($ilon < 100) {
		$tile .= "$ilat";
	} else {
		$tile .= "$ilat" # SHOULD NOT EXIST
	}
	return $tile;
}

sub add_2_tiles {	# $tile
	my ($tl) = shift;
	if (@tilelist) {
		foreach my $t (@tilelist) {
			if ($t eq $tl) {
				return 0;
			}
		}
	}
	push(@tilelist, $tl);
	return 1;
}

sub is_valid_nav {
	my ($t) = shift;
    if ($t && length($t)) {
        my $txt = "$t";
        my $cnt = 0;
        foreach my $n (@navset) {
            if ($n eq $txt) {
                $actnav = $navtypes[$cnt];
                return 1;
            }
            $cnt++;
        }
    }
	return 0;
}

sub set_average_apt_latlon {
    $g_acnt = scalar @aptlist2;
    $g_total_aps = scalar @g_aptlist;
	my $ac = $g_acnt;
	my $tlat = 0;
	my $tlon = 0;
    my ($alat,$alon);
    prt( "Found $g_acnt, of $totaptcnt, airports ...getting average...\n" ) if ($dbg3 || VERB9());
	if ($ac) {
		for (my $i = 0; $i < $ac; $i++ ) {
			$alat = $aptlist2[$i][3];
			$alon = $aptlist2[$i][4];
			$tlat += $alat;
			$tlon += $alon;
		}
		$av_apt_lat = $tlat / $ac;
		$av_apt_lon = $tlon / $ac;
        if ($SRCHICAO) {
            prt( "Found $g_acnt matching $apticao ...(av. $av_apt_lat,$av_apt_lon)\n" ) if ($dbg3 || VERB9());
        } elsif ($SRCHONLL) {
            prt( "Found $g_acnt matching ".ctr_latlon_stg()." ...(av. $av_apt_lat,$av_apt_lon)\n" ) if ($dbg3 || VERB9());
        } else {
            prt( "Found $g_acnt matching $aptname ... (av. $av_apt_lat,$av_apt_lon)\n" ) if ($dbg3 || VERB9());
        }
	}
}

#                 0      1      2      3      4      5   6  7  8  9      10     11    12     13   14        15
#push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@runways,$aalt]);
# my $nmaxlatd = 1.5;
# my $nmaxlond = 1.5;
sub near_an_airport {
	my ($lt, $ln, $dist, $az) = @_;
    my ($az1, $az2, $s, $ret);
	my $ac = scalar @aptlist2;
    my ($x,$y,$z) = fg_ll2xyz($ln,$lt);    # get cart x,y,z
    my $d2 = $max_range_km * 1000;      # get meters
    my ($alat,$alon,$diff,$icao,$name);
	for (my $i = 0; $i < $ac; $i++ ) {
		$diff = $aptlist2[$i][0];
		$icao = $aptlist2[$i][1];
		$name = $aptlist2[$i][2];
		$alat = $aptlist2[$i][3];
		$alon = $aptlist2[$i][4];
        if ($usekmrange) {
            my ($xb, $yb, $yz) = fg_ll2xyz($alon, $alat);
            my $dst = sqrt( fg_coord_dist_sq( $x, $y, $z, $xb, $yb, $yz ) ) * $DIST_FACTOR;
            if ($dst < $d2) {
                $s = -1;
                $az1 = -1;
                $ret = fg_geo_inverse_wgs_84($alat, $alon, $lt, $ln, \$az1, \$az2, \$s);
                $$dist = $s;
                $$az = $az1;
                return ($i + 1);
            }
        } else {
    		my $td = abs($lt - $alat);
	    	my $nd = abs($ln - $alon);
		    if (($td < $nmaxlatd)&&($nd < $nmaxlond)) {
                $s = -1;
                $az1 = -1;
                $ret = fg_geo_inverse_wgs_84($alat, $alon, $lt, $ln, \$az1, \$az2, \$s);
                $$dist = $s;
                $$az = $az1;
			    return ($i + 1);
		    }
        }
	}
	return 0;
}

sub not_in_world_range($$) {
    my ($lt,$ln) = @_;
    return 1 if ($lt < -90);
    return 1 if ($lt >  90);
    return 1 if ($ln < -180);
    return 1 if ($ln >  180);
    return 0;
}

# like sub near_an_airport {
sub near_given_point {
	my ($lt, $ln, $rdist, $raz) = @_;
    if ( not_in_world_range($lt,$ln) ) {
        prtw("WARNING: near_given_point given OUT OF WORLD RANGE [$lt,$ln]\n");
        return 0;
    }
    my ($az1, $az2, $s, $ret);
    my ($x,$y,$z) = fg_ll2xyz($ln,$lt);    # get cart x,y,z
    my $d2 = $max_range_km * 1000;      # get meters
    my $ngp_ret = 0;
    my ($alat,$alon);
    if ($SRCHONLL) {
	# for (my $i = 0; $i < $ac; $i++ ) {
	#	$diff = $aptlist2[$i][0];
	#	$icao = $aptlist2[$i][1];
	#	$name = $aptlist2[$i][2];
		$alat = $g_center_lat;
		$alon = $g_center_lon;
        if ($usekmrange) {
            my ($xb, $yb, $yz) = fg_ll2xyz($alon, $alat);
            my $dst = sqrt( fg_coord_dist_sq( $x, $y, $z, $xb, $yb, $yz ) ) * $DIST_FACTOR;
            if ($dst < $d2) {
                $s = -1;
                $az1 = -1;
                $ret = fg_geo_inverse_wgs_84($alat, $alon, $lt, $ln, \$az1, \$az2, \$s);
                ${$rdist} = $s;
                ${$raz}   = $az1;
                $ngp_ret = 1;
            }
        } else {
    		my $td = abs($lt - $alat);
	    	my $nd = abs($ln - $alon);
		    if (($td < $nmaxlatd)&&($nd < $nmaxlond)) {
                $s = -1;
                $az1 = -1;
                $ret = fg_geo_inverse_wgs_84($alat, $alon, $lt, $ln, \$az1, \$az2, \$s);
                ${$rdist} = $s;
                ${$raz} = $az1;
			    $ngp_ret = 1;
		    }
        }
	}
	return $ngp_ret;
}

sub show_nav_list($) {
    my ($rnl) = @_;
    my $cnt = scalar @{$rnl};
    my ($ic,$typ,$nlat,$nlon,$nalt,$nfrq,$nrng,$nfrq2,$nid,$name,$off,$dist,$az,$line);
    prt("$g_nav_hdr\n");
    for ($ic = 0; $ic < $cnt; $ic++) {
		$typ   = ${$rnl}[$ic][0];
		$nlat  = ${$rnl}[$ic][1];
		$nlon  = ${$rnl}[$ic][2];
		$nalt  = ${$rnl}[$ic][3];
		$nfrq  = ${$rnl}[$ic][4];
		$nrng  = ${$rnl}[$ic][5];
		$nfrq2 = ${$rnl}[$ic][6];
		$nid   = ${$rnl}[$ic][7];
		$name  = ${$rnl}[$ic][8];
		$off   = ${$rnl}[$ic][9];
        $dist  = ${$rnl}[$ic][10];
        $az    = ${$rnl}[$ic][11];

        $nalt  = ' '.$nalt while (length($nalt) < $g_maxnaltl);
        $nfrq  = ' '.$nfrq while (length($nfrq) < $g_maxnfrql);
        $nrng  = ' '.$nrng while (length($nrng) < $g_maxnrngl);
        $nfrq2 = ' '.$nfrq2 while (length($nfrq2) < $g_maxnfq2l);
        $nid   = ' '.$nid while (length($nid) < $g_maxnnidl);
        $nlat  = ' '.$nlat while (length($nlat) < $g_maxnlatl);
        $nlon  = ' '.$nlon while (length($nlon) < $g_maxnlonl);

		is_valid_nav($typ); # set global $actnav
        $line   = $actnav;
        $line  .= ' ' while (length($line) < $maxnnlen);
        $line  .= ' ';
        $line .= "$nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name";
        prt("$line\n");
    }
}

sub show_navaids_found {
	my ($ic, $in, $line, $lcnt, $dnone);
	my ($icao, $alat, $alon);
    # my ($diff); 20110221 - remove display of this DIFF???
	my ($typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off);
    my ($dist, $az, $adkm, $ahdg);
    my ($apds,$apaz,$dist_hdg,$add,$ap_line,$amsl);
    my $msg = '';
    my $hdr = "Type  Latitude     Logitude        Alt.  Freq.  Range  Frequency2    ID  Name";
	#prt( "$actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n");
	#push(@navlist2, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off]);
    my $nearna = scalar @g_navlist3;
	my $ac = scalar @aptlist2; # found APT list count
    # found within range of AP (or CENTER)
    if ($sort_by_distance) {
        @navlist2 = sort mycmp_decend_dist @navlist2;
    } elsif ($sortbyfreq) {
        @navlist2 = sort mycmp_ascend_n4 @navlist2;
    }
	my $nc = scalar @navlist2;
    my $tot = $nc + $nearna;
    my $dspcnt = 0;
    $msg = "For ";
    $msg .= "$nearna 'near' aids, " if ($nearna);
    $msg .= "$ac airports, found $tot NAVAIDS, ";
    if ($usekmrange) {
    	$msg .= "within [$max_range_km] Km ...";
    } else {
    	$msg .= "within [$nmaxlatd,$nmaxlond] degrees ...";
    }
    prt("$msg\n") if (VERB1());
	$lcnt = 0;
    prt("List $nearna NEAR [".ctr_latlon_stg()."]...\n") if ($nearna && VERB1());
    #my @navlist3 = sort mycmp_decend_dist @g_navlist3;
    #my $rnavlist3 = \@navlist3;
    @g_navlist3 = sort mycmp_decend_dist @g_navlist3;
    my $rnavlist_d = \@g_navlist3;
    my @g_navlist_az = sort mycmp_decend_az @g_navlist3;
    my $rnavlist3 = \@g_navlist_az;
    $dspcnt = 0;
    $dnone = 0; # header display line control
    my ($dlat,$dlon);   # here $nlat is often 'extended' for display, but this is the NUMBER
	for ($ic = 0; $ic < $nearna; $ic++) {
		$typ = ${$rnavlist3}[$ic][0];
		$dlat = ${$rnavlist3}[$ic][1];
		$dlon = ${$rnavlist3}[$ic][2];
		$nlat = ${$rnavlist3}[$ic][1];
		$nlon = ${$rnavlist3}[$ic][2];
		$nalt = ${$rnavlist3}[$ic][3];
		$nfrq = ${$rnavlist3}[$ic][4];
		$nrng = ${$rnavlist3}[$ic][5];
		$nfrq2 = ${$rnavlist3}[$ic][6];
		$nid = ${$rnavlist3}[$ic][7];
		$name = ${$rnavlist3}[$ic][8];
		$off = ${$rnavlist3}[$ic][9];
        $dist = ${$rnavlist3}[$ic][10];
        $az = ${$rnavlist3}[$ic][11];
		is_valid_nav($typ); # set global $actnav
        if ($vor_only) {
            if (($actnav =~ /VOR/) || ($actnav =~ /NBD/)) {
                # these are OK
            } else {
                next;
            }
        }
        $dspcnt++;
        $line = $actnav;
        $line .= ' ' while (length($line) < $maxnnlen);

        $line .= ' ';
        $nalt = ' '.$nalt while (length($nalt) < 5);

        $nfrq = ' '.$nfrq while (length($nfrq) < 5);
        $nrng = ' '.$nrng while (length($nrng) < 5);
        $nfrq2 = ' '.$nfrq2 while (length($nfrq2) < 10);
        $nid = ' '.$nid while (length($nid) < 4);
        $nlat = ' '.$nlat while (length($nlat) < 12);
        $nlon = ' '.$nlon while (length($nlon) < 13);
        $adkm = sprintf( "%0.2f", ($dist / 1000.0));    # get kilometers
        $ahdg = sprintf( "%0.1f", $az );    # and azimuth
        $name = elim_the_dupes($name);
        $name .= ' ' while (length($name) < $g_max_name_len);
        $dist_hdg = "(".$adkm."Km on $ahdg, ap$off)"; # keep separate
        $line .= "$nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name";
        if ($dnone == 0) {
            #prt( "Type  Latitude     Logitude        Alt.  Freq.  Range  Frequency2    ID  Name\n" );
            prt( "$hdr\n" ) if (VERB1());
            $dnone++;
        }
        if (defined $g_dupe_shown{$line}) {
            prt( "[v5] DupeNR: Shown [$actnav $nlat,$nlon $name] - SHOWN\n") if (VERB5());
        } else {
            prt( "$line $dist_hdg\n" ) if (VERB9());
            $outcount++;
            $lcnt++;
            $g_dupe_shown{$line} = 1;
            $msg = $actnav;
            $msg .= ' ' while (length($msg) < $maxnnlen);
            $adkm = sprintf( "%03.1f", ($dist / 1000.0));    # get kilometers
            $adkm = " $adkm" while (length($adkm) < 6);
            $ahdg = sprintf( "%03.1f", $az );    # and azimuth
            $ahdg = " $ahdg" while (length($ahdg) < 5);
            $msg .= "$adkm"."Km on hdg $ahdg";
            $msg .= ", freq $nfrq, ident $nid $name $nlat $nlon";
            prt("$msg\n");
        }
    }
    prt("Done $dspcnt of $nearna NEAR [".ctr_latlon_stg()."]...\n") if ($nearna && VERB1());
	$lcnt = 0;
    # count objects for display...
    $dspcnt = 0;
    # go through found airport list
	for ($ic = 0; $ic < $ac; $ic++) {
		# $diff = $aptlist2[$ic][0];
		$icao = $aptlist2[$ic][1];
		$name = $aptlist2[$ic][2];
		$alat = $aptlist2[$ic][3];
		$alon = $aptlist2[$ic][4];
        $apds = $aptlist2[$ic][12];
        $apaz = $aptlist2[$ic][13];
        $amsl = $aptlist2[$ic][15]; # get APT altitude (AMSL) feet
		$icao .= ' ' while (length($icao) < 4);
		# $line = $diff; # this 'diff' is what exactly? 
		$line = $amsl; # 20110221 - Now start with AMSL (feet)
		$line = ' '.$line while (length($line) < 6);
		$line .= ' '.$icao.' '.$name.' ('.$alat.','.$alon.')';
		$dnone = 0;
		for ( $in = 0; $in < $nc; $in++ ) {
			$typ = $navlist2[$in][0];
            $dlat = ${$rnavlist3}[$ic][1];
            $dlon = ${$rnavlist3}[$ic][2];
			$nlat = $navlist2[$in][1];
			$nlon = $navlist2[$in][2];
			$nalt = $navlist2[$in][3];
			$nfrq = $navlist2[$in][4];
			$nrng = $navlist2[$in][5];
			$nfrq2 = $navlist2[$in][6];
			$nid = $navlist2[$in][7];
			$name = $navlist2[$in][8];
			$off = $navlist2[$in][9];
            $dist = $navlist2[$in][10];
            $az = $navlist2[$in][11];
			if ($off == ($ic + 1)) {
				# it is FOR this airport
				is_valid_nav($typ);
                if ($vor_only) {
                    if (($actnav =~ /VOR/) || ($actnav =~ /NBD/)) {
                        # these are OK
                    } else {
                        next;
                    }
                }
    			#     NDB  50.049000, 008.328667,   490,   399,    25,      0.000,  WBD, Wiesbaden NDB (ap=2 nnnKm on 270.1)
                #     Type Latitude   Logitude     Alt.  Freq.  Range  Frequency2    ID  Name
                #     VOR  37.61948300, -122.37389200,    13, 11580,    40,       17.0,  SFO, SAN FRANCISCO VOR-DME (ap=1 nnnKm on 1.1)
				#prt( "$actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n");
                $dspcnt++;
			}
		}
		# prt( "$hdr\n" ) if ($dnone && VERB1());
	}
    prt("List for $ac airports... near [".ctr_latlon_stg()."]... display $dspcnt...\n") if ($ac && VERB1());
    $lcnt = 0;
    $ap_line = '';
	for ($ic = 0; $ic < $ac; $ic++) {
		# $diff = $aptlist2[$ic][0];
		$icao = $aptlist2[$ic][1];
		$name = $aptlist2[$ic][2];
		$alat = $aptlist2[$ic][3];
		$alon = $aptlist2[$ic][4];
        $apds = $aptlist2[$ic][12];
        $apaz = $aptlist2[$ic][13];
        $amsl = $aptlist2[$ic][15];
        
		$icao .= ' ' while (length($icao) < 4);

		# $line = $diff; # start with 'offset' from center point
        $line = $amsl; # start with ALTITUDE (AMSL) feet
		$line = ' '.$line while (length($line) < 6);
		$line .= ' '.$icao.' '.$name.' ('.$alat.','.$alon.')';

        $dspcnt = 0;
        # check the LIST of viable items to DISPLAY
		for ( $in = 0; $in < $nc; $in++ ) {
			$typ = $navlist2[$in][0];
			$off = $navlist2[$in][9];
			if ($off == ($ic + 1)) {
				is_valid_nav($typ);
                if ($vor_only) {
                    if (($actnav =~ /VOR/) || ($actnav =~ /NBD/)) {
                        $dspcnt++; # these are OK
                    }
                } else {
                    $dspcnt++;
                }
            }
        }
        $line .= " ($dspcnt)";
        prt("\n") if ($ic && $dspcnt && VERB1());
		# prt("$line\n");
        $ap_line = $line;   # setup to SHOW, only if a NAV AID is to be displayed
		$outcount++;
		$dnone = 0;
        $line = '';
		for ( $in = 0; $in < $nc; $in++ ) {
			$typ = $navlist2[$in][0];
			$dlat = $navlist2[$in][1];
			$dlon = $navlist2[$in][2];
			$nalt = $navlist2[$in][3];
			$nfrq = $navlist2[$in][4];
			$nrng = $navlist2[$in][5];
			$nfrq2 = $navlist2[$in][6];
			$nid = $navlist2[$in][7];
			$name = $navlist2[$in][8];
			$off = $navlist2[$in][9];
            $dist = $navlist2[$in][10];
            $az = $navlist2[$in][11];
			$nlat = $dlat;
			$nlon = $dlon;
			if ($off == ($ic + 1)) {
				# it is FOR this airport
				is_valid_nav($typ);
                if ($vor_only) {
                    if (($actnav =~ /VOR/) || ($actnav =~ /NBD/)) {
                        # these are OK
                    } else {
                        next;
                    }
                }
    			#     NDB  50.049000, 008.328667,   490,   399,    25,      0.000,  WBD, Wiesbaden NDB (ap=2 nnnKm on 270.1)
                #     Type Latitude   Logitude     Alt.  Freq.  Range  Frequency2    ID  Name
                #     VOR  37.61948300, -122.37389200,    13, 11580,    40,       17.0,  SFO, SAN FRANCISCO VOR-DME (ap=1 nnnKm on 1.1)
				#prt( "$actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n");
                # start line
				$line = $actnav;
				$line .= ' ' while (length($line) < $maxnnlen);
				$line .= ' ';

				$nalt = ' '.$nalt while (length($nalt) < 5);
				$nfrq = ' '.$nfrq while (length($nfrq) < 5);
				$nrng = ' '.$nrng while (length($nrng) < 5);
				$nfrq2 = ' '.$nfrq2 while (length($nfrq2) < 10);
				$nid = ' '.$nid while (length($nid) < 4);
                $nlat = ' '.$nlat while (length($nlat) < 12);
                $nlon = ' '.$nlon while (length($nlon) < 13);
                $adkm = sprintf( "%0.2f", ($dist / 1000.0));    # get kilometers
                $ahdg = sprintf( "%0.1f", $az );    # and azimuth
                $name = elim_the_dupes($name);
                $name .= ' ' while (length($name) < $g_max_name_len);
                $dist_hdg = "(".$adkm."Km on $ahdg, ap$off)";
				$line .= "$nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name";
                $add = 0;
                if (defined $g_dupe_shown{$line}) {
                    if (VERB5()) {
                        $add = 1;
                    }
                } else {
                    $add = 1;
                }

				if ($add && ($dnone == 0)) {
					#prt( "Type  Latitude     Logitude        Alt.  Freq.  Range  Frequency2    ID  Name\n" );
                    if (VERB1()) {
                        if (length($ap_line)) {
                            prt("$ap_line\n");
                            $ap_line = '';
                        }
    					prt( "$hdr\n" );

                    }
					$dnone = 1;
				}
  				$outcount++;
    			$lcnt++;
                if (defined $g_dupe_shown{$line}) {
                    if (VERB5()) {
                        prt( "DupeAID: Shown [$actnav $nlat,$nlon $name] - SHOWN\n");
                    }
                } else {
                    if (length($ap_line)) {
                        prt("$ap_line\n");
                        $ap_line = '';
                    }
                    if ($check_sg_dist) {
                        my ($sg_az1,$sg_az2,$sg_dist);
                        my $res = fg_geo_inverse_wgs_84 ($g_center_lat,$g_center_lon,$dlat,$dlon,\$sg_az1,\$sg_az2,\$sg_dist);
                        my $sg_km = $sg_dist / 1000;
                        my $sg_im = int($sg_dist);
                        my $sg_ikm = int($sg_km + 0.5);
                        # if (abs($sg_pdist) < $CP_EPSILON)
                        $dist_hdg .= " (SGDist: ";
                        $sg_az1 = int(($sg_az1 * 10) + 0.05) / 10;
                        if (abs($sg_km) > $SG_EPSILON) { # = 0.0000001; # EQUALS SG_EPSILON 20101121
                            if ($sg_ikm && ($sg_km >= 1)) {
                                $sg_km = int(($sg_km * 10) + 0.05) / 10;
                                $dist_hdg .= "$sg_km km";
                            } else {
                                $dist_hdg .= "$sg_im m, <1km";
                            }
                        } else {
                            $dist_hdg .= "0 m";
                        }
                        $dist_hdg .= " on $sg_az1";
                        $dist_hdg .= ")";
                    }
                    #prt( "$lcnt: $line $dist_hdg\n" );
                    prt( "$line $dist_hdg\n" );
                    $g_dupe_shown{$line} = 1;
		    		add_2_tiles( get_bucket_info( $nlon, $nlat ) );
                }
			}
		}
		prt( "$hdr\n" ) if ($dnone &&  VERB1() );
	}
	prt( "[v5] Listed $lcnt NAVAIDS ...\n" ) if (VERB5());
}

# LOAD apt.dat.gz
# details see : http://data.x-plane.com/file_specs/Apt810.htm
# Line codes used in apt.dat (810 version) 
# Airport Line - eg 
# 0  1    2   3   4    5++
# 1  1050 0   0   YGIL Gilgandra
# ID AMSL Twr Bld ICAO Name
# Code (apt.dat) Used for 
# 1 Airport header data. 
# 16 Seaplane base header data. No airport buildings or boundary fences will be rendered in X-Plane. 
# 17 Heliport header data.  No airport buildings or boundary fences will be rendered in X-Plane. 
# 10 Runway or taxiway at an airport. 
# 14 Tower view location. 
# 15 Ramp startup position(s) 
# 18 Airport light beacons (usually "rotating beacons" in the USA).  Different colours may be defined. 
# 19 Airport windsocks. 
# 50 to 56 Airport ATC (Air Traffic Control) frequencies. 
# runway
# 0  1           2          3   4       5    6         7             8 9      10  11   12    13    14     15
# 10 -31.696928  148.636404 15x 162.00  4204 0000.0000 0000.0000    98 121121  5   0    2    0.25   0     0000.0000
# rwy lat        lon        num true    feet displament/extension  wid lights surf shld mark smooth signs VASI
sub load_apt_data {
    my ($cnt,$msg);
    prt("[v9] Loading $aptdat file ...\n") if (VERB9());
    mydie("ERROR: Can NOT locate $aptdat ...$!...\n") if ( !( -f $aptdat) );
    ###open IF, "<$aptdat" or mydie("OOPS, failed to open [$aptdat] ... check name and location ...\n");
    open IF, "gzip -d -c $aptdat|" or mydie( "ERROR: CAN NOT OPEN $aptdat...$!...\n" );
    my @lines = <IF>;
    close IF;
    $cnt = scalar @lines;
    prt("[v9] Got $cnt lines to scan...\n") if (VERB9());
    my ($add,$alat,$alon);
    # ================
    # SEARCH THE LINES
    # search ICAO, POSITION, or NAME...
    # ================
    $add = 0;
    my ($off,$dist,$az,@arr,@arr2,$rwyt,$glat,$glon,$dlat,$dlon,$rlat,$rlon);
    my ($line,$apt,$diff,$rwycnt,$icao,$name,@runways,$version);
    my ($aalt,$actl,$abld,$ftyp,$cfrq,$frqn,@freqs);
    my $total_apts = 0;
    my $total_lat = 0;
    my $total_lon = 0;
    my $c_lat = $g_center_lat;
    my $c_lon = $g_center_lon;
    $off = 0;
    $dist = 0;
    $az = 0;
    $glat = 0;
    $glon = 0;
    $apt = '';
    $rwycnt = 0;
    @runways = ();
    @freqs = ();
    $msg = '[v1] ';
    if ($SRCHICAO) {
        $msg .= "Search ICAO [$apticao]...";
    } elsif ($SRCHONLL) {
        $msg .= "Search LAT,LON [".ctr_latlon_stg()."], w/diff [$maxlatd,$maxlond]...";
    } else {
        $msg .= "Search NAME [$aptname]...";
    }
    $msg .= " got $cnt lines, FOR airports,rwys,txwys... ";
    foreach $line (@lines) {
        $line = trimall($line);
        if ($line =~ /\s+Version\s+/i) {
            @arr2 = split(/\s+/,$line);
            $version = $arr2[0];
            $msg .= "Version $version";
            last;
        }
    }
    prt("$msg\n") if (VERB1());
    my $lncnt = 0;
    foreach $line (@lines) {
        $lncnt++;
        $line = trimall($line);
        ###prt("$line\n");
        my @arr = split(/ /,$line);
        if ($line =~ /^$aln\s+/) {	# start with '1'
            # 0  1   2 3 4     
            # 17 126 0 0 EH0001 [H] VU medisch centrum
            # ID ALT C B NAME++
            if (length($apt) && ($rwycnt > 0)) {
                # average position
                $alat = $glat / $rwycnt;
                $alon = $glon / $rwycnt;
                $off = -1;
                $dist = 9999000;
                $az = 400;
                #$off = near_given_point( $alat, $alon, \$dist, \$az );
                $dlat = abs( $c_lat - $alat );
                $dlon = abs( $c_lon - $alon );
                $diff = int( ($dlat * 10) + ($dlon * 10) );
                @arr2 = split(/ /,$apt);
                $aalt = $arr2[1]; # Airport (general) ALTITUDE AMSL
                $actl = $arr2[2]; # control tower
                $abld = $arr2[3]; # buildings
                $icao = $arr2[4]; # ICAO
                $name = join(' ', splice(@arr2,5)); # Name
                ##prt("$diff [$apt] (with $rwycnt runways at [$alat, $alon]) ...\n");
                ##prt("$diff [$icao] [$name] ...\n");
                #push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az]);
                my @f = @freqs;
                #                 0      1      2      3      4      5      6
                push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, $aalt, \@f]);
                #prt("$icao, $name, $alat, $alon, $aalt, $rwycnt runways\n");
                $add = 0;   # add to FOUND a/p, IFF
                if ($SRCHICAO) {
                    # 1 - ICAO matches
                    $add = 1 if ($icao =~ /$apticao/);
                } elsif ($SRCHONLL) {
                    # 2 - searching by LAT,LON position
                    if (($dlat < $maxlatd) && ($dlon < $maxlond)) {
                        $add = 1;
                    }
                } else {
                    # 3 - searching by airport name
                    $add = 1 if ($name =~ /$aptname/i);
                }
                if ($add) {
                    $off = near_given_point( $alat, $alon, \$dist, \$az ); # if ($SRCHONLL), near GLOBAL center
                    prt("[v1] Adding: $icao, $name, $alat, $alon, rwys $rwycnt...\n") if ($dbg1 || VERB1());
    				#                  0=typ, 1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name, 9=off, 10=dist, 11=az);
	    			#push(@g_navlist3, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
                    my @a = @runways;
                    #                 0     1      2      3      4      5   6  7  8  9      10     11    12     13   14   15
                    push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@a, $aalt]); # = @runways
                    $total_lat += $alat;
                    $total_lon += $alon;
                    $total_apts++;
                }
            }
            $apt = $line;
            $rwycnt = 0;
            @runways = ();  # clear RUNWAY list
            @freqs = (); # clear frequencies
            $glat = 0;
            $glon = 0;
            $totaptcnt++;	# count another AIRPORT
        } elsif ($line =~ /^$rln\s+/) {
            # 10  36.962213  127.031071 14x 131.52  8208 1595.0620 0000.0000   150 321321  1 0 3 0.25 0 0300.0300
            # 10  36.969145  127.020106 xxx 221.51   329 0.0 0.0    75 161161  1 0 0 0.25 0 
            $rlat = $arr[1];
            $rlon = $arr[2];
            $rwyt = $arr[3]; # text 'xxx'=taxiway, 'H1x'=heleport, else a runway
            ###prt( "$line [$rlat, $rlon]\n" );
            if ( $rwyt ne "xxx" ) {
                $glat += $rlat;
                $glon += $rlon;
                $rwycnt++;
                push(@runways, \@arr);
            }
        } elsif ($line =~ /^5(\d+)\s+/) {
            # frequencies
            $ftyp = $1;
            $cfrq = $arr[1];
            $frqn = $arr[2];
            $add = 0;
            if ($ftyp == 0) {
                $add = 1; # ATIS
            } elsif ($ftyp == 1) {
                $add = 1; # Unicom
            } elsif ($ftyp == 2) {
                $add = 1; # clearance
            } elsif ($ftyp == 3) {
                $add = 1; # ground
            } elsif ($ftyp == 4) {
                $add = 1; # tower
            } elsif ($ftyp == 5) {
                $add = 1; # approach
            } elsif ($ftyp == 6) {
                $add = 1; # departure
            }
            if ($add) {
                push(@freqs, \@arr); # save the freq array
            } else {
                pgm_exit(1, "WHAT IS THIS [5$ftyp $cfrq $frqn] [$line]\n FIX ME!!!");
            }
        } elsif ($line =~ /^$sealn\s+/) { # =  '16'; # Seaplane base header data.
            $rwycnt = 0;
            @runways = ();  # clear RUNWAY list
            @freqs = (); # clear frequencies
            $glat = 0;
            $glon = 0;
        } elsif ($line =~ /^$heliln\s+/) { # = '17'; # Heliport header data.  
            $rwycnt = 0;
            @runways = ();  # clear RUNWAY list
            @freqs = (); # clear frequencies
            $glat = 0;
            $glon = 0;
        } elsif ($line =~ /^$lastln\s?/) {	# 99, followed by space, count 0 or more ...
            prt( "Reached END OF FILE ... \n" ) if ($dbg1);
            last;
        }
    }

    # do any LAST entry
    $add = 0;
    $off = -1;
    $dist = 0;
    $az = 0;
    if ($rwycnt > 0) {
        $alat = $glat / $rwycnt;
        $alon = $glon / $rwycnt;
        $off = -1;
        $dist = 999999;
        $az = 400;
        #$off = near_given_point( $alat, $alon, \$dist, \$az );
        $dlat = abs( $c_lat - $alat );
        $dlon = abs( $c_lon - $alon );
        $diff = int( ($dlat * 10) + ($dlon * 10) );
        @arr2 = split(/ /,$apt);
        $aalt = $arr2[1];
        $actl = $arr2[2]; # control tower
        $abld = $arr2[3]; # buildings
        $icao = $arr2[4];
        $name = join(' ', splice(@arr2,5));
        ###prt("$diff [$apt] (with $rwycnt runways at [$alat, $alon]) ...\n");
        ###prt("$diff [$icao] [$name] ...\n");
        ###push(@g_aptlist, [$diff, $icao, $name, $alat, $alon]);
        ##push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az]);
        my @f = @freqs;
        #                 0      1      2      3      4      5      6
        push(@g_aptlist, [$diff, $icao, $name, $alat, $alon, $aalt, \@f]);
        $totaptcnt++;	# count another AIRPORT
        $add = 0;
        if ($SRCHICAO) {
            $add = 1 if ($name =~ /$apticao/);
        } else {
            if ($SRCHONLL) {
                if (($dlat < $maxlatd) && ($dlon < $maxlond)) {
                    $add = 1;
                }
            } else {
                $add = 1 if ($name =~ /$aptname/i);
            }
        }
        if ($add) {
             $off = near_given_point( $alat, $alon, \$dist, \$az ); # if ($SRCHONLL), near GLOBAL center
             prt("$icao, $name, $alat, $alon, rwycnt $rwycnt - LAST\n") if ($dbg1);
             #                  0      1      2      3      4      5   6  7  8  9      10     11    12     13   14        15
             # push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@runways,$aalt]);
    		 #                  0=typ, 1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name, 9=off, 10=dist, 11=az);
	    	 #push(@g_navlist3, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
             my @a2 = @runways;
             push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@a2, $aalt]); # = @runways + altitude
            $total_lat += $alat;
            $total_lon += $alon;
            $total_apts++;
        }
    }
    if ( (! $SRCHONLL) && $total_apts) {
        # either by ICAO or Name search, so this become CENTER lat/lon
        $g_center_lat = $total_lat / $total_apts;
        $g_center_lon = $total_lon / $total_apts;
        prt( "[v1] Set CENTER LAT,LON [".ctr_latlon_stg()."]\n" ) if (VERB1());

    }
    ### pgm_exit(1,"TEMP EXIT");
    $cnt =scalar @g_aptlist;
    prt("[v9] Done scan of $lncnt lines for $cnt airports...\n") if (VERB9());
}

sub load_nav_file {
	prt("\n[v9] Loading $navdat file ...\n") if (VERB9());
	mydie("ERROR: Can NOT locate [$navdat]!\n") if ( !( -f $navdat) );
	open NIF, "gzip -d -c $navdat|" or mydie( "ERROR: CAN NOT OPEN $navdat...$!...\n" );
	my @nav_lines = <NIF>;
	close NIF;
    prt("[v9] Got ".scalar @nav_lines." lines to scan...\n") if (VERB9());
    return @nav_lines;
}

# we have NOT found an airport by an ICAO name, search,
# and group by area nav with PART of this name...
sub search_nav_name() {
    my $cnt = 0;
    my @nav_lines = load_nav_file();
    my ($line,$lnn,$len,$nc,$vcnt);
    my ($nlat,$nlon,$nalt,$nfreq,$nid,$name,$i,$ln);
    my (@arr,$typ,$off,$nfrq,$nfrq2,$nrng,$dist,$az);
    my $rnls = \@nav_lines;
	my $nav_cnt = scalar @{$rnls};
    my $aptid = substr($apticao,1); # drop the country letter
    my $found = 0;
    my @navlist = ();
    prt("Searching $nav_cnt navaid records...using ICAO [$apticao], apt-id [$aptid]...\n");
    for ($ln = 0; $ln < $nav_cnt; $ln++) {
        $line = ${$rnls}[$ln];
		$line = trimall($line);
        $len = length($line);
        $lnn++;
        next if ($line =~ /\s+Version\s+/i);
        next if ($line =~ /^I/);
        next if ($len == 0);
		@arr = split(/ /,$line);
		$nc = scalar @arr;
		$typ = $arr[0];
        last if ($typ == 99);
        if ($nc < 8) {
            prt("Type: [$typ] - Handle this line [$line] - count = $nc...\n");
            pgm_exit(1,"ERROR: FIX ME FIRST!\n");
        }
        # Check for type number in @navset, and set $actnav to name, like VOR, NDB, etc
        $off = 0;
		if ( is_valid_nav($typ) ) {
			$vcnt++;
			$nlat  = $arr[1];
			$nlon  = $arr[2];
			$nalt  = $arr[3];
			$nfrq  = $arr[4];
			$nrng  = $arr[5];
			$nfrq2 = $arr[6];
			$nid   = $arr[7];
			$name  = '';
			for ($i = 8; $i < $nc; $i++) {
				$name .= ' ' if length($name);
				$name .= $arr[$i];
			}
            if ($nid =~ /$aptid/) {
                $off  = 0;
                $dist = -1; # this is by NAME, not location
                $az   = 400;
                prt( "[04] $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name\n"); # if ($dbg_fa04);
                #                 0=typ, 1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name, 9=off, 10=dist, 11=az);
                push(@navlist, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
                $found++;
            }
        }
    }
    prt("Searched $vcnt navais records... found $found with [$aptid]...\n");
    if ($found) {
        show_nav_list(\@navlist);
    }
}


# ---------------------------------------------------------
# sub search_nav 
# Scan the NAVAID lines
# Run 1:
# Populate @g_navlist3 with navaids found within a given range of a CENTER point ($g_lat,$g_lon)
#                  0=typ, 1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name, 9=off, 10=dist, 11=az);
# push(@g_navlist3, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
# Run 2:
# Populate @navlist2 with navaids found within a given range of the AIRPORTS found, else the CENTER point
#prt( "[02] $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n") if ($dbg_fa02);
#                 0     1      2      3      4      5      6       7     8      9     10     11
#push(@navlist2, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
#
# ---------------------------------------------------------
sub search_nav {
	my ($typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off);
    my ($alat, $alon);
    my ($dist, $az,$msg);
    my $cnt = 0;
    my @nav_lines = load_nav_file();
	my $nav_cnt = scalar @nav_lines;
	my $ac = scalar @aptlist2;
    $msg = '';
    $msg .= "Search FOR [".ctr_latlon_stg()."]... ";
    #                 0      1      2      3      4      5   6  7  8  9      10     11    12     13   14        15
    #push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@runways,$aalt]);
    if ($ac == 1) {
   		$alat = $aptlist2[0][3];
		$alon = $aptlist2[0][4];
        if ($usekmrange) {
            $msg .= "Use max [$max_range_km] Km from $ac ap at $alat,$alon.";
        } else {
            $msg .= "Use dev [$nmaxlatd,$nmaxlond] from $ac ap at $alat,$alon.";
        }
    } else {
        if ($usekmrange) {
            $msg .= "Use max dist [$max_range_km] Km from $ac apts.";
        } else {
            $msg .= "Use dev [$nmaxlatd,$nmaxlond] from $ac apts.";
        }
    }
    $msg .= " in $nav_cnt NAV lines...";

    prt("$msg\n") if (VERB1());
	my $vcnt = 0;
    my $navs_found = 0;
    my (@arr,$nc,$lnn,$len,$dnvers,$line);
    $lnn = 0;
    $lnn = 0;
    $dnvers = 0;
    $cnt = 0;
	foreach $line (@nav_lines) {
        $cnt++;
		$line = trimall($line);
        $len = length($line);
        $lnn++;
        # 810 Version - data cycle 2009.12, build 20091080, metadata NavXP810
        if ($line =~ /\s+Version\s+/i) {
            if ($line =~ /\s*(\n+)\s+Version\s+/) {
                $nav_file_version = $1;
            }
            $typ = length($line) > 80 ? substr($line,0,80) : $line;
            prt( "[v2] NAVAID: $typ\n" ) if (VERB2());
            $dnvers = 1;
            last;
        }
    }
    $lnn = 0;
    $cnt = scalar @nav_lines;
	prt( "[04] Doing 'center' search... $cnt NAV objects\n") if ($dbg_fa04);
    $cnt = 0;
    $vcnt = 0;
	foreach $line (@nav_lines) {
		$line = trimall($line);
        $len = length($line);
        $lnn++;
        next if ($line =~ /\s+Version\s+/i);
		@arr = split(/ /,$line);
		$nc = scalar @arr;
		$typ = $arr[0];
        # Check for type number in @navset, and set $actnav to name, like VOR, NDB, etc
        $off = 0;
		if ( is_valid_nav($typ) ) {
			$vcnt++;
			$nlat = $arr[1];
			$nlon = $arr[2];
			$nalt = $arr[3];
			$nfrq = $arr[4];
			$nrng = $arr[5];
			$nfrq2 = $arr[6];
			$nid = $arr[7];
			$name = '';
			for (my $i = 8; $i < $nc; $i++) {
				$name .= ' ' if length($name);
				$name .= $arr[$i];
			}
            $off = near_given_point( $nlat, $nlon, \$dist, \$az );
			if ($off) {
                $off = 2;
				prt( "[04] $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n") if ($dbg_fa04);

				#                 0=typ, 1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name, 9=off, 10=dist, 11=az);
				push(@g_navlist3, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
                $cnt++;
			}
        }
    }
  	prt( "[04] Done 'center' search, $vcnt valid, found $cnt...\n") if ($dbg_fa04);

    $lnn = 0;
    $vcnt = 0;
	foreach $line (@nav_lines) {
		$line = trimall($line);
        $len = length($line);
        $lnn++;
        my ($tmp);
        # 810 Version - data cycle 2009.12, build 20091080, metadata NavXP810
        if ($line =~ /\s+Version\s+/i) {
            if ($line =~ /\s*(\n+)\s+Version\s+/) {
                $nav_file_version = $1;
            }
            $typ = length($line) > 80 ? substr($line,0,80) : $line;
            prt( "[v2] NAVAID: $typ\n" ) if (VERB2() && !$dnvers);
            next;
        }
		###prt("$line\n");
		@arr = split(/ /,$line);
		# 0   1 (lat)   2 (lon)        3     4   5           6   7  8++
		# 2   38.087769 -077.324919  284   396  25       0.000 APH  A P Hill NDB
		# 3   57.103719  009.995578   57 11670 100       1.000 AAL  Aalborg VORTAC
		# 4   39.980911 -075.877814  660 10850  18     281.662 IMQS 40N 29 ILS-cat-I
		# 4  -09.458922  147.231225  128 11010  18     148.650 IWG  AYPY 14L ILS-cat-I
		# 5   40.034606 -079.023281 2272 10870  18     236.086 ISOZ 2G9 24 LOC
		# 5   67.018506 -050.682072  165 10955  18      61.600 ISF  BGSF 10 LOC
		# 6   39.977294 -075.860275  655 10850  10  300281.205 ---  40N 29 GS
		# 6  -09.432703  147.216444  128 11010  10  302148.785 ---  AYPY 14L GS
		# 7   39.960719 -075.750778  660     0   0     281.205 ---  40N 29 OM
		# 7  -09.376150  147.176867  146     0   0     148.785 JSN  AYPY 14L OM
		# 8  -09.421875  147.208331   91     0   0     148.785 MM   AYPY 14L MM
		# 8  -09.461050  147.232544  146     0   0     328.777 PY   AYPY 32R MM
		# 9   65.609444 -018.052222   32     0   0      22.093 ---  BIAR 01 IM
		# 9   08.425319  004.475597 1126     0   0      49.252 IL   DNIL 05 IM
		# 12 -09.432703  147.216444   11 11010  18       0.000 IWG  AYPY 14L DME-ILS
		# 12 -09.449222  147.226589   11 10950  18       0.000 IBB  AYPY 32R DME-ILS
		$nc = scalar @arr;
		$typ = $arr[0];
        # Check for type number in @navset, and set $actnav to name, like VOR, NDB, etc
		if ( is_valid_nav($typ) ) {
			$vcnt++;
			$nlat = $arr[1];
			$nlon = $arr[2];
			$nalt = $arr[3];
			$nfrq = $arr[4];
			$nrng = $arr[5];
			$nfrq2 = $arr[6];
			$nid = $arr[7];
			$name = '';
			for (my $i = 8; $i < $nc; $i++) {
				$name .= ' ' if length($name);
				$name .= $arr[$i];
			}
			push(@navlist, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name]);
            # Using $nmaxlatd, $nmaxlond, check airports in @aptlist2;
			$off = near_an_airport( $nlat, $nlon, \$dist, \$az );
            $off = near_given_point( $nlat, $nlon, \$dist, \$az ) if (!$off);
			if ($off) {
				prt( "[02] $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n") if ($dbg_fa02);
                #                0     1      2      3      4      5      6       7     8      9     10     11
				push(@navlist2, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
			}
        #} elsif ($line =~ /^\d+\s+Version\s+-\s+DAFIF\s+/) {
        #    my $ind = index($line,',');
        #    prt( "NAVAID: ".substr($line, 0, (($ind > 0) ? $ind : 50) )."\n" );   # 810 Version - DAFIF ...
        } elsif (($line eq 'I')||($line eq '99')) {
            # ignore these
		} elsif (length($line)) {
            #$typ = $line;
            $typ = length($line) > 80 ? substr($line,0,80) : $line;
            prtw("WARNING: What is this line? [$typ]???\n".
                "from $navdat file ...\n");
        }
	}
    $navs_found = scalar @navlist2;
    if (($navs_found == 0) && $tryharder) {
        my $def_latd = $nmaxlatd;
        my $def_lond = $nmaxlond;
        my $def_dist = $max_range_km;
        while ($navs_found == 0) {
            $nmaxlatd += 0.1;
            $nmaxlond += 0.1;
            $max_range_km += 0.1;
            if ($usekmrange) {
                prt("Expanded to [$max_range_km] Km from $ac airport(s)...\n" ) if (VERB1());
            } else {
                prt("Expanded to [$nmaxlatd,$nmaxlond] from $ac airport(s)...\n" ) if (VERB1());
            }
            foreach $line (@nav_lines) {
                $line = trimall($line);
                ###prt("$line\n");
                @arr = split(/ /,$line);
                $nc = scalar @arr;
                $typ = $arr[0];
                # Check for type number in @navset, and set $actnav to name, like VOR, NDB, etc
                if ( is_valid_nav($typ) ) {
                    $nlat = $arr[1];
                    $nlon = $arr[2];
                    $nalt = $arr[3];
                    $nfrq = $arr[4];
                    $nrng = $arr[5];
                    $nfrq2 = $arr[6];
                    $nid = $arr[7];
                    $name = '';
                    for (my $i = 8; $i < $nc; $i++) {
                        $name .= ' ' if length($name);
                        $name .= $arr[$i];
                    }
                    # Using $nmaxlatd, $nmaxlond, check airports in @aptlist2;
                    $off = near_an_airport( $nlat, $nlon, \$dist, \$az );
                    if ($off) {
                        prt( "[02] $actnav, $typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name ($off)\n") if ($dbg_fa02);
                        push(@navlist2, [$typ, $nlat, $nlon, $nalt, $nfrq, $nrng, $nfrq2, $nid, $name, $off, $dist, $az]);
                    }
                }
            }
            $navs_found = scalar @navlist2;
        }
        $msg = "Found $navs_found nearby NAVAIDS, ";
        if ($usekmrange) {
            $msg .= "using distance $max_range_km Km...";
        } else {
            $msg .= "using difference $nmaxlatd, $nmaxlond...";
        }
        prt("$msg\n") if (VERB1());
        $nmaxlatd = $def_latd;
        $nmaxlond = $def_lond;
        $max_range_km = $def_dist;
    }
	prt("[v5] Done - Found $navs_found nearby NAVAIDS, of $vcnt searched...\n" ) if (VERB5());
}

# put least first
sub mycmp_ascend_n4 {
   if (${$a}[4] < ${$b}[4]) {
      return -1;
   }
   if (${$a}[4] > ${$b}[4]) {
      return 1;
   }
   return 0;
}

# put least first
sub mycmp_ascend {
   if (${$a}[0] < ${$b}[0]) {
      prt( "-[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
      return -1;
   }
   if (${$a}[0] > ${$b}[0]) {
      prt( "+[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
      return 1;
   }
   prt( "=[".${$a}[0]."] == [".${$b}[0]."]\n" ) if $verb3;
   return 0;
}

sub mycmp_decend {
   if (${$a}[0] < ${$b}[0]) {
      prt( "+[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
      return 1;
   }
   if (${$a}[0] > ${$b}[0]) {
      prt( "-[".${$a}[0]."] < [".${$b}[0]."]\n" ) if $verb3;
      return -1;
   }
   prt( "=[".${$a}[0]."] == [".${$b}[0]."]\n" ) if $verb3;
   return 0;
}

# 0=typ, 1=lat, 2=lon, 3=alt, 4=frq, 5-rng, 6-frq2, 7=nid, 8=name, 9=off, 10=dist, 11=az);
sub mycmp_decend_dist {
   return -1 if (${$a}[10] < ${$b}[10]);
   return 1 if (${$a}[10] > ${$b}[10]);
   return 0;
}
sub mycmp_decend_az {
   return -1 if (${$a}[11] < ${$b}[11]);
   return 1 if (${$a}[11] > ${$b}[11]);
   return 0;
}

# $dist = $aptlist2[$i][12];
#                 0      1      2      3      4      5   6  7  8  9      10     11    12     13   14        15
#push(@aptlist2, [$diff, $icao, $name, $alat, $alon, -1, 0, 0, 0, $icao, $name, $off, $dist, $az, \@runways,$aalt]);
sub mycmp_decend_ap_dist {
   return -1 if (${$a}[12] < ${$b}[12]);
   return 1 if (${$a}[12] > ${$b}[12]);
   return 0;
}


##############
### functions
sub trimall {	# version 20061127
	my ($ln) = shift;
	chomp $ln;			# remove CR (\n)
	$ln =~ s/\r$//;		# remove LF (\r)
	$ln =~ s/\t/ /g;	# TAB(s) to a SPACE
	while ($ln =~ /\s\s/) {
		$ln =~ s/\s\s/ /g;	# all double space to SINGLE
	}
	while ($ln =~ /^\s/) {
		$ln = substr($ln,1); # remove all LEADING space
	}
	while ($ln =~ /\s$/) {
		$ln = substr($ln,0, length($ln) - 1); # remove all TRAILING space
	}
	return $ln;
}

# 12/12/2008 - Additional distance calculations
# from 'signs' perl script
# Melchior FRANZ <mfranz # aon : at>
# $Id: signs,v 1.37 2005/06/01 15:53:00 m Exp $

# sub ll2xyz($$) {
sub ll2xyz {
	my $lon = (shift) * $D2R;
	my $lat = (shift) * $D2R;
	my $cosphi = cos $lat;
	my $di = $cosphi * cos $lon;
	my $dj = $cosphi * sin $lon;
	my $dk = sin $lat;
	return ($di, $dj, $dk);
}


# sub xyz2ll($$$) {
sub xyz2ll {
	my ($di, $dj, $dk) = @_;
	my $aux = $di * $di + $dj * $dj;
	my $lat = atan2($dk, sqrt $aux) * $R2D;
	my $lon = atan2($dj, $di) * $R2D;
	return ($lon, $lat);
}

# sub coord_dist_sq($$$$$$) {
sub coord_dist_sq {
	my ($xa, $ya, $za, $xb, $yb, $zb) = @_;
	my $x = $xb - $xa;
	my $y = $yb - $ya;
	my $z = $zb - $za;
	return $x * $x + $y * $y + $z * $z;
}

sub get_fix_sample() {
    my $stg = <<EOF;
I
600 Version - data cycle 2009.12, build 20091080, metadata FixXP700.  Copyright © 2009, Robin A. Peel (robin\@xsquawkbox.net).
 52.013889 -000.052778 ASKEY
 50.052778  008.533611 ASKIK
 54.503333  031.086667 ASKIL
99
EOF
    return $stg;
}

sub load_fix_hash($) {
    my ($rfa) = @_;
    my $max = scalar @{$rfa};
    my ($line,$len,@arr,$cnt,$typ,$flat,$flon,$fname,$name,$key);
    my %h;
    foreach $line (@{$rfa}) {
        chomp $line;
        $line = trim_all($line);
        $len = length($line);
        next if ($len == 0);
        next if ($line =~ /^I/);
        @arr = split(/\s+/,$line);
        $cnt = scalar @arr;
        $typ = $arr[0];
        next if ($typ == 600);
        last if ($typ == 99);
        if ($cnt >= 3) {
            $flat = $arr[0];
            $flon = $arr[1];
            $name = trim_all($arr[2]);
            $h{$name} = [ $flat, $flon ];
        }
    }
    return \%h;
}


sub search_fix_file($) {
    my ($name) = @_;
    my ($flat,$flon,$rll,$key);
    my $rfa = load_fix_file();
    # my $raa = load_awy_file();
    my $tfh = load_fix_hash($rfa);
    my $cnt = scalar keys %{$tfh};
    prt("[v2] Searching $cnt fix records for [$name]\n") if (VERB2());
    $cnt = 0;
    foreach $key (keys %{$tfh}) {
        if ($key =~ /$name/) {
            $rll = ${$tfh}{$key};
            $flat = ${$rll}[0];
            $flon = ${$rll}[1];
            $flat  = ' '.$flat while (length($flat) < $g_maxnlatl);
            $flon  = ' '.$flon while (length($flon) < $g_maxnlonl);
            prt("FIX: $key $flat $flon\n");
            $cnt++;
        }
    }
    return $cnt;
}

sub get_awy_sample() {
    my $stg = <<EOF;
I
640 Version - data cycle 2009.12, build 20091080, metadata AwyXP700.  Copyright © 2009, Robin A. Peel (robin\@xsquawkbox.net). 
ASKER  38.273889  038.774722 ERH    38.463333  038.112222 1 115 285 W73
ASKER  38.273889  038.774722 GAZ    36.950278  037.473333 1 255 285 W701
ASKIK  50.052778  008.533611 DONIS  49.930556  008.859444 1 120 240 Z74
ASKIK  50.052778  008.533611 FFM    50.053742  008.637092 1 050 240 L984
ASKIK  50.052778  008.533611 MODAU  49.815000  008.811944 1 050 240 T840
ASKIK  50.052778  008.533611 RUDUS  50.047500  008.078333 1 050 240 L984
ASKIK  50.052778  008.533611 TABUM  50.290833  008.405000 1 050 240 T840
ASKIL  54.503333  031.086667 KOSAN  54.775000  029.278333 1 100 190 L736
99
EOF
    return $stg;
}

sub show_awy_item($$$$$$$$$$) {
    my ($name,$from,$flat,$flon,$to,$tlat,$tlon,$cat,$bfl,$efl) = @_;
    my ($sg_az1,$sg_az2,$sg_dist);
    my $ret = fg_geo_inverse_wgs_84($flat, $flon, $tlat, $tlon, \$sg_az1, \$sg_az2, \$sg_dist);
    my $sg_km = $sg_dist / 1000;
    my $sg_im = int($sg_dist);
    my $sg_ikm = int($sg_km + 0.5);
    my $sg_idegs = int($sg_az1 + 0.5);
    $sg_idegs = " $sg_idegs" while (length($sg_idegs) < 3);
    $sg_ikm = " $sg_ikm" while (length($sg_ikm) < 3);

    # begin bulding a display line
    my $line = "$name ";
    my $len = 6+1;
    my $max = 0;
    $line .= ' ' while (length($line) < $len);

    $len += 6+1;
    $line .= "$from ";
    $line .= ' ' while (length($line) < $len);

    $max = $g_maxnlatl - (length($line) - $len);
    $flat = ' '.$flat while (length($flat) < $max);
    $len += $g_maxnlatl+1;
    $line .= "$flat ";
    $line .= ' ' while (length($line) < $len);

    $max = $g_maxnlonl - (length($line) - $len);
    $flon = ' '.$flon while (length($flon) < $max);
    $len += $g_maxnlatl+1;
    $line .= "$flon ";
    $line .= ' ' while (length($line) < $len);

    $line .= "$to ";
    $len += 6 + 1;
    $line .= ' ' while (length($line) < $len);
    $max = $g_maxnlatl - (length($line) - $len);
    $tlat = ' '.$tlat while (length($tlat) < $max);
    $len += $g_maxnlatl+1;
    $line .= "$tlat ";
    $line .= ' ' while (length($line) < $len);

    $max = $g_maxnlonl - (length($line) - $len);
    $tlon = ' '.$tlon while (length($tlon) < $max);
    $len += $g_maxnlatl+1;
    $line .= "$tlon ";
    $line .= ' ' while (length($line) < $len);

    prt("AWY: $line (on $sg_idegs for $sg_ikm km).\n");
}

# from airways.cxx
# in >> identStart;
# if (identStart == "99") { break; }
# in >> latStart >> lonStart >> identEnd >> latEnd >> lonEnd >> type >> base >> top >> name;
# // type = 1; low-altitude
# // type = 2; high-altitude
# Network* net = (type == 1) ? static_lowLevel : static_highLevel;
sub load_awy_hash($) {
    my ($raa) = @_;
    my $max = scalar @{$raa};
    my ($line,$len,@arr,$cnt,$typ,$flat,$flon,$fname,$name,$key);
    my ($tlat,$tlon,$from,$to,$hadver);
    my ($cat,$bfl,$efl,$ra,$lnn);
    my %ids = ();
    $lnn = 0;
    $hadver = 0;
    foreach $line (@{$raa}) {
        $lnn++;
        chomp $line;
        $line = trim_all($line);
        $len = length($line);
        next if ($len == 0);
        next if (($line =~ /^I/)&&($hadver == 0));
        if ($line =~ /\s+Version\s+/) {
            $hadver = 1;
            #next if ($typ == 640);
            next;
        }
        @arr = split(/\s+/,$line);
        $cnt = scalar @arr;
        $typ = $arr[0];
        last if ($typ =~ /^99/);
        if ($cnt >= 10) {
            # 0      1          2          3      4          5          6 7   8   9
            # ASKIK  50.052778  008.533611 RUDUS  50.047500  008.078333 1 050 240 L984
            $from = $arr[0];
            $flat = $arr[1];
            $flon = $arr[2];
            $to   = $arr[3];
            $tlat = $arr[4];
            $tlon = $arr[5];
            # 1 115 285 W73
            $cat  = $arr[6]; # category 1 == low altitude, 2 == high altitude
            $bfl  = $arr[7]; # begin flight level
            $efl  = $arr[8]; # end flight level
            $name = trim_all($arr[9]);
            $ids{$name} = [ ] if (!defined $ids{$name});
            $ra = $ids{$name};
            push(@{$ra}, [ $from, $flat, $flon, $to, $tlat, $tlon, $cat, $bfl, $efl ]);
        }
    }
    return \%ids;
}


# from airways.cxx
# in >> identStart;
# if (identStart == "99") { break; }
# in >> latStart >> lonStart >> identEnd >> latEnd >> lonEnd >> type >> base >> top >> name;
# // type = 1; low-altitude
# // type = 2; high-altitude
# Network* net = (type == 1) ? static_lowLevel : static_highLevel;
sub search_awy_file($) {
    my ($name) = @_;
    my ($from,$flat,$flon,$rll,$key);
    my $raa = load_awy_file();
    # my $raa = load_awy_file();
    my $tah = load_awy_hash($raa);
    my $cnt = scalar keys %{$tah};
    my ($acnt,$to,$tlat,$tlon,$cat,$bfl,$efl,$id,$i);
    prt("[v2] Searching $cnt airway records for [$name]\n") if (VERB2());
    $cnt = 0;
    foreach $key (keys %{$tah}) {
        if ($key =~ /$name/) {
            $rll = ${$tah}{$key};
            $acnt = scalar @{$rll};
            for ($i = 0; $i < $acnt; $i++) {
                ##               0      1      2      3    4      5      6     7     8
                #$ids{$name} = [ $from, $flat, $flon, $to, $tlat, $tlon, $cat, $bfl, $efl ];
                $from = ${$rll}[$i][0];
                $flat = ${$rll}[$i][1];
                $flon = ${$rll}[$i][2];
                $to   = ${$rll}[$i][3];
                $tlat = ${$rll}[$i][4];
                $tlon = ${$rll}[$i][5];
                # 1 115 285 W73 
                $cat  = ${$rll}[$i][6]; # category 1 = low, 2 = high
                $bfl  = ${$rll}[$i][7]; # begin flight level
                $efl  = ${$rll}[$i][8]; # end flight level
                show_awy_item($key,$from,$flat,$flon,$to,$tlat,$tlon,$cat,$bfl,$efl);
                $cnt++;
            }
        }
    }
    return $cnt;
}

sub show_awy_array($$) {
    my ($name,$ra) = @_;
    #my $tmp = ref($ra);
    #prt("Got ref [$tmp]\n");
    ##               0      1      2      3    4      5      6     7     8
    #$ids{$name} = [ $from, $flat, $flon, $to, $tlat, $tlon, $cat, $bfl, $efl ];
    my $from = ${$ra}[0];
    my $flat = ${$ra}[1];
    my $flon = ${$ra}[2];
    my $to   = ${$ra}[3];
    my $tlat = ${$ra}[4];
    my $tlon = ${$ra}[5];
    # 1 115 285 W73 
    my $cat  = ${$ra}[6]; # category 1 = low, 2 = high
    my $bfl  = ${$ra}[7]; # begin flight level
    my $efl  = ${$ra}[8]; # end flight level
    show_awy_item($name,$from,$flat,$flon,$to,$tlat,$tlon,$cat,$bfl,$efl);
}

sub list_awy_file() {
    my $raa = load_awy_file();
    my $tah = load_awy_hash($raa);
    my $cnt = scalar keys %{$tah};
    prt("Listing $cnt airway records..\n"); # if (VERB2());
    my ($name,$val,$acnt,$i,$ra,$tmp);
    foreach $name (sort keys %{$tah}) {
        $val = ${$tah}{$name};
        $acnt = scalar @{$val};
        for ($i = 0; $i < $acnt; $i++) {
            $ra = ${$val}[$i];
            $tmp = ref($ra);
            # prt("Extracted a [$tmp]\n");
            show_awy_array($name,$ra);
        }
    }
}

sub load_awy_hash2($) {
    my ($raa) = @_;
    my $max = scalar @{$raa};
    my ($line,$len,@arr,$cnt,$typ,$flat,$flon,$fname,$name,$key);
    my ($tlat,$tlon,$from,$to,$hadver);
    my ($cat,$bfl,$efl,$ra,$lnn);
    my %h = ();
    $lnn = 0;
    $hadver = 0;
    foreach $line (@{$raa}) {
        $lnn++;
        chomp $line;
        $line = trim_all($line);
        $len = length($line);
        next if ($len == 0);
        next if (($line =~ /^I/)&&($hadver == 0));
        if ($line =~ /\s+Version\s+/) {
            $hadver = 1;
            #next if ($typ == 640);
            next;
        }
        @arr = split(/\s+/,$line);
        $cnt = scalar @arr;
        $typ = $arr[0];
        last if ($typ =~ /^99/);
        if ($cnt >= 10) {
            # 0      1          2          3      4          5          6 7   8   9
            # ASKIK  50.052778  008.533611 RUDUS  50.047500  008.078333 1 050 240 L984
            $from = $arr[0];
            $flat = $arr[1];
            $flon = $arr[2];
            $to   = $arr[3];
            $tlat = $arr[4];
            $tlon = $arr[5];
            # 1 115 285 W73
            $cat  = $arr[6]; # category 1 == low altitude, 2 == high altitude
            $bfl  = $arr[7]; # begin flight level
            $efl  = $arr[8]; # end flight level
            $name = trim_all($arr[9]);
            $h{$from} = [ ] if (!defined $h{$from});
            $ra = $h{$from};
            #              0      1      2    3      4      5     6     7     8
            push(@{$ra}, [ $flat, $flon, $to, $tlat, $tlon, $cat, $bfl, $efl, $name ]);
            $h{$to} = [ ] if (!defined $h{$to});
            $ra = $h{$to};
            push(@{$ra}, [ $tlat, $tlon, $from, $flat, $flon, $cat, $bfl, $efl, $name ]);
        }
    }
    return \%h;
}

sub search_awy_file_prev($) {
    my ($name) = @_;
    my ($flat,$flon,$rll,$key);
    my $raa = load_awy_file();
    # my $raa = load_awy_file();
    my $tah = load_awy_hash2($raa);
    my $cnt = scalar keys %{$tah};
    my ($acnt,$to,$tlat,$tlon,$cat,$bfl,$efl,$id,$i);
    my ($ret,$sg_az1,$sg_az2,$sg_dist);
    prt("[v2] Searching $cnt airway records for [$name]\n") if (VERB2());
    $cnt = 0;
    foreach $key (keys %{$tah}) {
        if ($key =~ /$name/) {
            $rll = ${$tah}{$key};
            $acnt = scalar @{$rll};
            for ($i = 0; $i < $acnt; $i++) {
                $flat = ${$rll}[$i][0];
                $flon = ${$rll}[$i][1];
                $to   = ${$rll}[$i][2];
                $tlat = ${$rll}[$i][3];
                $tlon = ${$rll}[$i][4];
                # 1 115 285 W73 
                $cat  = ${$rll}[$i][5]; # category 1 = low, 2 = high
                $bfl  = ${$rll}[$i][6]; # begin flight level
                $efl  = ${$rll}[$i][7]; # end flight level
                $id   = ${$rll}[$i][8]; # airway NAME
                $ret = fg_geo_inverse_wgs_84($flat, $flon, $tlat, $tlon, \$sg_az1, \$sg_az2, \$sg_dist);
                my $sg_km = $sg_dist / 1000;
                my $sg_im = int($sg_dist);
                my $sg_ikm = int($sg_km + 0.5);
                my $sg_idegs = int($sg_az1 + 0.5);
                $sg_idegs = " $sg_idegs" while (length($sg_idegs) < 3);
                $sg_ikm = " $sg_ikm" while (length($sg_ikm) < 3);
                $flat = ' '.$flat while (length($flat) < $g_maxnlatl);
                $flon = ' '.$flon while (length($flon) < $g_maxnlonl);
                $to .= ' ' while (length($to) < 6);
                $tlat = ' '.$tlat while (length($tlat) < $g_maxnlatl);
                $tlon = ' '.$tlon while (length($tlon) < $g_maxnlonl);
                prt("AWY: $key $flat $flon $to $tlat $tlon on $sg_az1 ($sg_idegs at $sg_ikm km).\n");
                $cnt++;
            }
        }
    }
    return $cnt;
}


sub do_looks_like_fix() {
    # looks like a FIX...
    my $cnt = 0;
    prt("ICAO has len greater than 4, so assume a FIX... [$apticao]\n");
    $g_fix_name = $apticao;
    if (search_fix_file($g_fix_name)) {
        prt("Found the above fix...\n");
        $cnt++;
    }
    if (search_awy_file($g_fix_name)) {
        prt("Found the above airways...\n");
        $cnt++;
    }
    if ($cnt) {
        prt("Hope your search for $apticao is one of these ;=)) Or try another name...\n");
    } else {
        prt("ICAO $apticao NOT found ;=(( Try another name...\n");
    }
    pgm_exit(0,"");
}

#======================================================
### MAIN ###
# ==========

#search_fix_file($g_fix_name);
#pgm_exit(1,"");
#list_awy_file();
#$loadlog = 1;
#pgm_exit(1,"");

parse_args(@ARGV);	# collect command line arguments ...

prt( "$pgmname ... Hello, World ... ".scalar localtime(time())."\n" ) if (VERB9());

if ($SRCHICAO && (length($apticao) > 4)) {
    do_looks_like_fix();
}

load_apt_data();

set_average_apt_latlon();

#my @aptsort = sort mycmp_ascend @aptlist;
if ( show_airports_found($max_cnt) || $SRCHONLL ) {
    if ($SHOWNAVS) {
        search_nav();
        show_navaids_found();
        show_airports_found($max_cnt) if (VERB9());
    }
    show_scenery_tiles();
} elsif ( $SHOWNAVS ) {
    prt("No airport found, so no show of nav's around it...\n");
    if ($SRCHICAO) {
        search_nav_name();
    }
}


my $elapsed = tv_interval ( $t0, [gettimeofday]);
prt( "Ran for $elapsed seconds ...\n" ) if (VERB5());
pgm_exit(0,"");

#######################################################
### HELP AND COMMAND LINE

sub give_help {
	prt( "FLIGHTGEAR AIRPORT SEARCH UTILITY - $VERS\n" );
	prt( "Usage: $pgmname options\n" );
	prt( "Options: A ? anywhere for this help.\n" );
    prt( " --file <file>   (-f) = Load commands from this 'file' of commands...\n");
	prt( " -icao=$apticao           - Search using icao.\n" );
	prt( " -latlon=$g_center_lat,$g_center_lon  - Search using latitude, longitude.\n" );
	prt( " -maxout=$max_cnt            - Limit the airport output. A 0 for ALL.\n" );
	prt( " -maxll=$maxlatd,$maxlond       - Maximum difference, when searching ariports using lat,lon.\n" );
	prt( " -name=\"$aptname\"   - Search using airport name. (A -name=. would match all.)\n" );
	prt( " -shownavs (or -s)    - Show NAVAIDS around airport found, if any. " );
    prt( "(Def=". ($SHOWNAVS ? "On" : "Off") . ")\n" );
	prt( " -nmaxll=$nmaxlatd,$nmaxlond      - Maximum difference, when searching NAVAID lat,lon.\n" );
	prt( " -aptdata=$aptdat  - Use a specific AIRPORT data file.\n" );
	prt( " -navdata=$navdat  - Use a specific NAVAID data file.\n" );
    prt( " -range=$max_range_km             - Set Km range when checking for NAVAIDS.\n" );
    prt( " -r                   - Use above range ($max_range_km Km) for searching.\n" );
    prt( " -tryhard (or -t)     - Expand search if no NAVAIDS found in range. " );
    prt( "(Def=". ($tryharder ? "On" : "Off") . ")\n" );
    prt( " --verbosity (-v[nn]) - Increase or set verbosity.\n");
    prt( " --VOR           (-V) - List only VOR (+NDB)\n");
    prt( " --loadlog       (-l) - Load log at end of display.\n");
	mydie( "                                     Happy Searching.\n" );
}

# Ensure argument exists, or die.
sub require_arg {
    my ($arg, @arglist) = @_;
    mydie( "ERROR: no argument given for option '$arg' ...\n" ) if ! @arglist;
}

sub local_strip_both_quotes($) {
    my $txt = shift;
    if ($txt =~ /^'(.+)'$/) {
        return $1;
    }
    if ($txt =~ /^"(.+)"$/) {
        return $1;
    }
    return '' if ($txt eq '""');
    return '' if ($txt eq "''");
    #prt("Stripping [$txt] FAILED\n");
    return $txt;
}


sub load_input_file($$) {
    my ($arg,$file) = @_;
    if (open INF, "<$file") {
        my @lines = <INF>;
        close INF;
        my @carr = ();
        my ($line,@arr,$tmp,$i);
        my $lncnt = scalar @lines;
        for ($i = 0; $i < $lncnt; $i++) {
            $line = $lines[$i];
            $line = trim_all($line);
            next if (length($line) == 0);
            next if ($line =~ /^\#/);
            # load CONTINUATION lines - ends in '\' back-slash
            while (($line =~ /\\$/)&&(($i+1) < $lncnt)) {
                $i++;
                $line =~ s/\\$//;
                $line .= trim_all($lines[$i]);
            }
            @arr = split(/\s/,$line);
            foreach $tmp (@arr) {
                $tmp = local_strip_both_quotes($tmp);
                push(@carr,$tmp);
            }
        }
        $in_input_file++;
        parse_args(@carr);
        $in_input_file--;
    } else {
        pgm_exit(1,"ERROR: Unable to 'open' file [$file]!\n")
    }
}

sub deal_with_verbosity($) {
    my ($rav) = @_;
    my ($arg,$sarg,$i,$cnt);
    $cnt = scalar @{$rav};
    #prt("Doing verbosity check of $cnt args...\n");
    for ($i = 0; $i < $cnt; $i++) {
        $arg = ${$rav}[$i];
        #prt("Checking [$arg]...\n");
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if ($sarg =~ /^v/) {
                #prt("Got -v... [$arg]\n");
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/i) {
                        $verbosity++;
                        $sarg = substr($sarg,1)
                    }
                }
                prt( "[v1] Set verbosity to $verbosity\n") if (VERB1());
            }

        }
    }
}

# set $SRCHICAO on/off
# set $SRCHONLL on/off
sub parse_args {
	my (@av) = @_;
	my (@arr,$arg,$sarg,$lcarg,$ch);
    $arg = scalar @av;
    #prt("Deal with $arg command arguments...\n");
    deal_with_verbosity(\@av);
	while(@av) {
		$arg = $av[0]; # shift @av;
        $lcarg = lc($arg);
        $ch = substr($arg,0,1);
        $sarg = $arg;
        $sarg = substr($sarg,1) while ($sarg =~ /^-/);
		if ($arg =~ /\?/) {
			give_help();
        } elsif ($ch eq '-') {
            if ($sarg =~ /^v/) {
                # done verbosity
            } elsif ($sarg =~ /^f/) {
                require_arg(@av);
                shift @av;
                $sarg = $av[0];
                load_input_file($arg,$sarg);
            } elsif ($sarg =~ /^V/) {
                $vor_only = 1;
                prt( "[v1] Set VOR (NBD) ONLY flag\n") if (VERB1());
            } elsif (( $sarg =~ /^loadlog$/ )||($sarg =~ /^l$/)) {
                prt("[v1] Set load log at end of display.\n") if (VERB1());
                $loadlog = 1;
            } elsif ( $arg =~ /-icao=(.+)/i ) {
                # BY ICAO
                $apticao = $1;
                $SRCHICAO = 1;
                $SRCHONLL = 0;
                $SRCHNAME = 0;
                prt( "[v1] Set search using ICAO of [$apticao] ...\n" ) if (VERB1());
            } elsif ( $lcarg eq '-icao' ) {
                require_arg(@av);
                shift @av;
                $SRCHICAO = 1;
                $SRCHONLL = 0;
                $SRCHNAME = 0;
                $apticao = $av[0];
                prt( "[v1] Set search using ICAO of [$apticao] ...\n" ) if (VERB1());

            # BY LAT,LON
            } elsif ( $arg =~ /-latlon=(.+)/i ) {
                $SRCHICAO = 0;
                $SRCHONLL = 1;
                $SRCHNAME = 0;
                @arr = split(',', $1);
                if (scalar @arr == 2) {
                    $g_center_lat = $arr[0];
                    $g_center_lon = $arr[1];
                    prt( "[v1] Set search using LAT,LON of [".ctr_latlon_stg()."] ...\n" ) if (VERB1());
                } else {
                    # 17/08/2010 - assume lat and lon also split, and try harder
                    $g_center_lat = $arr[0];
                    require_arg(@av);
                    shift @av;
                    $g_center_lon = $av[0];
                    if (($g_center_lat =~ /^(\d|-|\+|\.)+$/) && ($g_center_lon =~ /^(\d|-|\+|\.)+$/)) {
                        prt( "[v1] Set search using LAT,LON of [".ctr_latlon_stg()."] ...\n" ) if (VERB1());
                    } else {
                        mydie( "ERROR: Failed to find lat,lon in [$arg] [".ctr_latlon_stg()."]...\n" );
                    }
                }
            } elsif ( $lcarg eq '-latlon' ) {
                # set a center LAT,LON
                require_arg(@av);
                shift @av;
                $SRCHICAO = 0;
                $SRCHONLL = 1; # search using lat,lon input...
                $SRCHNAME = 0;
                @arr = split(',', $av[0]);
                if (scalar @arr == 2) {
                    $g_center_lat = $arr[0];
                    $g_center_lon = $arr[1];
                    prt( "[v1] Set search using LAT,LON of [".ctr_latlon_stg()."] ...\n" ) if (VERB1());
                } else {
                    # 17/08/2010 - assume lat and lon also split, and try harder
                    $g_center_lat = $arr[0];
                    require_arg(@av);
                    shift @av;
                    $g_center_lon = $av[0];
                    if (($g_center_lat =~ /^(\d|-|\+|\.)+$/) && ($g_center_lon =~ /^(\d|-|\+|\.)+$/)) {
                        prt( "[v1] Set search using LAT,LON of [".ctr_latlon_stg()."] ...\n" ) if (VERB1());
                    } else {
                        mydie( "ERROR: Failed to find lat,lon in [$arg] [".ctr_latlon_stg()."]...\n" );
                    }
                }
                $got_center_latlon = 1; # given *** CENTER OF WORLD *** position -latlon lat lon
            # By NAME
            } elsif ( $arg =~ /-name=(.+)/i ) {
                $aptname = $1;
                $SRCHICAO = 0;
                $SRCHONLL = 0;
                $SRCHNAME = 1;
                prt( "[v1] Set search using NAME of [$aptname] ...\n" ) if (VERB1());
            } elsif ( $lcarg eq '-name' ) {
                require_arg(@av);
                shift @av;
                $SRCHICAO = 0;
                $SRCHONLL = 0;
                $SRCHNAME = 1;
                $aptname = $av[0];
                prt( "[v1] Set search using NAME of [$aptname] ...\n" ) if (VERB1());
            } elsif ( $arg =~ /^-loadlog$/i ) {
                $loadlog = 1;
                prt( "[v1] Set load log into wordpad.\n" ) if (VERB1());
            } elsif ( $arg =~ /^-shownavs$/i ) {
                $SHOWNAVS = 1;
                prt( "[v1] Set show NAVAIDS around airport, if any.\n" ) if (VERB1());
            } elsif ( $arg =~ /^-s$/i ) {
                $SHOWNAVS = 1;
                prt( "[v1] Set show NAVAIDS around airport, if any.\n" ) if (VERB1());
            } elsif ( $arg =~ /-maxll=(.+)/i ) {
                @arr = split(',', $1);
                if (scalar @arr == 2) {
                    $maxlatd = $arr[0];
                    $maxlond = $arr[1];
                    prt( "Search maximum difference LAT,LON of [$maxlatd,$maxlond] ...\n" ) if (VERB1());
                } else {
                    # 17/08/2010 - assume lat and lon also split, and try harder
                    $maxlatd = $arr[0];
                    require_arg(@av);
                    shift @av;
                    $maxlond = $av[0];
                    if (($maxlatd =~ /^(\d|-|\+|\.)+$/) && ($maxlond =~ /^(\d|-|\+|\.)+$/)) {
                        prt( "Search maximum difference LAT,LON of [$maxlatd,$maxlond] ...\n" ) if (VERB1());
                    } else {
                        mydie( "ERROR: Failed to find maximum lat,lon difference in [$arg] [$maxlatd,$maxlond]...\n" );
                    }
                }
            } elsif ( $lcarg eq '-maxll' ) {
                require_arg(@av);
                shift @av;
                @arr = split(',', $av[0]);
                if (scalar @arr == 2) {
                    $maxlatd = $arr[0];
                    $maxlond = $arr[1];
                    prt( "[v1] Set search maximum difference LAT,LON of [$maxlatd,$maxlond] ...\n" ) if (VERB1());
                } else {
                    # 17/08/2010 - assume lat and lon also split, and try harder
                    $maxlatd = $arr[0];
                    require_arg(@av);
                    shift @av;
                    $maxlond = $av[0];
                    if (($maxlatd =~ /^(\d|-|\+|\.)+$/) && ($maxlond =~ /^(\d|-|\+|\.)+$/)) {
                        prt( "[v1] Set search maximum difference LAT,LON of [$maxlatd,$maxlond] ...\n" ) if (VERB1());
                    } else {
                        mydie( "ERROR: Failed to find maximum lat,lon difference in [$arg] [$maxlatd,$maxlond]...\n" );
                    }
                }
            } elsif ( $arg =~ /-nmaxll=(.+)/i ) {
                @arr = split(',', $1);
                if (scalar @arr == 2) {
                    $nmaxlatd = $arr[0];
                    $nmaxlond = $arr[1];
                    prt( "[v1] Set search maximum NAV difference LAT,LON of [$nmaxlatd,$nmaxlond] ...\n" ) if (VERB1());
                } else {
                    # 17/08/2010 - assume lat and lon also split, and try harder
                    $nmaxlatd = $arr[0];
                    require_arg(@av);
                    shift @av;
                    $nmaxlond = $av[0];
                    if (($nmaxlatd =~ /^(\d|-|\+|\.)+$/) && ($nmaxlond =~ /^(\d|-|\+|\.)+$/)) {
                        prt( "[v1] Set search maximum NAV difference LAT,LON of [$nmaxlatd,$nmaxlond] ...\n" ) if (VERB1());
                    } else {
                        mydie( "ERROR: Failed to find maximum lat,lon NAV difference in [$arg] [$nmaxlatd,$nmaxlond]...\n" );
                    }
                }
            } elsif ( $lcarg eq '-nmaxll' ) {
                require_arg(@av);
                shift @av;
                @arr = split(',', $av[0]);
                if (scalar @arr == 2) {
                    $nmaxlatd = $arr[0];
                    $nmaxlond = $arr[1];
                    prt( "[v1] Set search maximum NAV difference LAT,LON of [$nmaxlatd,$nmaxlond] ...\n" ) if (VERB1());
                } else {
                    # 17/08/2010 - assume lat and lon also split, and try harder
                    $nmaxlatd = $arr[0];
                    require_arg(@av);
                    shift @av;
                    $nmaxlond = $av[0];
                    if (($nmaxlatd =~ /^(\d|-|\+|\.)+$/) && ($nmaxlond =~ /^(\d|-|\+|\.)+$/)) {
                        prt( "[v1] Set search maximum NAV difference LAT,LON of [$nmaxlatd,$nmaxlond] ...\n" ) if (VERB1());
                    } else {
                        mydie( "ERROR: Failed to find maximum lat,lon NAV difference in [$arg] [$nmaxlatd,$nmaxlond]...\n" );
                    }
                }
            } elsif ( $arg =~ /-aptdata=(.+)/i ) {
                $aptdat = $1;	# the airports data file
                prt( "[v1] Set using AIRPORT data file [$aptdat] ...\n" ) if (VERB1());
            } elsif ( $lcarg eq '-aptdata' ) {
                require_arg(@av);
                shift @av;
                $aptdat = $av[0];	# the airports data file
                prt( "[v1] Set using AIRPORT data file [$aptdat] ...\n" ) if (VERB1());
            } elsif ( $arg =~ /-navdata=(.+)/i ) {
                $navdat = $1;
                prt( "[v1] Set using NAVAID data file [$navdat] ...\n" ) if (VERB1());
            } elsif ( $lcarg eq '-navdata' ) {
                require_arg(@av);
                shift @av;
                $navdat = $av[0];
                prt( "[v1] Set Using NAVAID data file [$navdat] ...\n" ) if (VERB1());
            } elsif ( $arg =~ /-maxout=(.+)/i ) {
                $max_cnt = $1;
                prt( "[v1] Set AIRPORT output limited to $max_cnt. A zero (0), for no limit\n" ) if (VERB1());
            } elsif ( $lcarg eq '-maxout' ) {
                require_arg(@av);
                shift @av;
                $max_cnt = $av[0];
                prt( "[v1] Set AIRPORT output limited to $max_cnt. A zero (0), for no limit\n" ) if (VERB1());
            } elsif ( $arg =~ /-range=(.+)/i ) {
                $max_range_km = $1;
                # prt( "Set navaid search range $max_range_km Km. A zero (0), for no limit\n" ) if (VERB1());
                $usekmrange = 1;
                prt( "[v1] Set NAVAID search range $max_range_km Km.\n" ) if (VERB1());
            } elsif ( $lcarg eq '-range' ) {
                require_arg(@av);
                shift @av;
                $max_range_km = $av[0];
                #prt( "Navaid search using $max_range_km Km. A zero (0), for no limit\n" ) if (VERB1());
                $usekmrange = 1;
                prt( "[v1] Set NAVAID search range $max_range_km Km.\n" ) if (VERB1());
            } elsif ( $lcarg eq '-r' ) {
                $usekmrange = 1;
                prt( "[v1] Navaid search using $max_range_km Km.\n" ) if (VERB1());
            } elsif (( $lcarg eq '-tryhard' )||( $lcarg eq '-t' )) {
                $tryharder = 1;  # Expand the search for NAVAID, until at least 1 found
                prt( "[v1] Set NAVAID search 'tryharder'...\n" ) if (VERB1());
            } else {
                mydie( "ERROR: Unknown argument [$arg]. Try ? for HELP ...\n" );
            }
        } else {
            # ASSUME AN AIRPORT NAME
            $SRCHICAO = 0;
            $SRCHONLL = 0;
            $SRCHNAME = 1;
            if ((length($arg) == 4)&&($arg =~ /^[A-Z0-9]+$/)) {
                # assume is an ICAO value
                # BY ICAO
                $apticao = $arg;
                $SRCHICAO = 1;
                $SRCHONLL = 0;
                $SRCHNAME = 0;
                prt( "[v1] Set search using ICAO of [$apticao] ...\n" ) if (VERB1());
            } else {
                $aptname = $arg;
                prt( "[v1] Search using NAME of [$aptname] ...\n" ) if (VERB1());
            }
        }
		shift @av;
	}

    if ( ! $in_input_file ) {
        # NOT in an INPUT file
        # *** ONLY FOR TESTING ***
        if ($test_name) {
            $SRCHICAO = 0;
            $SRCHONLL = 0;
            $SRCHNAME = 1;
            $SHOWNAVS = 1;
            $usekmrange = 1;
            $max_range_km = 5;
            $aptname = $def_name;
        } elsif ($test_ll) {
            $g_center_lat = $def_lat;
            $g_center_lon = $def_lon;
            $maxlatd = 0.1;
            $maxlond = 0.1;
            $SRCHICAO = 0;
            $SRCHONLL = 1;
            $SRCHNAME = 0;
        } elsif ($test_icao) {
            $SRCHICAO = 1;
            $SRCHONLL = 0;
            $SRCHNAME = 0;
            $SHOWNAVS = 1;
            $apticao = $def_icao;
            # now have $tryharder to expand this, if NO NAVAIDS found
            $tryharder = 1;
            $usekmrange = 1;
        }


        if ( ($SRCHICAO == 0) && ($SRCHONLL == 0) && ($SRCHNAME == 0) ) {
            prt( "ERROR: No valid command action found, like -\n" );
            prt( "By ICAO '-icao=KSFO', by LAT/LON '-latlon=21,-122', or '-name=something'!\n" );
            give_help();
        }
    }

}

# eof - findap03.pl
