#!/usr/bin/perl -w
#
# btl-all.pl
#
#  Genere un fichier btl avec tous les parametres a partir des fichiers
#  Seabird .btl
#
# $Id J Grelet avril 2014$
#
use strict;    # necessite de declarer toutes les variables globales

#use diagnostics;

# use open qw(:std :utf8);
# binmode(STDERR, ":utf8");
use Time::Local;
use Date::Manip;
use Getopt::Long;
use File::Basename;
use Data::Dumper;
use Oceano::Seawater;
use Oceano::Convert;
use Oceano::Seabird;
use Cwd;
use Config::Tiny;

#use Tie::IxHash;

#------------------------------------------------------------------------------
# Les repertoires de sorties
#------------------------------------------------------------------------------
my $ascii_dir = 'ascii/';
my $odv_dir   = 'odv/';

#------------------------------------------------------------------------------
# Les variables globales
#------------------------------------------------------------------------------
our $VERSION = "1.0";
my $version;
my $help = undef;
my $author;
my $debug;
my $echo;
my $dtd;
my $dtdLocalPath;
my $encoding;

my $hdr_file;
my $btl_file;
my $xml_file;
my $odv_file;

my $header_file;
my $institute;
my $btlType;
my $btlSn;
my $pi;
my $creator;
my $acquisitionSoftware;
my $acquisitionVersion;
my $processingSoftware;
my $processingVersion;

my $cycle_mesure;
my $plateforme;
my $contexte;
my $timezone;
my $format_date;
my $processing_code;
my $begin_date;
my $end_date;
my $cruisePrefix;
my $stationPrefixLength;
my $title_summary;
my $comment;
my $header;
my @header;
my $split;
my %split;
my $format;
my @format;
my %format;
my %data;
my @data;
my %sbe35;

# sbe35
my $t90sbe35;

my $code = -1;    # header code

# fichiers bouteille seabird .btl
my $sbe_file;
my ( $s_date, $e_date,   $e_time,   $iso_date, $t_date );
my ( $PRFL,   $BOTL );
my ( $lat,    $lat_pos,  $lat_deg,  $lat_min,  $lat_hemi );
my ( $long,   $long_pos, $long_deg, $long_min, $long_hemi );
my ( $julien, $date, $month, $day, $year, $heure, $min, $time );

my $type_odv = 'B'; # station-type (either B, C or X for bottle, CTD or XBT data
my $bottom_depth = 1e36;

#------------------------------------------------------------------------------
# display_version()
# display program version
#------------------------------------------------------------------------------
sub display_version() {
  print "Version: $VERSION\nAuthor: $author\n\n";
  exit 1;
}

#------------------------------------------------------------------------------
# usage()
#------------------------------------------------------------------------------
sub usage() {
  print STDERR "\nusage: btl-all.pl [options] <files>\n\n";
  print STDERR
    "Options:\n    --help                 Display this help message\n";
  print STDERR "    --version              program version\n";
  print STDERR "    --debug                debug info\n";
  print STDERR "    --echo                 display filenames processed\n";
  print STDERR "    --dtd=[local|public]   define DTD\n";
  print STDERR "examples:\n\$ perl btl-all.pl --echo data/btl/*.btl\n";
  exit 1;
}

#------------------------------------------------------------------------------
# get_options()
# analyse les options
#------------------------------------------------------------------------------
sub get_options() {

  &GetOptions(
    "echo"    => \$echo,
    "dtd=s"   => \$dtd,
    "debug"   => \$debug,
    "version" => \$version,
    "help"    => \$help
  ) or &usage;

  &display_version if $version;
  &usage           if $help;
}

#------------------------------------------------------------------------------
# read string key inside section in config file
#------------------------------------------------------------------------------
sub read_config_string() {
  my ( $Config, $section, $key ) = @_;

  my $value = $Config->{$section}->{$key};
  if ( !defined $value ) {
    die "Missing string '$key' in section '$section' $!";
  }
  return $value;
}

#------------------------------------------------------------------------------
# read config.ini file where cruise parameter are defined
#------------------------------------------------------------------------------
sub read_config() {
  my ($configFile) = @_;

  # Create a config
  my $Config = Config::Tiny->new;

  $Config = Config::Tiny->read($configFile)
    or die "Could not open '$configFile' $!";

  $author       = &read_config_string( $Config, 'global', 'author' );
  $debug        = &read_config_string( $Config, 'global', 'debug' );
  $echo         = &read_config_string( $Config, 'global', 'echo' );
  $dtd          = &read_config_string( $Config, 'xml',    'dtd' );
  $dtdLocalPath = &read_config_string( $Config, 'xml',    'dtdLocalPath' );
  $encoding     = &read_config_string( $Config, 'xml',    'encoding' );
  $cycle_mesure = &read_config_string( $Config, 'cruise', 'cycle_mesure' );
  $plateforme   = &read_config_string( $Config, 'cruise', 'plateforme' );
  $contexte     = &read_config_string( $Config, 'cruise', 'context' );
  $timezone     = &read_config_string( $Config, 'cruise', 'timezone' );
  $format_date  = &read_config_string( $Config, 'cruise', 'format_date' );
  $processing_code =
    &read_config_string( $Config, 'cruise', 'processing_code' );
  $begin_date = &read_config_string( $Config, 'cruise', 'begin_date' );
  $end_date   = &read_config_string( $Config, 'cruise', 'end_date' );
  $institute  = &read_config_string( $Config, 'cruise', 'institute' );
  $pi         = &read_config_string( $Config, 'cruise', 'pi' );
  $creator    = &read_config_string( $Config, 'cruise', 'creator' );
  $acquisitionSoftware =
    &read_config_string( $Config, 'ctd', 'acquisitionSoftware' );
  $acquisitionVersion =
    &read_config_string( $Config, 'ctd', 'acquisitionVersion' );
  $processingSoftware =
    &read_config_string( $Config, 'ctd', 'processingSoftware' );
  $processingVersion =
    &read_config_string( $Config, 'ctd', 'processingVersion' );
  $cruisePrefix = &read_config_string( $Config, 'ctd', 'cruisePrefix' );
  $stationPrefixLength =
    &read_config_string( $Config, 'ctd', 'stationPrefixLength' );
  $btlType       = &read_config_string( $Config, 'btl', 'type' );
  $btlSn         = &read_config_string( $Config, 'btl', 'sn' );
  $title_summary = &read_config_string( $Config, 'btl', 'title_summary' );
  $comment       = &read_config_string( $Config, 'btl', 'comment' );
  $header        = &read_config_string( $Config, 'btl', 'header' );
  $split         = &read_config_string( $Config, 'btl', 'split' );
  $format        = &read_config_string( $Config, 'btl', 'format' );
}

#------------------------------------------------------------------------------
# write XML header to output _ctd.xml file using oceano.dtd
#------------------------------------------------------------------------------
sub write_xml_header {
  my $today = &dateFormat( undef, "%d/%m/%Y" );

  print XML_BTL_FILE "<?xml version=\"1.0\" encoding=\"$encoding\"?>\n";

  # les commentaires ne sont pas acceptés par XML Toolbox Matlab de Geodise
  if ( $dtd eq 'local' ) {
    print XML_BTL_FILE
"<!DOCTYPE OCEANO SYSTEM \"$dtdLocalPath/$cycle_mesure/local/oceano.dtd\">\n";
  }
  else {
    print XML_BTL_FILE
'<!DOCTYPE OCEANO PUBLIC "-//US191//DTD OCEANO//FR" "http://www.brest.ird.fr/us191/database/oceano.dtd">'
      . "\n";
  }
  print XML_BTL_FILE '<OCEANO TYPE="PROFIL">' . "\n";
  print XML_BTL_FILE "  <ENTETE>\n";
  print XML_BTL_FILE "    <PLATEFORME>\n";
  print XML_BTL_FILE "      <LIBELLE>$plateforme</LIBELLE>\n";
  print XML_BTL_FILE "    </PLATEFORME>\n";
  print XML_BTL_FILE
"    <CYCLE_MESURE CONTEXTE=\"$contexte\" TIMEZONE=\"$timezone\" FORMAT=\"$format_date\">\n";
  print XML_BTL_FILE "      <LIBELLE>$cycle_mesure</LIBELLE>\n";
  print XML_BTL_FILE "      <DATE_DEBUT>$begin_date</DATE_DEBUT>\n";
  print XML_BTL_FILE "      <DATE_FIN>$end_date</DATE_FIN>\n";
  print XML_BTL_FILE "      <INSTITUT>$institute</INSTITUT>\n";
  print XML_BTL_FILE "      <RESPONSABLE>$pi</RESPONSABLE>\n";
  print XML_BTL_FILE
"      <ACQUISITION LOGICIEL=\"$acquisitionSoftware\" version=\"$acquisitionVersion\"></ACQUISITION>\n";
  print XML_BTL_FILE
"      <TRAITEMENT LOGICIEL=\"$processingSoftware\" version=\"$processingVersion\"></TRAITEMENT>\n";
  print XML_BTL_FILE
"      <VALIDATION LOGICIEL=\"$0\" VERSION=\"$VERSION\" DATE=\"$today\" OPERATEUR=\"$creator\" CODIFICATION=\"OOPC\">\n";
  print XML_BTL_FILE "        <CODE>$processing_code</CODE>\n";
  print XML_BTL_FILE "        <COMMENTAIRE>$comment</COMMENTAIRE>\n";
  print XML_BTL_FILE "        <COMMENTAIRE>$title_summary</COMMENTAIRE>\n";
  print XML_BTL_FILE "      </VALIDATION>\n";
  print XML_BTL_FILE "    </CYCLE_MESURE>\n";
  print XML_BTL_FILE
    "    <INSTRUMENT TYPE=\"$btlType\" NUMERO_SERIE=\"$btlSn\">\n";
  print XML_BTL_FILE "    </INSTRUMENT>\n";
  print XML_BTL_FILE "  </ENTETE>\n";
  print XML_BTL_FILE "  <DATA>\n";
}

#------------------------------------------------------------------------------
# fonctions de calcul de la position/date
#------------------------------------------------------------------------------
sub position {
  my ( $deg, $min, $hemi ) = @_;
  my $sign = 1;
  if ( $hemi eq "S" || $hemi eq "W" ) {
    $sign = -1;
  }
  my $tmp = $min;
  $min = abs $tmp;
  my $sec = ( $tmp - $min ) * 100;
  return ( ( $deg + ( $min + $sec / 100 ) / 60 ) * $sign );
}

#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
#sub julian {
#  my($jj,$h,$m) = @_;
#  my $tmp = (($h * 60) + $m ) / 1440;
#  return( $jj + $tmp );
#}

#------------------------------------------------------------------------------
# entete ODV
#------------------------------------------------------------------------------
sub entete_odv {
  my $today = &dateFormat( undef, "%Y-%m-%dT%H:%M:%S" );
  my $cwd = getcwd();

  print ODV_FILE "//ODV Spreadsheet file : $odv_file\n";
  print ODV_FILE "//Data treated : $today\n";
  print ODV_FILE "//<DataType>Profiles</DataType>\n";
  print ODV_FILE "//<InstrumentType>$btlType</InstrumentType>\n";
  print ODV_FILE "//<Source>$cwd</Sources>\n";
  print ODV_FILE "//<Creator>$creator</Creator>\n";
  print ODV_FILE "//\n";
  print ODV_FILE
"Cruise\tStation\tType\tyyyy-mm-ddThh:mm:ss\tLongitude [degrees_east]\tLatitude [degrees_north]\tBot. Depth [m]\tBTL\tPres [db]\tTE01 [C]\tTE02 [C]\tPSA1 [Psu]\tPSA2 [Psu]\tDO12 [micromole/kg]\tDO22 [micromole/kg]\tFLU2 [milligram/m3]\t TUR3 [%]\tPotemp090 [C]\tCPotemp190C [C]\n";
}

#------------------------------------------------------------------------------
# Debut du programme principal
#------------------------------------------------------------------------------
#&Date_Init( "TZ=UTC" );
&Date_Init('SetDate=now,UTC');
&read_config('../config.ini');
&usage if ( $#ARGV == -1 );
&get_options;

# define files name
$hdr_file = lc $ascii_dir . $cycle_mesure . '-all.btl';
$btl_file = lc $ascii_dir . $cycle_mesure . '-all_btl';
$xml_file = lc $ascii_dir . $cycle_mesure . '-all_btl.xml';
$odv_file = lc $odv_dir . $cycle_mesure . '-all_btl-odv.txt';

# get variables liste order from header
@header = split /\s+/, $header;

# convert variable $split from config.ini to hash, ex $split = bottle,1,month,2,...
%split = split /,/, $split;

# convert output format fron config.ini to hash
@format = split /\s+/, $format;

# build hash %format with key: PRES, value: %7.3f
my @tmp = @format;
foreach my $key (@header) {
  $format{$key} = shift @tmp;
}

# test if output dirs exist and create them if not
mkdir($odv_dir)   unless ( -d $odv_dir );
mkdir($ascii_dir) unless ( -d $ascii_dir );

# ouvre les fichiers de sortie
open( HDR_BTL_FILE, "+> $hdr_file" ) or die "Can't open file : $hdr_file\n";
open( BTL_FILE,     "+> $btl_file" ) or die "Can't open file : $btl_file\n";
open( XML_BTL_FILE, "+> $xml_file" ) or die "Can't open file : $xml_file\n";
open( ODV_FILE,     "+> $odv_file" ) or die "Can't open file : $odv_file\n";

print HDR_BTL_FILE
" St  Date_deb  Heure_deb  Date_fin Heure_fin  Latitude  Longitude  Nb_btl\n\n";
&entete_odv;
&write_xml_header;
print XML_BTL_FILE "$header\n";
$header_file = "$cycle_mesure  $plateforme  $institute  $btlType  $btlSn  $pi";
print BTL_FILE "$header_file\n";
print BTL_FILE "$header\n";

# read SBE35 files
my $dir_sbe35 = 'data/sbe35/';

#my @files = grep ( -f ,<*.txt>);
## see: https://stackoverflow.com/questions/1510305/how-can-i-get-a-list-of-all-files-with-a-certain-extension-from-a-specific-direc

foreach my $file_sbe35 ( grep ( -f, <data/sbe35/*.asc> ) ) {

  # recupere le numero de la station dans le nom du fichier
  ($PRFL) = ( $file_sbe35 =~ m/$cruisePrefix(\d{$stationPrefixLength})/i );

  # ouverture du fichier
  open( SBE35_FILE, $file_sbe35 );
  print STDERR "Lit: $file_sbe35\n" if $debug;
  my $i = 1;
  while (<SBE35_FILE>) {
    if ( not( /^[*#]/ || /^\s*$/ || /^dd/ ) ) {

      # print STDERR $_;
      if (/t90\s*=\s*(\d*\.\d*)/) {
        $t90sbe35 = ($1 >= 99.999) ? 1e+36 : $1;
        $sbe35{"$PRFL:$i"} = $t90sbe35;
        print STDERR $sbe35{"$PRFL:$i"} . "\n" if $debug;
        $i++;
      }
    }
  }
  close SBE35_FILE;
}

# parcourt des fichiers .btl passes en argument sur la ligne de commande
for ( my $i = 0 ; $i <= $#ARGV ; $i++ ) {
  my $fileName = $ARGV[$i];

  # ouverture du fichier
  open( DATA_FILE, $fileName );
  print STDERR "\nLit: $fileName " if $debug;

  # recupere le numero de la station dans le nom du fichier
  ($PRFL) = ( $fileName =~ m/$cruisePrefix(\d{$stationPrefixLength})/i );

  $BOTL = 0;

  # on lit le fichier bouteille
  while (<DATA_FILE>) {

    # decode la date et heure de debut de station
    if (/System UpLoad Time =\s+(\.*)/) {    # a modifier suivant le contexte
      ($time) = /System UpLoad Time =\s+(\w+\s+\d+\s+\d+\s+\d+:\d+:\d+)/;
      $date = &ParseDate($time);

      # transforme le day_of_year en julian day
      $julien = &UnixDate( $time, "%j" ) - 1;
      $julien = &julian(
        $julien,
        &UnixDate( $date, "%H" ),
        &UnixDate( $date, "%M" ),
        &UnixDate( $date, "%S" )
      );
      $s_date = &UnixDate( $date, "%d/%m/%Y %H:%M:%S" );

      #$t_date = &UnixDate($date,"%Y%m%d%H%M%S");
      $t_date = &UnixDate( $date, "%q" );
    }
    if (/NMEA Latitude\s*=\s*(\d+\s+\d+.\d+\s+\w)/) {
      ( $lat_deg, $lat_min, $lat_hemi ) = split " ", $1;
      $lat_pos = &position( $lat_deg, $lat_min, $lat_hemi );
    }
    if (/NMEA Longitude\s*=\s*(\d+\s+\d+.\d+\s+\w)/) {
      ( $long_deg, $long_min, $long_hemi ) = split " ", $1;
      $long_pos = &position( $long_deg, $long_min, $long_hemi );

      # print header and add 1e36 to complement columns
      printf XML_BTL_FILE "%05d  %d %7.3f %7.4f %8.4f %s", $PRFL, $code,
        $julien, $lat_pos, $long_pos, $t_date;
      printf XML_BTL_FILE " 1e36" x ( @header - 6 );
      printf XML_BTL_FILE "\n";
      printf BTL_FILE "%05d  %d %7.3f %7.4f %8.4f %s", $PRFL, $code, $julien,
        $lat_pos, $long_pos, $t_date;
      printf BTL_FILE " 1e36" x ( @header - 6 );
      printf BTL_FILE "\n";
    }

    # decodage du fond (sonde)
    if (/Bottom Depth\s*:\s*(\d*)\s*\S*/i) {
      $bottom_depth = ( $1 eq '' ) ? 1e+36 : $1;
    }

# decode la date de chaque prelevement et extrait les donnees dans la ligne correspondante
# les parametres suivants doivent etre presents:
# Bottle Date Sal00 Sal11 Sbeox0ML/L Sbeox0Mm/Kg Sbeox1ML/L Sbeox1Mm/Kg Sbeox0dOV/dT Sbeox1dOV/dT PrDM DepSM
# T090C T190C C0S/m C1S/m Sbeox0V Sbeox1V (optionnels) FlECO-AFL Xmiss
# amop
# Bottle Date Sal00 Sal11 Sbeox0ML/L Sbeox0Mm/Kg Sbeox1ML/L Sbeox1Mm/Kg TimeJ PrDM DepSM T090C T190C
# C0S/m C1S/m Sbeox0V Sbeox1V Upoly0 FlC CStarAt0 CStarTr0 Spar Par Cpar Sbeox0dOV/dT Sbeox1dOV/dT
# Position Time
# TUR3 -> CStarTr0  (transmissiometre Wetlab %)
# TUR4 -> Upoly0 (turbidimètre)
# LGHT;LIGHT IRRADIANCE IMMERGED PAR;micromole photon/(m2.s);0;4000;%8.3lf;9999.999;
# LGH3;float32;light irradiance corrected par;;micromole photon/(m2.s);;TIME DEPTH LATITUDE LONGITUDE;;0;3000;%8.3f;1E+36;;0.001;;;;;;;
# LGH4;LIGHT IRRADIANCE SURFACE PAR;micromole photon/(m2.s);0;3000;%8.3lf;9999.999;
# LGH5 -> CPar (%)
# #LGHT;LIGHT IRRADIANCE IMMERGED PAR;micromole photon/(m2.s);0;4000;%8.3lf;
# FLU2 -> FlC (Chelsea Acquatracka III)
#
    if (/^\s+\d+\s+\w+\s+\d+\s+\d+/) {

# extract values and fill hash %data with position give by hash %split from config.ini
      @data = split /\s+/;
      while ( my ( $key, $value ) = each(%split) ) {
        $data{$key} = $data[$value];
      }
      $date =
        &ParseDate( $data{'month'} . ' ' . $data{'day'} . ' ' . $data{'year'} );
      $e_date   = &UnixDate( $date, "%d/%m/%Y" );
      $iso_date = &UnixDate( $date, "%Y-%m-%dT" );

      # a changer eventuellement si LADCP et choix "table driven" dans seaseave
      $BOTL += 1;

      # only for profile number or station
      $data{'PRFL'} = $PRFL;

# print for each value from hash %data with format from hash %format describe in config.ini
      foreach my $key (@header) {
        if ( $key eq 'TE35' ) {
          my $TE35 = defined $sbe35{"$PRFL:$BOTL"} ? $sbe35{"$PRFL:$BOTL"} : 1e36;
          printf XML_BTL_FILE "$format{$key} ", $TE35;
          printf BTL_FILE "$format{$key} ",     $TE35;
        }
        else {
          printf XML_BTL_FILE "$format{$key} ", $data{$key};
          printf BTL_FILE "$format{$key} ",     $data{$key};
        }
      }
      printf XML_BTL_FILE "\n";
      printf BTL_FILE "\n";
    }

    # decode l'heure sur la ligne suivante
    if (/\s+(\d+:\d+:\d+)/) {
      ($time) = /\s+(\d+:\d+:\d+)/;
      $date = &ParseDate($time);
      $e_time = &UnixDate( $date, "%H:%M:%S" );
      if ( $BOTL >= 1 ) {
        printf ODV_FILE "%s\t%05d\t%s\t%s\t%8.4f\t%7.4f",
          $cycle_mesure, $PRFL, $type_odv,
          $iso_date . &UnixDate( $date, "%H:%M:%S" ),
          $long_pos, $lat_pos;
        printf ODV_FILE ( $bottom_depth > 1e35 ) ? "\t" : "\t%6.1f",
          $bottom_depth;
        printf ODV_FILE "\t$format{'BOTL'}", $data{'BOTL'};
        printf ODV_FILE "\t$format{'PRES'}", $data{'PRES'};
        printf ODV_FILE ( $data{'TE01'} > 1e35 ) ? "\t" : "\t$format{'TE01'}",
          $data{'TE01'};
        printf ODV_FILE ( $data{'TE02'} > 1e35 ) ? "\t" : "\t$format{'TE02'}",
          $data{'TE02'};
        printf ODV_FILE ( $data{'PSA1'} > 1e35 ) ? "\t" : "\t$format{'PSA1'}",
          $data{'PSA1'};
        printf ODV_FILE ( $data{'PSA2'} > 1e35 ) ? "\t" : "\t$format{'PSA2'}",
          $data{'PSA2'};
        printf ODV_FILE "\t$format{'DO21'}", $data{'DO21'};
        printf ODV_FILE "\t$format{'DO22'}", $data{'DO22'};
        printf ODV_FILE "\t$format{'FLU2'}", $data{'FLU2'};
        printf ODV_FILE "\t$format{'TUR3'}", $data{'TUR3'};

        #printf ODV_FILE "\t$format{'Potemp090C'}", $data{'Potemp090C'};
        #printf ODV_FILE "\t$format{'Potemp190C'}\n", $data{'Potemp190C'};
      }
    }
  }

  # pour chaque fichier, on garde la date et heure de la derniere bouteille
  # impression a l'ecran
  printf STDERR "%05d  %s  %s %s  %02d%c%05.2f %s  %03d\xB0%05.2f %s  %2d",
    $PRFL,
    $s_date, $e_date, $e_time, $lat_deg, 176, $lat_min, $lat_hemi, $long_deg,
    $long_min, $long_hemi, $BOTL
    if $echo;
  printf STDERR "\n" if not $debug and $echo;

  # impression dans le fichier
  printf HDR_BTL_FILE "%05d %s %s %s %02d%c%05.2f %s %03d%c%05.2f %s  %2d\n",
    $PRFL, $s_date, $e_date, $e_time, $lat_deg, 176, $lat_min, $lat_hemi,
    $long_deg, 176, $long_min, $long_hemi, $BOTL;
}

printf STDERR "\n";

print XML_BTL_FILE "  </DATA>\n";
print XML_BTL_FILE "</OCEANO>\n";

close HDR_BTL_FILE;
close XML_BTL_FILE;
close BTL_FILE;
close ODV_FILE;