#!/usr/bin/env ruby
=begin
= NAME

gpcat - Read a variable in multiple NetCDF files, concatenate and write them to a single netcdf file. 

= DESCRIPTION

((*gpcat*)) is command line tools for read a variable in multiple
  NetCDF files, concatenate and write them to a single netcdf file.

= USAGE

  % gpcat -v VAR [options] FILE1 FILE2...

= OPTIONS

:-h, --help
   print this message. 
:-v var, --variable var
   variable name (required).
:-s sfmt, --slice sfmt
   slice,thinnng  (optional).
:-m axis --mean axis
   mean along axis (optional)
:-d axis --stddev axis
   stddev along axis (optional)
:-e axis --eddy axis
   eddy along axis  (optional)
:-o file, --output file
   output filename (optional). Default output filename is 'gphys.nc'.
                                 

= HISTORY

  2005/05/17  S Takehiro (created)
  2005/08/10  S Takehiro (utilize internal function for printing help message)
  2005/08/21  S Takehiro (global attributes copied to the output file)
  2005/08/23  S Takehiro (common methods to gp* command moved to gpcommon.rb)
  2010/03/10  Y SASAKI (change help block into RD format)
  2014/03/10  S Takehiro (big fix for ruby 1.9)
  2015/02/03  S Takehiro (option --mean, --stddev, --eddy added)

=end

require "numru/gphys"
require "numru/gphys/gpcommon"
include NumRu

require "getoptlong"

#-------------------- Slice parameter analysis --------------------
def parse_slice(arg_slice)

  slice = Hash.new
  thinning = Hash.new
  arg_slice_descr = arg_slice.split(/,/)
#  arg_slice = arg_slice_descr.shift
  arg_slice_descr.each do |s|
    if /(.*)=(.*)/ =~ s
      dimname = $1
      subset = $2
      case subset
      when /(.*):(.*):(.*)/
        slice[dimname] = ($1.to_f)..($2.to_f)
        thinning[dimname] = {0..-1=>$3.to_i}
      when /(.*):(.*)/
        slice[dimname] = ($1.to_f)..($2.to_f)
      else
        slice[dimname] = subset.to_f
      end
    else
      raise "invalid slice format\n\n"
      "slice format: " + SliceFMT
    end
  end
  thinning = nil if thinning.length == 0
  [slice, thinning]
end

#------------------------ Default Settings ------------------------
Output_default = 'gphys.nc'
SliceFMT = "dimname=pos1[:pos2[:thinning_intv]][,dimname=...]]"

#---------------------- Option Configuration ----------------------
parser = GetoptLong.new(
  ["--variable", "-v", GetoptLong::REQUIRED_ARGUMENT],
  ["--output",   "-o", GetoptLong::REQUIRED_ARGUMENT],
  ["--slice",    "-s", GetoptLong::REQUIRED_ARGUMENT],
  ["--mean",     "-m", GetoptLong::REQUIRED_ARGUMENT],
  ["--stddev",   "-d", GetoptLong::REQUIRED_ARGUMENT],
  ["--eddy",     "-e", GetoptLong::REQUIRED_ARGUMENT],
  ["--help",     "-h", GetoptLong::NO_ARGUMENT      ])
begin
  parser.each{|opt, arg|
    case opt
    when "--variable" then eval "$OPT_var='#{arg}'"
    when "--output" then eval "$OPT_output='#{arg}'"
    when "--slice" then eval "$OPT_slice='#{arg}'"
    when "--mean" then eval "$OPT_mean='#{arg}'"
    when "--stddiv" then eval "$OPT_stddev='#{arg}'"
    when "--eddy" then eval "$OPT_eddy='#{arg}'"
    when "--help" then eval "$HELP=true"
    else
      raise "must not happen"
    end
  }
  rescue GetoptLong::AmbigousOption, GetoptLong::InvalidOption,
          GetoptLong::MissingArgument, 
          GetoptLong::NeedlessArgument => err
    help
    $srderr.puts err.message
    exit 1
end

#------------------------ Help message ------------------------
if $HELP then
  help
  exit(0)
end  

#------------------------ Option check ------------------------
unless $OPT_var then 
  raise "Variable must be set with '-v' or '--var' option."
end

unless $OPT_output then 
  $OPT_output = Output_default
end

slice, thinning = parse_slice($OPT_slice) if $OPT_slice

#------------------------ Output file check  --------------------------
raise "#{$OPT_output} already exists." if FileTest.exist?($OPT_output) 
outncfile=NetCDF.create($OPT_output)

#------------------------ Open gphys variable ------------------------
# Copy global attributes (only for NetCDF file)
gphys = GPhys::IO.open(ARGV[0],$OPT_var)
outncfile.copy_global_att(gphys) # Copy global attributes (only for NetCDF)

case ARGV.length
when 1 then
  gphys = GPhys::IO.open(ARGV[0],$OPT_var)
  print "#{$0}: Gphys variable '#{$OPT_var}' in NetCDF file, " + ARGV[0] +", was opened successfully.\n"
else
  gphys = GPhys::IO.open(ARGV, $OPT_var)
  print "#{$0}: GPhys variable '#{$OPT_var}' in NetCDF files, " + ARGV.join(', ') +", was opened successfully.\n"
end


#------------------- Slice/thinning gphys variable --------------------
gphys = gphys.cut(slice) if slice
gphys = gphys[thinning]  if thinning

#------------------- mean/eddy gphys variable --------------------
## mean along any axis
if ($OPT_mean)
  dims_mean = ($OPT_mean).split(/\s*,\s*/)
  dims_mean = dims_mean.map{|dim| dim.to_i.to_s == dim ? dim.to_i : dim}
  dims_mean.each{|dim|
    gphys = gphys.mean(dim)
  }
end

## standard deviation along any axis
if ($OPT_stddev)
  dims_stddev = ($OPT_stddev).split(/\s*,\s*/)
  dims_stddev = dims_stddev.map{|dim| dim.to_i.to_s == dim ? dim.to_i : dim}
  dims_stddev.each{|dim|
    gphys = gphys.stddev(dim)
  }
end
 
## deviation from mean along any axis
if ($OPT_eddy)
  dims_eddy = ($OPT_eddy).split(/\s*,\s*/)
  dims_eddy = dims_eddy.map{|dim| dim.to_i.to_s == dim ? dim.to_i : dim}
  dims_eddy.each{|dim|
    gphys = gphys.eddy(dim)
  }
end

#---------------------- Output GPhys variable  ------------------------
GPhys::IO.write( outncfile, gphys )
NetCDF_Conventions.add_history(outncfile, File.basename($0)+" "+ARGV[0])
outncfile.close

print File.basename($0)+": GPhys variable '#{$OPT_var}' in " + ARGV.join(', ') + ", is written to #{$OPT_output}.\n"
