#!/usr/bin/env perl
=head1 NAME

readSkewt.pl - download NUCAPS Skew-T SVG files to zip file.

=head1 DESCRIPTION

 This script downloads SVG files into a zip file.
 It downloads SVGs for 2 different situations: 
  (1) Downloading most recent SVG data (Day 1, D1)
      for some rectangular domain.
  (2) Downloading 10 SVG files for 10-day for some 
      Skew-T location (represented by some ID gXXXXXX).

=head1 USAGE

 I. Command-line/web-browser usage for downloading SVGs for 
    rectangular domain:

    General Usage 
    $ ./readSkewt.pl 
      coordinates={MIN_LON,MAX_LON,MIN_LAT,MAX_LAT} 
      files=g{XXXXXX},g{XXXXXX}
 
    Example Usage: 
    $ ./readSkewt.pl 
      "coordinates=-75.5,-61,-45,-39.5&files=186689,194638"
    
    Example Usage: 
    $ perl readSkewt.pl 
      "coordinates=-75.5,-61,-45,-39.5&files=186689,194638"

  II. Command-line/web-browser usage for downloading SVGs for
      10-day Skew-T animation (only one dot/location on map):

    General Usage
    $ ./readSkewt.pl
      id=gXXXXXX (where XXXXXX is a 6-digit XML ID integer)

    $ ./readSkewt.pl id=g072044

   Web-Browser (URL) Usage:
   Place the following URL in your web-browser:
   *(note: this is just an example for a NOAA/OSPO "jumpbox")
   https://ospo2.espc.nesdis.noaa.gov/cgi-bin/nucaps_skewt/readSkewt.pl?id=g072404

=head1 AUTHOR

  Gerasimos Michalitsianos
  gerasimos.michalitsianos@noaa.gov
  Last Updated: January 2024

=head1 VERSION

  $Header: http://10.144.6.20/espcrepo/ESPC/Sounding/NUCAPS_MONITOR/branches/WR10406_M03/web_public/cgi-bin/readSkewt.pl 7842 2019-12-30 23:26:57Z oroytbur $

=cut

use warnings;
use strict;
use CGI;
use CGI qw(:standard);
use FindBin;
use lib "$FindBin::Bin";
use SkewtTable;

# read parameters by calling method
# ReadConfigFile() from SkewtTable.pm
die "Unable to read SkewT configuration file."
  unless ( ReadConfigFile() );

# obtain grid dimensions using Skew-T config.
# inside SkewtTable.pm
my $params = CalculateTableParams();
my $NCOLS = ${$params}{"nCols"};

# create new Perl CGI object, 
# retrieve coordinate bbox string
my $cgi = CGI->new;

# this script can be called two different ways. 
# where are a couple URL examples: 
#   https://ospo2.espc.nesdis.noaa.gov/cgi-bin/nucaps_skewt
#     /readSkewt.pl?coordinates=-30.5,-29.5,31,32&files=83819,85261
#   https://ospo2.espc.nesdis.noaa.gov/cgi-bin/nucaps_skewt
#     /readSkewt.pl?id=g086721
# So the first example shows how this script to 
# used to download Skew-T SVGs for an area from the 
# map, and the second is by clicking the "Download" button 
# on the NUCAPS Skew-T 10-day animation page, which can be 
# navigated to by clicking a dot (square) on the global map.
# 

# make sure there are either 
# 2 input arguments (for downloading SVG Skew-T data 
# for a rectangular domain) or 1 input (a simple XML id for 
# one Skew-T location/dot on map to download data for 
# 10-day animation. These arguments should be passed into 
# the URL via via GET (in the web-browser)

my @params = $cgi->param();
unless (@params == 2 || @params == 1) {
  die "There should be 1 or 2 arguments passed via GET" ;
}

# initialize corner coordinates
# they may have been passed-in via the URL (if we are
# downloading data for rectangular domain from map), 
# else we set them to NULL if we are downloading SVGs
# for 10-day Skew-T animation

my $minLon = "N/A";
my $maxLon = "N/A";
my $minLat = "N/A";
my $maxLat = "N/A";

# define SkewT "g string" START,FINISH integer indices
# which may have been passed via URL if downloading data
# for rectangular domain. If not, they are initialized to
# 0,1 for the purpose of downloading data for only one "dot" 
# on the map (for the Skew-T 10-day animation).
my @corners = (0,1);

# initialize other variables to iterate through Skew-T grid.
my @row;
my @days;
my $rowCounter = $corners[0];
my $ncols = "NULL";

my $DOWNLOAD_SKEWT_ANIMATION;
my $animationID = "NULL";

if( $cgi->param() == 2) { 

  # if we are in this block, we are 
  # NOT coming from NUCAPS Skew-T ANIMATION page
  $DOWNLOAD_SKEWT_ANIMATION = 0;

  # make sure names of two arguments passed in via GET are:
  # (1) "coordinates"
  # (2) "files"
  # in that particular order

  unless( $params[0] eq "coordinates" ) {
    die "First arg. should be a string \"coordinates\"";
  }

  unless( $params[1] eq "files" ) {
    die "Second arg. should be FILES" ;
  }

  # now make sure first second argument "files" goes like
  # the format : (integer,integer)

  my $fileIDs = $cgi->param("files");
  $fileIDs =~ s/^\s+//;

  die "Second input arg should have comma-separated positive integers i.e. integer,integer"
    unless ( $fileIDs =~ /^(\d+(,\d+)*)?$/ ) ;
  @corners = split "," ,$fileIDs ;

  # make sure input coordinate string is list of 4 comma-separated floating-point variables
  my $coordinateStr = $cgi->param("coordinates");
  die "Coordinate string should be comma-separated string of 4 numbers"
    unless ($coordinateStr =~
        /^[-+]?([0-9]{1,3}|[0-9]{1,3}\.[0-9]*) *,[-+]?[0-9]{1,3}\.?[0-9]* *,[-+]?[0-9]{1,3}\.?[0-9]* *,[-+]?[0-9]{1,3}\.?[0-9]* *$/);
  my @bbox = split "," , $coordinateStr ;

  # make sure input coordinate bounding-box string is within bounds
  # of global domain, it should go like:
  # min-longitude,max-longitude,min-latitude,max-latitude

  $minLon = $bbox[0];
  $maxLon = $bbox[1];
  $minLat = $bbox[2];
  $maxLat = $bbox[3];

  unless(($minLon >= -180.0 ) && ($minLon <= 180.0 )) {
    die "Min. longitude in 'coordinates' GET argument (first arg.) should be between -180 and 180 deg.";
  }

  unless(($maxLon >= -180.0 ) && ($maxLon <= 180.0 )) {
    die "Min. longitude in 'coordinates' GET argument (first arg.) should be between -180 and 180 deg.";
  }

  unless(($minLat >= -90.0 )  && ($minLat <= 90.0 )) {
    die "Min. latitude in 'coordinates' GET argument (first arg.) should be between -90 and 90 deg.";
  }

  unless(($maxLat >= -90.0 )  && ($maxLat <= 90.0 )) {
    die "Min. latitude in 'coordinates' GET argument (first arg.) should be between -90 and 90 deg.";
  }

  # make sure coordinates, in decimal degrees, make sense.
  if( $minLon > $maxLon ) {
    die "Maximum longitude $maxLon should be greater than minimum longitude $minLon";
  }

  if( $minLat > $maxLat ) {
    die "Maximum longitude $minLat should be greater than minimum latitude $minLat";
  }

  $ncols = (($bbox[1] - $bbox[0]) / 0.5) + 1 ;
  $rowCounter = $corners[0];
  @days = qw( D1 );

} else { 

  # if we are in this clause, then we are downloading Skew-T SVG
  # data for one dot or point on the map, for 10 days of the Skew-T 
  # NUCAPS data. 

  my @row;

  #@days = SkewtTable::NumObservations ();
  #@days = map "D".$_, 1 .. SkewtTable::NumObservations (); 
  @days = SkewtTable::SoundingDirs();

  $DOWNLOAD_SKEWT_ANIMATION = 1;
  
  unless( $params[0] eq "id" ) {
    die 'First arg. should be "id"';
  }
  $animationID = $cgi->param("id");

  die 'Skew-T XML ID should be "g" followed by a 6-digit integer (i.e. "g080128")'
    unless ($animationID =~ /g\d{6}/);
  $animationID =~ s/g//g;
  $animationID=int($animationID);

}

# define hash to hold the following: 
#   (1) XML filename (path, not URL) as keys
#   (2) contents of XML file as string

my %xmlData = ();
my $numXML=0;

while( $rowCounter<$corners[1] ) {

  my @row;
  unless( $DOWNLOAD_SKEWT_ANIMATION ) {
    @row=();
    foreach my $column ( $rowCounter .. ($rowCounter + $ncols-1) ) {
      push @row, $column;
    }
  } else { 
    @row = map {$_ = $animationID} (1 .. SkewtTable::NumObservations ());
  }

  foreach my $day (@days) {
     foreach my $xmlID (@row) {

      my $xmlPath = GetSkewtImagePath($xmlID,$day);
      my $xml = WEB_ROOT_DIR.$xmlPath;
      open my $xmlh, "<" , $xml or next;
      my $xmlContents = do { local $/; <$xmlh> };

      # ------------------------------------------------------------
      # get following information from XML file: 
      #   (1) Coordinates of sounding (location, Latitude/Longitude)
      #   (2) Accepted or Rejected Flag
      #   (3) Platform (i.e. SNPP, N20, Metop-A, Metop-B, etc.)
      #   (4) Date string
      # ------------------------------------------------------------

      my($location) = $xmlContents 
        =~ /<Location>(.*)<\/Location>/;
      $location =~ s/°/ deg/g;
      my($acceptedOrRejected) = $xmlContents 
        =~ /<AcceptedOrRejected>(.*)<\/AcceptedOrRejected>/; 
      my($platform) = $xmlContents 
        =~ /<Platform>(.*)<\/Platform>/; 
      my($date) = $xmlContents 
        =~ /<Date>(.*)<\/Date>/; 
   
      # ----------------------------------------
      # get following information from XML file: 
      #   (1) Profile Table Data
      #   (2) Aerologic Table Data
      # ----------------------------------------

      my($profileData) = $xmlContents 
        =~ /<profileTable>(.*)<\/profileTable>/; 
      my($aerologicData) = $xmlContents 
        =~ /<aerologicTable>(.*)<\/aerologicTable>/; 
   
      # assign all data to a hash 
      my %skewtDataHash = ();
      $skewtDataHash{loc} = $location; 
      $skewtDataHash{acceptedOrRejected} = $acceptedOrRejected;
      $skewtDataHash{platform} = $platform;
      $skewtDataHash{date} = $date;
      $skewtDataHash{aerologicData} = $profileData;
      $skewtDataHash{profileData} = $aerologicData;
      $skewtDataHash{xml} = $xmlPath;
      $xmlData{$xmlPath} = \%skewtDataHash;
      $numXML++;
    }
  }
  $rowCounter += $NCOLS;
};

if( $DOWNLOAD_SKEWT_ANIMATION) { 
  $numXML=SkewtTable::NumObservations ();
}

# get hostname 
my $host = "https://".$ENV{HTTP_HOST};
print "Content-type: text/html\n\n";
print <<HEADER;
<html>
  <head>

    <title> 
    </title>
    </br>
    </br>
    </br>
    <center>
      <p id="waitMessage" style="text-align:center;">
        <b>Please wait while your NUCAPS Skew-T plots download ... </b>
      </p>
      </br></br></br></br></br>
      </br></br></br></br></br>
      <p> <b> Output Format</b>: Scalable Vector Graphics (SVG) </p>
      <p> <b> Rectangular Domain, Decimal Degrees (min. lon.,max. lon.,min. lat.,max. lat.) </b> : $minLon, $maxLon, $minLat, $maxLat </p>
      <p> <b> Number of SVG plots in downloading archive </b> : $numXML </p>
    </center>

HEADER

# add JS includes and CSS
print <<JS_CSS;
  <link rel="stylesheet" href="$host/data/soundings/nucaps/pskewt/NPPstatic/skewtStyles.css">
  <script src="$host/scripts/jquery-3.2.1.min.js"></script>
  <script src="$host/scripts/d3.min.js"></script>
  <script src="$host/scripts/Stuk-jszip-4cbbb64/dist/jszip.min.js"></script>
  <script src="$host/scripts/FileSaver.js-master/src/FileSaver.js"></script>
  <script src="$host/data/soundings/nucaps/pskewt/NPPstatic/loadSkewT.js"></script>
  <script src="$host/data/soundings/nucaps/pskewt/NPPstatic/background.js"></script>
  <script src="$host/data/soundings/nucaps/pskewt/NPPstatic/tables.js"></script>
  <script src="$host/data/soundings/nucaps/pskewt/NPPstatic/skewtAnimation.js"></script>
JS_CSS
print "</head>\n";
print "<body>\n";

# (right now we are still in Perl CGI)
# write out Javascript 
print <<JS;
  <script>
      
    function printProfileTable(profileData) { 

      var units = GetUnits();
      var count=0;
      var ypos = 25; 
      var xpos = 890;

      svg.append("text")
        .attr("x",xpos)
        .attr("y",ypos)
        .text("Param MW_IR MW Units")
        .attr("font-weight","bold");

      var profileTableLines =  profileData.split(";");
      var line;
      var outrows = [];
      ypos = ypos + 25; 

      while( count<profileTableLines.length ) {
            
        line = profileTableLines[count].split(",");
        var unitString = units[count];

        if( line.length>1 ) {
     
          if(!((parseFloat( line[0] ) < -999999.0 ) || ( parseFloat( line[0] ) > 999999.0 ))) { 
          
            svg.append("text")
              .attr("y",ypos)
              .attr("x",xpos+80)
              .text( line[0] )
              .style("fill","darkred");
          }

          svg.append("text")
            .attr("y",ypos)
            .attr("x",xpos)
            .text( line[2]  )
            .attr("font-weight","bold");
          
          svg.append("text")
            .attr("y",ypos)
            .attr("x",xpos+150)
            .text( line[1]  )
            .style("fill","blue");
          
          svg.append("text")
            .attr("y",ypos)
            .attr("x",xpos+210)
            .attr("font-weight","bold")
            .text( unitString  );
        }
        count++;
        ypos = ypos+25;
      }
    }

    function printAerologicTable(aerologicData) { 

      var count=0;
      var aerologicTableLines = aerologicData.split(";");
      var ypos = 25;
      var xpos = 630;

      svg.append("text")
        .attr("x",xpos)
        .attr("y",ypos)
        .text( "P (hPa) T(" + String.fromCharCode(176)+"C) DP ("+String.fromCharCode(176)+"C)" )
        .attr("font-weight","bold");
     
      var line;
      var outrows = [];
      ypos = ypos+25;

      while( count<aerologicTableLines.length ) { 
        line = aerologicTableLines[count].split(",");
        if( line.length>1 ) {

          svg.append("text")
            .attr("y",ypos)
            .attr("x",xpos+20)
            .text( line[0]  );

          svg.append("text")
            .attr("y",ypos)
            .attr("x",xpos+90)
            .text( line[1]  )
            .style("fill","darkred");

          svg.append("text")
            .attr("y",ypos)
            .attr("x",xpos+150)
            .text( line[2]  )
            .style("fill","blue");

        }
        count++;
        ypos = ypos+25;
      }
    }
 
    var zip = new JSZip();
    var svgZip = zip.folder("svgs");

  </script>
JS

# create a <div> element to hold Skew-T plot(s)
print "<div>\n";
print "<div id=\"mainbox\"></div>\n";
print "</div>\n";

foreach my $xml_filename ( keys %xmlData ) { 

  my $xmlURL = $host.$xml_filename;
  my(%soundingHash) = %{ $xmlData{$xml_filename} };

  # ----------------------------------------------
  # get the following to display on top of plot: 
  #   (1) Location (Latitude,Longitude)
  #   (2) Date (UTC)
  #   (3) Platform (i.e. Metop-A, N20, SNPP, ...)
  #   (4) Accepted or Rejected
  # ----------------------------------------------

  my $location = $soundingHash{loc};
  my $date = $soundingHash{date};
  my $platform = $soundingHash{platform};
  my $status = $soundingHash{acceptedOrRejected};

  # ----------------------------------------------
  # get two strings that hold data for: 
  #   (1) Profile Table
  #   (2) Aerologic Table
  # ----------------------------------------------

  my $profileData   = $soundingHash{profileData};
  my $aerologicData = $soundingHash{aerologicData};  
  my $svgCounter=0;

  # --------------------------------------------
  # create output filename for SVG that will go 
  # into zip file
  # --------------------------------------------

  my @namesplit = split "/" , $xmlURL;
  my $nameLen   = scalar(@namesplit);
  my $svgName   = $namesplit[$nameLen-2]."_".$namesplit[$nameLen-1];
  $svgName =~ s/.xml/.svg/; 

  print <<PLOT;
    <script>

        \$("div#mainbox").hide();
        var m = [ 30,40,20,35 ], 
            w = 700 - m[1] - m[3]
            h = 700 - m[0] - m[2];
        var svg = InitializeSkewtPlotSVG( m,w,h, "div#mainbox", 500,140, [50,40] );
        var linesAndAxis = DrawSkewtBackground(m,w,h, "12px", "div#mainbox", true);
        var line1 = linesAndAxis[0];
        var line2 = linesAndAxis[1];
        var xBottom = linesAndAxis[2];
        var y = linesAndAxis[3];
        LoadSkewtLines("$xmlURL",svg,line1,line2,xBottom,y,null);
        
        svg.append("text")
           .attr("y",710)
           .attr("x",275)
           .attr("font-weight","bold")
           .text("$location");

        svg.append("text")
           .attr("y",735)
           .attr("x",275)
           .attr("font-weight","bold")
           .text("$date");

        svg.append("text")
           .attr("y",760)
           .attr("x",275)
           .attr("font-weight","bold")
           .text("Platform: $platform");

        svg.append("text")
           .attr("y",785)
           .attr("x",275)
           .attr("font-weight","bold")
           .text("$status");

        printProfileTable("$profileData");
        printAerologicTable("$aerologicData");

        d3.selectAll("svg").node().setAttribute("xmlns","http://www.w3.org/2000/svg");
        var svgData = d3.selectAll("#mainbox").node().outerHTML;
        var preface = '<?xml version="1.0" standalone="no"?>\\r\\n';
        var svgBlob = new Blob([preface,svgData],{type:"image/svg+xml;charset=utf-8"});
        svgZip.file( "$svgName" , svgBlob, {base64: true});
        \$("div#mainbox").empty();

    </script>
PLOT
#  last;
}

print <<JS;
  <script>
 
    var unix_ts = Date.now().toString(); 
    zip.generateAsync({type:"blob"})
    .then(function(content) {
      \$("#waitMessage").hide();
      saveAs(content, "archive_unix_ts_" + unix_ts + ".zip");
    });
  
  </script>
JS
print "</body></html>\n"
