#!/usr/bin/perl -w
# NAME: cntnavtypes.pl
# AIM: *** VERY SPECIFIC *** Count x-plane navaids in earth_nav_dat
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use Cwd;
my $os = $^O;
my $perl_dir = '/home/geoff/bin';
my $PATH_SEP = '/';
my $temp_dir = '/tmp';
if ($os =~ /win/i) {
    $perl_dir = 'C:\GTools\perl';
    $temp_dir = $perl_dir;
    $PATH_SEP = "\\";
}
unshift(@INC, $perl_dir);
require 'lib_utils.pl' or die "Unable to load 'lib_utils.pl' Check paths in \@INC...\n";
require 'fg_wsg84.pl' or die "Unable to load fg_wsg84.pl ...\n";
# log file stuff
our ($LF);
my $pgmname = $0;
if ($pgmname =~ /(\\|\/)/) {
    my @tmpsp = split(/(\\|\/)/,$pgmname);
    $pgmname = $tmpsp[-1];
}
my $outfile = $temp_dir.$PATH_SEP."temp.$pgmname.txt";
open_log($outfile);

# user variables
my $VERS = "0.0.2 2014-01-13";
my $load_log = 0;
my $in_file = 'D:\FG\xplane\1000\earth_nav.dat';
my $verbosity = 0;
my $out_file = $temp_dir.$PATH_SEP."earth_nav.csv";
my $out_file2 = $temp_dir.$PATH_SEP."clean-nav.csv";

# ### DEBUG ###
my $debug_on = 0;
my $def_file = 'def_file';

### program variables
my @warnings = ();
my $cwd = cwd();

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

sub show_warnings($) {
    my ($val) = @_;
    if (@warnings) {
        prt( "\nGot ".scalar @warnings." WARNINGS...\n" );
        foreach my $itm (@warnings) {
           prt("$itm\n");
        }
        prt("\n");
    } else {
        prt( "\nNo warnings issued.\n\n" ) if (VERB9());
    }
}

sub pgm_exit($$) {
    my ($val,$msg) = @_;
    if (length($msg)) {
        $msg .= "\n" if (!($msg =~ /\n$/));
        prt($msg);
    }
    show_warnings($val);
    close_log($outfile,$load_log);
    exit($val);
}


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

my %navtypes = (
    2 => 'NDB',
    3 => 'VOR',
    4 => 'ILS',
    5 => 'LOC',
    6 => 'GS',
	7 => 'OM',
    8 => 'MM',
    9 => 'IM',
	12 => 'V-DME',
    13 => 'O-DME'
    );

# sort by type
sub mycmp_ascend_n0 {
   return -1 if (${$a}[0] < ${$b}[0]);
   return  1 if (${$a}[0] > ${$b}[0]);
   return 0;
}


# sort by ID text
sub mycmp_ascend_t7 {
   return -1 if (${$a}[7] lt ${$b}[7]);
   return  1 if (${$a}[7] gt ${$b}[7]);
   return 0;
}

# sort by freq number
sub mycmp_ascend_n4 {
   return -1 if (${$a}[4] < ${$b}[4]);
   return  1 if (${$a}[4] > ${$b}[4]);
   return 0;
}

# 0  1           2                  3   4    5     6   7    8
# typ latitude   longitude      elev   freq  rng  brng id   name
# 2  38.08777778 -077.32491667      0   396  50    0.0 APH  A P HILL NDB
sub party_with_navaids($) {
    my $rn = shift;
    ###my @navs = sort mycmp_ascend_n4 @{$rn};
    my $max = scalar @{$rn};
    prt("Partying with $max navaids... outing where freq and id are equal...\n");
    my ($typ,$lat,$lon,$alt,$frq,$rng,$brg,$id,$name);
    my ($i,$ra,$j,$ra2,$fnd,$i2);
    my ($typ2,$lat2,$lon2,$alt2,$frq2,$rng2,$brg2,$id2,$name2);
    my ($ret,$az1,$az2,$dist,$km,$rak);
    my ($nlen,$nlen2);
    my %done = ();
    my @nnavs = ();
    my %had = ();
    for ($i = 0; $i < $max; $i++) {
        $ra = ${$rn}[$i];
        $typ = ${$ra}[0];
        $lat = ${$ra}[1];
        $lon = ${$ra}[2];
        $alt = ${$ra}[3];
        $frq = ${$ra}[4];
        $rng = ${$ra}[5];
        $brg = ${$ra}[6];
        $id  = ${$ra}[7];
        $name = ${$ra}[8];
        if ($id eq '----') {
            push(@nnavs,$ra); # push this ONE to keep
            next;
        }
        if ($frq == 0) {
            push(@nnavs,$ra); # push this ONE to keep
            next;
        }
        next if (defined $done{$i});
        if (($typ == 4)||($typ == 5)||($typ == 6)) {
            push(@nnavs,$ra); # push this ONE to keep
            next;
        }
        $done{$i} = 1;
        $fnd = 0;
        $rak = $ra;
        %had = ();
        $had{$typ} = 1; # had this type
        for ($j = $i + 1; $j < $max; $j++) {
            next if (defined $done{$j});
            $ra2 = ${$rn}[$j];
            $typ2 = ${$ra2}[0];
            $lat2 = ${$ra2}[1];
            $lon2 = ${$ra2}[2];
            $alt2 = ${$ra2}[3];
            $frq2 = ${$ra2}[4];
            $rng2 = ${$ra2}[5];
            $brg2 = ${$ra2}[6];
            $id2  = ${$ra2}[7];
            $name2 = ${$ra2}[8];
            next if ($id2 eq '----');
            next if ($frq2 == 0);
            next if (($typ2 == 4)||($typ2 == 5)||($typ2 == 6));
            if ( ($id eq $id2) && ($frq == $frq2) ) {
                $ret = fg_geo_inverse_wgs_84($lat, $lon, $lat2, $lon2, \$az1, \$az2, \$dist);
                $km = $dist / 1000;
                if ($km < 10) {
                    $km = (int(($km+0.005) * 100) / 100);
                    if ($fnd == 0) {
                        $nlen = length($name);
                        prt("\n1:$i: $typ,$lat,$lon,$alt,$frq,$rng,$brg,$id,$name,$km\n");
                    }
                    $fnd++;
                    $i2 = $fnd + 1;
                    $nlen2 = length($name2);
                    prt("$i2:$j: $typ2,$lat2,$lon2,$alt2,$frq2,$rng2,$brg2,$id2,$name2,$km\n");
                    $done{$j} = 1;
                    $had{$typ2} = 1;
                    if ($typ == $typ2) {
                        if ($nlen2 > $nlen) {
                            $nlen = $nlen2;
                            $rak = $ra2;
                        }
                    } elsif ($typ == 3) {
                        if ($typ2 == 12) {
                            $rak = $ra2;
                        } elsif ($typ2 == 13) {
                            if (!defined $had{12}) {
                                $rak = $ra2;
                            }
                        } else {
                            prtw("WARNING: Type $typ and $typ2 duplication NOT checked. *FIX ME*\n");
                        }
                    } elsif (($typ == 12)&&($typ2 == 13)) {
                        # have aleady selected the 12, so nothing to do here
                    } else {
                        prtw("WARNING: Type $typ and $typ2 duplication NOT checked. *FIX ME*\n");
                    }
                }
            }
        }
        push(@nnavs,$rak); # push the ONE to keep
    }
    # ========================================
    # output the new records
    @nnavs = sort mycmp_ascend_n0 @nnavs;
    $max = scalar @nnavs;
    prt("Done $i records... kept $max...\n");
    my $csv = "type,lat,lon,feet,freq,rng,bear,id,name\n";
    for ($i = 0; $i < $max; $i++) {
        $ra = $nnavs[$i];
        $typ = ${$ra}[0];
        $lat = ${$ra}[1];
        $lon = ${$ra}[2];
        $alt = ${$ra}[3];
        $frq = ${$ra}[4];
        $rng = ${$ra}[5];
        $brg = ${$ra}[6];
        $id  = ${$ra}[7];
        $name = ${$ra}[8];
        $csv .= "$typ,$lat,$lon,$alt,$frq,$rng,$brg,$id,$name\n";
    }
    if (-f $out_file2) {
        my $msg = "WARNING: $out_file2 ALREADY exists! Delete or rename to write a new one.\n";
        if (open INF, "<$out_file2") {
            my @lines = <INF>;
            close INF;
            $max = scalar @lines;
            $msg .= "It contains $max lines.\n";
        } else {
            $msg .= "But am unable to open it...\n";
        }
        prtw($msg);
    } else {
        write2file($csv,$out_file2);
        prt("Clean navaids written to $out_file2\n");
    }
    $load_log = 1;
}

sub process_in_file($) {
    my ($inf) = @_;
    if (! open INF, "<$inf") {
        pgm_exit(1,"ERROR: Unable to open file [$inf]\n"); 
    }
    my @lines = <INF>;
    close INF;
    my $lncnt = scalar @lines;
    prt("Processing $lncnt lines, from [$inf]...\n");
    my %types = ();
    my %navids = ();
    my %navfreqs = ();
    my ($i,$line,$inc,$lnn,$len,@arr,$typ,$nc);
    my ($lat,$lon,$alt,$frq,$rng,$brg,$id,$name);
    my ($off,$ra);
    $lnn = 0;
    my $csv = "type,lat,lon,feet,freq,rng,bear,id,name\n";
    my @navaids = ();
    for ($i = 0; $i < $lncnt; $i++) {
        $lnn++;
        $line = $lines[$i];
        chomp $line;
        $line = trim_all($line);
        $len = length($line);
        next if ($len < 10); # arbitrary - skip first 'I' and last '99' lines
        if ($line =~ /\s+Version\s+/i) {
            prt(substr($line,0,50)."\n");
            next;
        }
        @arr = split(/\s+/,$line);
        $nc = scalar @arr;
        $typ = $arr[0];
        if ($nc < 8) {
            prt("Type: [$typ] - Handle this line [$line] - count = $nc...\n");
            pgm_exit(1,"ERROR: FIX ME FIRST!\n");
        }
        $lat = $arr[1];
        $lon = $arr[2];
        $alt = $arr[3];
        $frq = $arr[4];
        $rng = $arr[5];
        $brg = $arr[6];
        $id  = $arr[7];
        $name = join(' ', splice(@arr,8));
        $name =~ s/,/ /g;
        $csv .= "$typ,$lat,$lon,$alt,$frq,$rng,$brg,$id,$name\n";
        $off = scalar @navaids;
        push(@navaids,[$typ,$lat,$lon,$alt,$frq,$rng,$brg,$id,$name]);

        if (!defined $types{$typ}) {
            $types{$typ} = 1;
        } else {
            $types{$typ}++;
        }

        if (!defined $navids{$id}) {
            $navids{$id} = 1;
        } else {
            $navids{$id}++;
        }

        if (!defined $navfreqs{$frq}) {
            $navfreqs{$frq} = 1;
        } else {
            $navfreqs{$frq}++;
        }
    }

    @arr = keys %types;
    $i = scalar @arr;
    prt("Types $i ");
    $len = 0;
    foreach $typ (@arr) {
        $lnn = $types{$typ};
        $line = '???';
        if (defined $navtypes{$typ}) {
            $line = $navtypes{$typ};
            #$line .= ' ' while (length($line) < 5);
        }
        # prt("$typ=$lnn ");
        prt("$typ $line=$lnn, ");
        $len += $lnn;
    }
    prt(" Total $len navaids\n");


    @arr = sort keys %navids;
    $lnn = 0;    # count of ids repeated
    $typ = 0;   # most used
    $line = '';
    foreach $id (@arr) {
        next if ($id eq '----');
        $len = $navids{$id};
        if ($len > 1) {
            $lnn++;
            if ($len > $typ) {
                $typ = $len;
                $line = $id;
            }
        }
    }
    prt("There are $lnn ID repeated, first most used $line with count of $typ\n");

    @arr = sort keys %navfreqs;
    $lnn = 0;    # count of freq repeated
    $typ = 0;   # most used
    $line = '';
    foreach $frq (@arr) {
        next if ($frq == 0);
        $len = $navfreqs{$frq};
        if ($len > 1) {
            $lnn++;
            if ($len > $typ) {
                $typ = $len;
                $line = $frq;
            }
        }
    }
    prt("There are $lnn freq repeated, first most used $line with count of $typ\n");


    party_with_navaids(\@navaids);

    if (! -f $out_file) {
        write2file($csv,$out_file);
        prt("Nav csv wirtten to $out_file\n");
    }
}

#########################################
### MAIN ###
###parse_args(@ARGV);
process_in_file($in_file);
pgm_exit(0,"");
########################################

sub need_arg {
    my ($arg,@av) = @_;
    pgm_exit(1,"ERROR: [$arg] must have a following argument!\n") if (!@av);
}

sub parse_args {
    my (@av) = @_;
    my ($arg,$sarg);
    my $verb = VERB2();
    while (@av) {
        $arg = $av[0];
        if ($arg =~ /^-/) {
            $sarg = substr($arg,1);
            $sarg = substr($sarg,1) while ($sarg =~ /^-/);
            if (($sarg =~ /^h/i)||($sarg eq '?')) {
                give_help();
                pgm_exit(0,"Help exit(0)");
            } elsif ($sarg =~ /^v/) {
                if ($sarg =~ /^v.*(\d+)$/) {
                    $verbosity = $1;
                } else {
                    while ($sarg =~ /^v/) {
                        $verbosity++;
                        $sarg = substr($sarg,1);
                    }
                }
                $verb = VERB2();
                prt("Verbosity = $verbosity\n") if ($verb);
            } elsif ($sarg =~ /^l/) {
                if ($sarg =~ /^ll/) {
                    $load_log = 2;
                } else {
                    $load_log = 1;
                }
                prt("Set to load log at end. ($load_log)\n") if ($verb);
            } elsif ($sarg =~ /^o/) {
                need_arg(@av);
                shift @av;
                $sarg = $av[0];
                $out_file = $sarg;
                prt("Set out file to [$out_file].\n") if ($verb);
            } else {
                pgm_exit(1,"ERROR: Invalid argument [$arg]! Try -?\n");
            }
        } else {
            $in_file = $arg;
            prt("Set input to [$in_file]\n") if ($verb);
        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON!\n");
        if (length($in_file) ==  0) {
            $in_file = $def_file;
            prt("Set DEFAULT input to [$in_file]\n");
        }
    }
    if (length($in_file) ==  0) {
        pgm_exit(1,"ERROR: No input files found in command!\n");
    }
    if (! -f $in_file) {
        pgm_exit(1,"ERROR: Unable to find in file [$in_file]! Check name, location...\n");
    }
}

sub give_help {
    prt("$pgmname: version $VERS\n");
    prt("Usage: $pgmname [options] in-file\n");
    prt("Options:\n");
    prt(" --help  (-h or -?) = This help, and exit 0.\n");
    prt(" --verb[n]     (-v) = Bump [or set] verbosity. def=$verbosity\n");
    prt(" --load        (-l) = Load LOG at end. ($outfile)\n");
    prt(" --out <file>  (-o) = Write output to this file.\n");
}

sub get_nav_spec() {
    my $txt = <<EOF;

from : http://data.x-plane.com/file_specs/XP\%20NAV810\%20Spec.pdf

2 NDB (Non-Directional Beacon) Includes NDB component of Locator Outer Markers (LOM)
3 VOR (including VOR-DME and VORTACs) Includes VORs, VOR-DMEs and VORTACs
4 Localiser component of an ILS (Instrument Landing System)
5 Localiser component of a localiser-only approach Includes for LDAs and SDFs
6 Glideslope component of an ILS Frequency shown is paired frequency, not the DME channel
7 Outer markers (OM) for an ILS Includes outer maker component of LOMs
8 Middle markers (MM) for an ILS
9 Inner markers (IM) for an ILS
12 DME, including the DME component of an ILS, VORTAC or VOR-DME Frequency display suppressed on X-Plane’s charts
13 Stand-alone DME, or the DME component of an NDB-DME Frequency will displayed on X-Plane’s charts

2 NDB Non-directional beacon
2 Row code for an NDB 2
47.63252778 Latitude of NDB in decimal degrees Eight decimal places supported
-122.38952778 Longitude of NDB in decimal degrees Eight decimal places supported
0 Elevation in feet above MSL Integer. Used to calculate service volumes.
362 Frequency in KHz Integer. Decimal frequencies not supported.
50 Maximum reception range in nautical miles Integer
0.0 Not used for NDBs 0.0
BF NDB identifier Up to four characters. Not unique
NOLLA NDB NDB name Text, suffix with "NDB"

3 VOR Includes VOR-DMEs and VORTACs
3 Row code for a VOR 3
47.43538889 Latitude of VOR in decimal degrees Eight decimal places supported
-122.30961111 Longitude of VOR in decimal degrees Eight decimal places supported
354 Elevation in feet above MSL Integer. Used to calculate service volumes.
11680 Frequency in MHZ (multiplied by 100) Integer - MHz multiplied by 100 (eg. 123.45MHz = 12345)
130 Maximum reception range in nautical miles Integer
19.0 Slaved variation for VOR Up to three decimal places supported
SEA VOR identifier Up to four characters. Not unique
SEATTLE VORTAC VOR name Text, suffix with "VOR", "VORTAC" or "VOR-DME"

4, 5 LOC Includes localisers (inc. LOC-only), LDAs and SDFs
4 Row code for a localizer associated with an ILS 4=ILS localizer, 5=stand-alone localizer (inc LOC, LDA & SDF)
47.42939200 Latitude of localiser in decimal degrees Eight decimal places supported.
-122.30805600 Longitude of localiser in decimal degrees Eight decimal places supported.
338 Elevation in feet above MSL Integer.
11030 Frequency in MHZ (multiplied by 100) Integer - MHz multiplied by 100 (eg. 123.45MHz = 12345)
18 Maximum reception range in nautical miles Integer
180.343 Localiser bearing in true degrees Up to three decimal places supported
ISNQ Localiser identifier Up to four characters. Usually start with "I". Not unique
KSEA Airport ICAO code Up to four characters. Must be valid airport code
16L Associated runway number Up to three characters
ILS-cat-I Localiser name Use "ILS-cat-I", "ILS-cat-II", "ILS-cat-III", "LOC", "LDA" or "SDF"

6 Glideslope Glideslope associated with an ILS
6 Row code for a glideslope 6
47.46081700 Latitude of glideslope aerial in decimal degrees Eight decimal places supported
-122.30939400 Longitude of glideslope aerial in decimal degrees Eight decimal places supported
425 Elevation in feet above MSL Integer.
11030 Frequency in MHZ (multiplied by 100) Integer - MHz multiplied by 100 (eg. 123.45MHz = 12345)
10 Maximum reception range in nautical miles Integer
300180.343 Associated localiser bearing in true degrees prefixed by glideslope angle Up to three decimal places supported.
Glideslope angle multiplied by 100,000 and added (eg.
Glideslope of 3.25 degrees on heading of 123.456 becomes
325123.456)
ISNQ Glideslope identifier Up to four characters. Usually start with "I". Not unique
KSEA Airport ICAO code Up to four characters. Must be valid airport code
16L Associated runway number Up to three characters
GS Name "GS"

7, 8, 9 Marker beacons Outer (OM), Middle (MM) and Inner (IM) Markers
8 Row code for a middle marker 7=OM, 8=MM, 9=IM
47.47223300 Latitude of marker in decimal degrees Eight decimal places supported
-122.31102500 Longitude of marker in decimal degrees Eight decimal places supported
433 Elevation in feet above MSL Integer
0 Not used 0
0 Not used 0
180.343 Associated localiser bearing in true degrees Up to three decimal places supported
---- Not used Use "----" to indicate no associated ID
KSEA Airport ICAO code Up to four characters. Must be valid airport code
16L Associated runway number Up to three characters
MM Name "OM", "MM" or "IM"

12, 13 DME Distance Measuring Equipment
12 Row code for a DME 12=Suppress frequency, 13=display frequency
47.43433300 Latitude of DME in decimal degrees Eight decimal places supported
-122.30630000 Longitude of DME in decimal degrees Eight decimal places supported
369 Elevation in feet above MSL Integer
11030 Frequency in MHZ (multiplied by 100) Integer - MHz multiplied by 100 (eg. 123.45MHz = 12345)
10 Minimum reception range in nautical miles Integer
0.000 DME bias in nautical miles. Default is 0.000
ISNQ Identifier Up to four characters. Not unique.
KSEA Airport ICAO code (for DMEs associated with an ILS)   Only used for DMEs associated with an ILS.
  Up to four characters. Must be valid ICAO code
16L Associated runway number (for DMEs associated with an ILS)   Only used for DMEs associated with an ILS.
  Up to three characters
DME-ILS DME name (all DMEs)   "DME-ILS if associated with ILS
  Suffix "DME" to navaid name for VOR-DMEs, VORTACs &
NDB-DMEs (eg. "SEATTLE VORTAC DME" in example data)
  For standalone DMEs just use DME name


EOF
    return $txt;
}

# eof - cntnavtypes.pl
