#!/usr/bin/env perl
#=======================================================================
#
#  This is a script to read the CLM namelist XML file
#
# Usage:
#
# queryDefaultNamelist.pl [options]
#
# To get help on options and usage:
#
# queryDefaultNamelist.pl -help
#
#=======================================================================

use Cwd;
use strict;
#use diagnostics;
use Getopt::Long;
use English;

#-----------------------------------------------------------------------------------------------

#Figure out where configure directory is and where can use the XML/Lite module from
my $ProgName;
($ProgName = $PROGRAM_NAME) =~ s!(.*)/!!; # name of program
my $ProgDir = $1;                         # name of directory where program lives

my $cwd = getcwd();  # current working directory
my $cfgdir;

if ($ProgDir) { $cfgdir = $ProgDir; }
else { $cfgdir = $cwd; }

#-----------------------------------------------------------------------------------------------
# Add $cfgdir to the list of paths that Perl searches for modules
my @dirs = ( $cfgdir, "$cfgdir/../../../cime/utils/perl5lib" );
unshift @INC, @dirs;
my $result = eval "require XML::Lite";
if ( ! defined($result) ) {
   die <<"EOF";
** Cannot find perl module \"XML/Lite.pm\" from directories: @dirs **
EOF
}
require Build::Config;
require Build::NamelistDefinition;
require queryDefaultXML;

# Defaults
my $namelist = "clm_inparm";
my $config = "config_cache.xml";


sub usage {
    die <<EOF;
SYNOPSIS
     $ProgName [options]

     query default namelist values.
OPTIONS
     -config "file"                       CLM build configuration file created by configure.
     -cesm                                CESM mode set csmdata to \$DIN_LOC_ROOT.
     -usrname "name"                      Dataset resolution/descriptor for personal datasets.
                                          Default : not used
                                          Example: 1x1pt_boulderCO to describe location,
                                          number of pts
     -csmdata "dir"                       Directory for head of csm inputdata.
     -demand                              Demand that something is returned.
     -filenameonly                        Only return the filename -- not the full path to it.
     -help  [or -h]                       Display this help.
     -justvalue                           Just display the values (NOT key = value).
     -var "varname"                       Variable name to match. Use "-var list" to
                                          list valid variable names from all namelists.
     -namelist "namelistname"             Namelist name to read in (by default $namelist).
     -onlyfiles                           Only output filenames.
     -options "item=value,item2=value2"   Set options to query for when matching.
                                          (comma delimited, with equality to set value).
     -phys "CLM-version" [or -p]          CLM version to use (clm4_0 or clm4_5)
     -res  "resolution"                   Resolution to use for files. Use "-res list" to
                                          list all valid resolutions. Use "-res any" to
                                          use any valid resolution.
     -silent [or -s]                      Do not do any extra printing.
     -test   [or -t]                      Test that files exists.
EXAMPLES

  To list all fsurdat files that match the resolution: 10x15:

  $ProgName -var "fsurdat" -res 10x15

  To only list files that match T42 resolution (or are for all resolutions)

  $ProgName  -onlyfiles  -res 64x128

  To test that all of the files exist on disk under a different default inputdata

  $ProgName  -onlyfiles -test -csmdata /spin/proj/ccsm/inputdata

  To query for namelist items that match particular configurations

  $ProgName  -namelist seq_infodata_inparm -options sim_year=2000,bgc=cn

  Only lists namelist items in the seqinfodata_inparm namelist with options for
  sim_year=2000 and BGC=cn.

EOF
}

#-----------------------------------------------------------------------------------------------

  my %opts = (
               namelist   => $namelist,
               model      => "clm4_5",
               var        => undef,
               hgrid      => undef,
               config     => undef,
               cesm       => undef,
               csmdata    => undef,
               demand     => undef,
               test       => undef,
               onlyfiles  => undef,
               fileonly   => undef,
               silent     => undef,
               usrname    => undef,
               help       => undef,
               options    => undef,
             );

  my $cmdline = "@ARGV";
  GetOptions(
        "f|file=s"     => \$opts{'file'},
        "n|namelist=s" => \$opts{'namelist'},
        "v|var=s"      => \$opts{'var'},
        "p|phys=s"     => \$opts{'model'},
        "r|res=s"      => \$opts{'hgrid'},
        "config=s"     => \$opts{'config'},
        "cesm"         => \$opts{'cesm'},
        "csmdata=s"    => \$opts{'csmdata'},
        "demand"       => \$opts{'demand'},
        "options=s"    => \$opts{'options'},
        "t|test"       => \$opts{'test'},
        "onlyfiles"    => \$opts{'onlyfiles'},
        "filenameonly" => \$opts{'fileonly'},
        "justvalues"   => \$opts{'justvalues'},
        "usrname=s"    => \$opts{'usrname'},
        "s|silent"     => \$opts{'silent'},
        "h|elp"        => \$opts{'help'},
  ) or usage();

  # Check for unparsed arguments
  if (@ARGV) {
      print "ERROR: unrecognized arguments: @ARGV\n";
      usage();
  }
  if ( $opts{'help'} ) {
      usage();
  }
  # Set if should do extra printing or not (if silent mode is not set)
  my $printing = 1;
  if ( defined($opts{'silent'}) ) {
      $printing = 0;
  }
  # Get list of options from command-line into the settings hash
  my %settings;
  if ( defined($opts{'options'}) ) {
     $opts{'options'} =~ s/\s//g;    # Remove all white-space in options
     my @optionlist = split( ",", $opts{'options'} );
     foreach my $item ( @optionlist ) {
        my ($key,$value) = split( "=", $item );
        $settings{$key} = $value;
     }
  }
  my $csmdata = "";
  if ( defined($opts{'fileonly'}) ) {
     if ( ! defined($opts{'justvalues'}) ) { print "When -filenameonly option used, -justvalues is set as well\n" if $printing; }
     if ( ! defined($opts{'onlyfiles'}) )  { print "When -filenameonly option used, -onlyfiles is set as well\n"  if $printing; }
     $opts{'justvalues'} = 1;
     $opts{'onlyfiles'}  = 1;
  }
  # List of input options
  my %inputopts;
  # This namelist files under the cime directories are in version 2 format and can't be read by perl code EBK 11/15/2016
  my $model                  = $opts{'model'};
  my @nl_definition_files    = ("$cfgdir/namelist_files/namelist_definition_drv.xml",
                                "$cfgdir/namelist_files/namelist_definition_$model.xml"
                               );
  $inputopts{empty_cfg_file} = "$cfgdir/config_files/config_definition_$model.xml";
  $inputopts{nldef_files}    = \@nl_definition_files;
  $inputopts{namelist}       = $opts{namelist};
  $inputopts{printing}       = $printing;
  $inputopts{cfgdir}         = $cfgdir;
  $inputopts{ProgName}       = $ProgName;
  $inputopts{cmdline}        = $cmdline;

  my $exitearly = 0;
  my $definition = Build::NamelistDefinition->new( $nl_definition_files[0] );
  foreach my $nl_defin_file ( @nl_definition_files ) {
     if ( ! -f "$nl_defin_file" ) {
        die "($ProgName $cmdline) ERROR:: bad namelist definition filename: $nl_defin_file.\n";
     }
     $definition->add( "$nl_defin_file" );
  }

  if ( ! defined($opts{csmdata}) ) {
     $inputopts{csmdata} = "default";
  } else {
     $inputopts{csmdata} = $opts{csmdata};
  }
  if ( defined($opts{cesm}) ) {
     $inputopts{csmdata} = '$DIN_LOC_ROOT';
  }
  if ( ! defined($opts{config}) ) {
     $inputopts{config} = "noconfig";
  } else {
     $inputopts{config} = $opts{config};
  }
  if (      ! defined($opts{var}) ) {
    $settings{'var'}  = undef;
  } elsif ( $opts{var} eq "list" ) {
     print "Valid variables: " if $printing;
     my @vars = $definition->get_var_names( );
     print "@vars\n";
     $exitearly = 1;
  } else {
    $settings{'var'}  = $opts{'var'};
  }
  if (      ! defined($opts{hgrid}) ) {
     $inputopts{hgrid} = "any";
  } elsif ( $opts{hgrid} eq "list" ) {
     print "Valid resolutions: " if $printing;
     my @hgrids = $definition->get_valid_values( "res", 'noquotes'=>1 );
     print "@hgrids\n";
     $exitearly = 1;
  } else {
     if ( ! $definition->is_valid_value( "res", $opts{hgrid}, 'noquotes'=>1 ) ) {
        if ( $opts{'hgrid'} ne $opts{'usrname'} ) {
           die "($ProgName $cmdline) ERROR:: invalid resolution entered.\n";
        }
     }
     $inputopts{hgrid} = $opts{hgrid};
  }
  # The namelist defaults file contains default values for all required namelist variables.
  my @nl_defaults_files = ( "$cfgdir/namelist_files/namelist_defaults_overall.xml" );
  if ( defined($opts{'usrname'}) ) {
     my $nl_defaults_file =  "$cfgdir/namelist_files/namelist_defaults_usr_files.xml";
     push( @nl_defaults_files, $nl_defaults_file );
     $settings{'clm_usr_name'} = $opts{'usrname'};
     $settings{'notest'}       = ! $opts{'test'};
     $settings{'csmdata'}      = $inputopts{csmdata};
  } else {
     my @files = ( "$cfgdir/namelist_files/namelist_defaults_${model}.xml",
                   "$cfgdir/namelist_files/namelist_defaults_${model}_tools.xml",
                   "$cfgdir/namelist_files/namelist_defaults_drv.xml",
                   "$cfgdir/namelist_files/namelist_defaults_drydep.xml",
                 );
     push( @nl_defaults_files, @files );
  }
  if ( ! $exitearly ) {
    $inputopts{files} = \@nl_defaults_files;

    my $defaults_ref = &queryDefaultXML::ReadDefaultXMLFile( \%inputopts, \%settings );
    my %defaults = %$defaults_ref;
    my @keys = keys(%defaults);
    if ( defined($opts{'demand'}) && ($#keys == -1) ) {
       die "($ProgName $cmdline) ERROR:: demand option is set and nothing was found.\n";
    }
    my $print;
    foreach my $var ( @keys ) {
       $print = 1;
       my $value   = $defaults{$var}{value};
       my $isadir  = $defaults{$var}{isdir};
       my $isafile = $defaults{$var}{isfile};
       my $isastr  = $defaults{$var}{isstr};
       # If onlyfiles option set do NOT print if is NOT a file
       if ( defined($opts{'onlyfiles'}) && (! $isafile) ) {
          $print = undef;
       }
       # If is a directory
       if ( $isadir ) {
          # Test that this directory exists
          if ( defined($opts{'test'})  && defined($print) ) {
             print "Test that directory $value exists\n" if $printing;
             if ( ! -d "$value" ) {
                die "($ProgName) ERROR:: directory $value does NOT exist!\n";
             }
          }
       }
       # If is a file
       if ( $isafile ) {
          # Test that this file exists
          if ( defined($opts{'test'})  && defined($print) ) {
             chomp( $value );
             print "Test that file $value exists\n" if $printing;
             if ( ! -f "$value" ) {
                die "($ProgName) ERROR:: file $value does NOT exist!\n";
             }
          }
       }
       # If a string
       if ( (! defined($opts{'justvalues'}) ) && ($isastr) ) {
         $value = "\'$value\'";
       }
       # if you just want the filename -- not the full path with the directory
       if ( defined($opts{'fileonly'}) ) {
         $value =~ s!(.*)/!!;
       }
       if ( defined($print) ) {
          if ( ! defined($opts{'justvalues'})  ) {
             print "$var = ";
          }
          print "$value\n";
       }
    }
  }
  if ( $printing && defined($opts{'test'}) ) {
     print "\n\nTesting was successful\n\n"
  }

