#!/usr/bin/perl -w
# NAME: livedata.pl
# AIM: VERY SPECIFIC - Analyse some mpserver15 livedata logs
use strict;
use warnings;
use File::Basename;  # split path ($name,$dir,$ext) = fileparse($file [, qr/\.[^.]*/] )
use JSON;
use Data::Dumper;
use Math::Trig;
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";
# 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_dir = '';
my $verbosity = 0;
my $out_file = $temp_dir.$PATH_SEP."tempdump.txt";

# ### DEBUG ###
my $debug_on = 1;
my $def_file = 'C:\OSGeo4W\apache\htdocs\map-test2\proxy';

### 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);
}

sub perl_scalar() {
    my $txt = <<EOF;
  'data' => {
              'wpt' => [
                         {
                           'alt' => '0',
                           'lat' => '37.769655',
                           'time' => '2014-09-05 17:35:13+08',
                           'callsign' => 'Olaf',
                           'flight_id' => '5772839',
                           'model' => 'mp-nimitz',
                           'current_status' => 'OPEN',
                           'id' => '380305248',
                           'time_raw' => '1409909713',
                           'lon' => '-122.613908'
                         },
                         {
                           'alt' => '14567.956326',
                           'lat' => '43.207371',
                           'time' => '2014-09-05 17:35:13+08',
                           'callsign' => 'Rayonix',
                           'flight_id' => '5772826',
                           'model' => 'Rafale-M',
                           'current_status' => 'OPEN',
                           'id' => '380305247',
                           'time_raw' => '1409909713',
                           'lon' => '9.567521'
                         },
                         ... etc
                               ]
                    },
  'header' => {
                'request_time' => '2014-09-05 17:35:22+0800',
                'msg' => 'OK',
                'request_time_raw' => 1409909722,
                'process_time' => '0.063275098800659',
                'code' => 200
              }

EOF
    return $txt;
}

my $pi = atan2(1,1) * 4;
my $pi2 = $pi + $pi;
my $d2r = $pi / 180;
my $r2d = 180 / $pi;
my $R = 6371; # earth's mean radius in km

#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#:::  This function get the arccos function using arctan function   :::
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
sub my_acos($) {
    my ($rad) = @_;
    my $ret = atan2(sqrt(1 - $rad**2), $rad);
    return $ret;
}
 
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#:::  This function converts decimal degrees to radians             :::
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
sub mdeg2rad($) {
    my ($deg) = @_;
    return ($deg * $pi / 180);
}
 
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#:::  This function converts radians to decimal degrees             :::
#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
sub mrad2deg($) {
    my ($rad) = @_;
    return ($rad * 180 / $pi);
}


sub distance($$$$$) {
    my ($lat1, $lon1, $lat2, $lon2, $unit) = @_;
    my $theta = $lon1 - $lon2;
    my $dist = sin(mdeg2rad($lat1)) * sin(mdeg2rad($lat2)) + 
        cos(mdeg2rad($lat1)) * cos(mdeg2rad($lat2)) * cos(mdeg2rad($theta));
    $dist = my_acos($dist);
    $dist = mrad2deg($dist);
    $dist = $dist * 60 * 1.1515;
    if ($unit eq "K") {
        $dist = $dist * 1.609344;
    } elsif ($unit eq "N") {
        $dist = $dist * 0.8684;
    }
    return ($dist);
}

sub distHaversine($$$$) {
    my ($lat1, $lon1, $lat2, $lon2) = @_;
    my $dLat = mdeg2rad($lat2-$lat1);
    my $dLon = mdeg2rad($lon2-$lon1);
    $lat1 = mdeg2rad($lat1);
    $lat2 = mdeg2rad($lat2);
    my $a = sin($dLat/2) * sin($dLat/2) +
        cos($lat1) * cos($lat2) * sin($dLon/2) * sin($dLon/2);
    my $c = 2 * atan2(sqrt($a), sqrt(1-$a));
    my $d = $R * $c;
    return $d;
}

sub pythag_distance($$$$) {
    my ($x1, $y1, $x2, $y2) = @_;
	return sqrt(($x2 - $x1) ** 2 + ($y2 - $y1) ** 2);
}


sub getBearing($$$$) {
    my ($Lat1,$Lon1,$Lat2,$Lon2) = @_;
    #// convert to radians
    my $startLat = $Lat1 * $d2r;
    my $startLon = $Lon1 * $d2r;
    my $endLat   = $Lat2 * $d2r;
    my $endLon   = $Lon2 * $d2r;
    my $dLon = $endLon - $startLon;
    my $dPhi = log(tan($endLat/2.0+$pi/4.0)/tan($startLat/2.0+$pi/4.0));
    if (abs($dLon) > $pi) {
        if ($dLon > 0.0) {
            $dLon = -($pi2 - $dLon);
        } else {
            $dLon =  ($pi2 + $dLon);
        }
    }
    my $d = atan2($dLon, $dPhi) * $r2d;
    return ($d + 360.0) % 360.0;
}

sub bearing2($$$$) {
    my ($lat1, $lon1, $lat2, $lon2) = @_;
    my $dLat = mdeg2rad($lat2-$lat1);
    my $dLon = mdeg2rad($lon2-$lon1);
    $lat1 = mdeg2rad($lat1);
    $lat2 = mdeg2rad($lat2);
    $lon1 = mdeg2rad($lon1);
    $lon2 = mdeg2rad($lon2);
    my $y = sin($lon2-$lon1) * cos($lat2);
    my $x = cos($lat1)*sin($lat2) - sin($lat1)*cos($lat2)*cos($lon2-$lon1);
    my $tc1 = 0;

    if ($y > 0) {
       if ($x > 0) {
           $tc1 = arctan($y/$x);
       } elsif ($x < 0) {
           $tc1 = 180 - arctan(-$y/$x);
       } else { # if ($x = 0)
           $tc1 = 90;
       }
    } elsif ($y < 0) {
       if ($x > 0) {
           $tc1 = -arctan(-$y/$x);
       } elsif ($x < 0) {
           $tc1 = arctan($y/$x)-180;
       } else { # if ($x = 0)
           $tc1 = 270;
       }
    } else { # if ($y = 0) then
       if ($x > 0) {
           $tc1 = 0;
       } elsif ($x < 0) {
           $tc1 = 180;
       } else { # if ($x = 0) then [the 2 points are the same]
           $tc1 = 0;
       }
    }
    return $tc1;
}


my %callsigns = ();
my @repeat_counts = ();
my $rqtime = '';

sub show_repeat_counts() {
    my $cnt = scalar @repeat_counts;
    my ($ra,$time,$file,$total,$rcnt,$av);
    ##                     0      1      2
    #push(@repeat_counts, [$rcnt, $file, $rqtime]);
    foreach $ra (@repeat_counts) {
        $time = ${$ra}[2];
        $rcnt = ${$ra}[0];
        $file = ${$ra}[1];
        $total += $rcnt;
    }
    $av = $total / $cnt;
    prt("Repeat count for $cnt files...average $av...\n");

}

sub show_callsigns() {
    my $max = 8;
    my @arr = keys %callsigns;
    my $tcnt = scalar @arr;
    prt("Got $tcnt callsigns...\n");
    my ($ra,$lat1,$lon1,$lat2,$lon2,$d,$total);
    my ($cs,$wpt,$adist,$stat,$msg,$b1,$b2,$ccnt,$fcnt);
    my ($cnt,$fid,@arr2,$cfid,$rt1,$rt2,$spd);
    my ($rtf,$rtl);
    $msg = '';
    foreach $cs (@arr) {
        $ra = $callsigns{$cs};
        my %fltfids = ();
        foreach $wpt (@{$ra}) {
            $fid  = ${$wpt}{flight_id};
            $fltfids{$fid} = 1;
        }
        @arr2 = keys %fltfids;
        foreach $cfid (@arr2) {
            $cnt = 0;
            $total = 0;
            $ccnt = 0;
            $msg = 'OPEN';
            $spd = '';
            $rtf = 0;
            $rtl = 0;
            $spd = '';
            foreach $wpt (@{$ra}) {
                $fid  = ${$wpt}{flight_id};
                next if ($fid ne $cfid);
                $stat = ${$wpt}{current_status};
                $lat1 = ${$wpt}{lat};
                $lon1 = ${$wpt}{lon};
                $rt1  = ${$wpt}{time_raw};
                if ($cnt) {
                    $d = distHaversine($lat1,$lon1,$lat2,$lon2);
                    $total += $d;
                    if ($cnt == 1) {
                        $b1 = getBearing($lat1,$lon1,$lat2,$lon2);
                    } else {
                        $b2 = getBearing($lat1,$lon1,$lat2,$lon2);
                    }
                    if ($d > 0) {
                        if ($rt1 > $rt2) {
                            $spd = $d / ($rt1 - $rt2);
                            $spd *= 3600 / 1000;
                        } elsif ($rt2 > $rt1) {
                            $spd = $d / ($rt2 - $rt1);
                            $spd *= 3600 / 1000;
                        }
                    }
                    $rtl = $rt1;
                } else {
                    $rtf = $rt1;
                }
                $lat2 = $lat1;
                $lon2 = $lon1;
                $rt2 = $rt1;
                $cnt++;
                if ($stat eq 'CLOSED') {
                    $msg = 'CLOSED';
                    $ccnt++;
                }
            }
            if (length($spd) == 0) {
                if ($total > 1) {
                    if ($rtl > $rtf) {
                        $spd = $total / ($rtl - $rtf);
                        $spd *= 3600 / 1000;
                    } elsif ($rtf > $rtl) {
                        $spd = $total / ($rtf - $rtl);
                        $spd *= 3600 / 1000;
                    }
                }
            }
            if (length($spd)) {
                $spd = int($spd * 100) / 100;

            }

            $adist = sprintf("%6.3f",$total);
            $cs .= ' ' while (length($cs) < $max);
            prt("$cfid: $cs $adist $cnt ($ccnt) $b1 $b2 $msg $spd\n");
        }
    }
}

sub show_json($$) {
    my ($line,$file) = @_;
    my $json = JSON->new->allow_nonref;
    my $ps = $json->decode( $line );
    #my $txt = Dumper($ps);
    #write2file($txt,$out_file);
    #prt("Dump of perl scalar to $out_file\n");
    my $header = ${$ps}{header};
    my $wpts = ${$ps}{data}{wpt};
    my $cnt = scalar @{$wpts};
    $rqtime = ${$header}{request_time};
    prt("Request time: $rqtime, got $cnt waypoints\n");
    my ($wpt,$cs,$ra,@arr,$stat,$ocnt,$max,$len,$adist,$id);
    my ($wpt2,$id2,$fnd,$rcnt);
    $ocnt = 0;
    $rcnt = 0;
    foreach $wpt (@{$wpts}) {
        $cs = ${$wpt}{callsign};
        $stat = ${$wpt}{current_status};
        $id = ${$wpt}{id};
        $ocnt++ if ($stat eq 'OPEN');
        $callsigns{$cs} = [] if (!defined $callsigns{$cs});
        $ra = $callsigns{$cs};
        $fnd = 0;
        foreach $wpt2 (@{$ra}) {
            $id2 = ${$wpt2}{id};
            if ($id eq $id2) {
                $fnd = 1;
                last;
            }
        }
        if ($fnd) {
            $rcnt++;
        } else {
            push(@{$ra},$wpt);
        }
    }
    if ($rcnt) {
        prt("Got $rcnt repeated waypoint ids in $file\n") if (VERB9());
    }
    #                     0      1      2
    push(@repeat_counts, [$rcnt, $file, $rqtime]);
}


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") if (VERB9());
    my ($line,$inc,$lnn);
    $lnn = 0;
    $line = join(" ",@lines);
    show_json($line,$inf);
}

sub process_in_dir($$) {
    my ($dir,$lev) = @_;
    if (!opendir(DIR,$dir)) {
        prtw("WARNING: Failed to open directory $dir!\n");
        return;
    }
    my @files = readdir(DIR);
    closedir(DIR);
    my $cnt = scalar @files;
    prt("Found $cnt items in $dir...\n");
    ut_fix_directory(\$dir);
    my ($file,$ff);
    my @dfiles = ();
    foreach $file (@files) {
        next if ($file eq '.');
        next if ($file eq '..');
        $ff = $dir.$file;
        if (-f $ff) {
            if ($file =~ /^livedata(\d+)\.txt$/) {
                push(@dfiles,$ff);
            }
        }
    }
    $cnt = scalar @dfiles;
    prt("Found $cnt data files...\n");
    foreach $file (@dfiles) {
        process_in_file($file);
        last;
    }

}

#########################################
### MAIN ###
parse_args(@ARGV);
process_in_dir($in_dir,0);
show_callsigns();
show_repeat_counts();
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_dir = $arg;
            prt("Set input to [$in_dir]\n") if ($verb);
        }
        shift @av;
    }

    if ($debug_on) {
        prtw("WARNING: DEBUG is ON!\n");
        if (length($in_dir) ==  0) {
            $in_dir = $def_file;
            prt("Set DEFAULT input to [$in_dir]\n");
        }
    }
    if (length($in_dir) ==  0) {
        pgm_exit(1,"ERROR: No input dir found in command!\n");
    }
    if (! -d $in_dir) {
        pgm_exit(1,"ERROR: Unable to find in dir [$in_dir]! 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");
}

# eof - template.pl
