#!/usr/bin/perl -w

#------------------------------------------------------------------------------
#                  GEOS-Chem Global Chemical Transport Model                  #
#------------------------------------------------------------------------------
#BOP
#
# !MODULE: ncCodeRead
#
# !DESCRIPTION: This Perl script automatically creates a Fortran subroutine
#  that reads data and attributes from a netCDF file.  The Fortran subroutine 
#  (named READ\_FROM\_NETCDF\_FILE) contains calls to the proper NcdfUtilities 
#  library routines.
#\\
#\\
# !USES:
#
  require 5.003;                        # Need this version of Perl or newer
  use English;                          # Use English language
  use Carp;                             # Get detailed error messages
  use strict 'refs';                    # Do not allow symbolic references
  use strict 'subs';                    # Treat all barewords as syntax errors 
  use StrTrim qw( &trim 
                  &splitLine 
                  &extractFile );       # Get string handling routines
#
# !PRIVATE MEMBER FUNCTIONS:
#  &readRcFile($)
#  &writeFortranVars($@)
#  &writeFortranCalls($@)
#
# !PUBLIC MEMBER FUNCTIONS:
#  &main()
#
# !PUBLIC DATA MEMBERS:
#
  $F_ID   = "";                         # netCDF file ID
  %F_DIMS = ();                         # Hash to store Fortran dim values
#
# !CALLING SEQUENCE:
#  ncCodeCreate RESOURCE-FILE-NAME
#
# !REMARKS:
#  Some hand-editing of the output Fortran subroutine may be necessary.
#
# !REVISION HISTORY: 
#  30 Jan 2012 - R. Yantosca - Initial version
#  31 Jan 2012 - R. Yantosca - Minor edits for consistency
#  07 Mar 2012 - R. Yantosca - Minor fix, ignore comment lines
#  26 Mar 2012 - R. Yantosca - Now echo info about the file I/O to stdout
#EOP
#------------------------------------------------------------------------------
#                  GEOS-Chem Global Chemical Transport Model                  #
#------------------------------------------------------------------------------
#BOP
#
# !IROUTINE: readRcFile
#
# !DESCRIPTION: Routine readRcFile reads the resource file which defines
#  the variables and attributes to be written to the netCDF file.
#\\
#\\
# !INTERFACE:
#
sub readRcFile($) {
#
# !INPUT PARAMETERS:
#
  # $fileName : Input file that describes the netCDF file
  my ( $fileName ) = @_;
#
# !CALLING SEQUENCE:
# &readRcFile( RESOURCE-FILE-NAME );
#
# !REVISION HISTORY:
#  30 Jan 2012 - R. Yantosca - Initial version
#  07 Mar 2012 - R. Yantosca - Minor fix, ignore comment lines
#EOP
#------------------------------------------------------------------------------
#BOC
#
# !LOCAL VARIABLES:
#
  my $cmdFile = "";
  my $line    = "";
  my @lines   = ();
  my $name    = "";

  #--------------------------------------------------
  # Read variable settings from the resource file
  #--------------------------------------------------
  open( I, "<$fileName" ) or die "Cannot open $fileName!\n";
  chomp( @lines = <I> );
  close( I );

  #--------------------------------------------------
  # Write Fortran commands to the output file
  #--------------------------------------------------
  
  # Pre-get a few quantities before creating the
  # output file with the fortran code
  foreach $line ( @lines ) {

    # Skip comment lines
    if ( !( substr( $line, 0, 1 ) eq '#' ) ) {

      # Name of output file w/ Fortran code
      if ( $line =~ 'Fortran Read File' ) {
	( $name, $cmdFile ) = &splitLine( $line, '=' );
      }

      # NetCDF file ID (aka filehandle)
      if ( $line =~ 'netCDF FileHandle' ) {
	( $name, $F_ID ) = &splitLine( $line, '=' );
      }
    }
  }
      
  # Open the file that will ho
  open( O, ">$cmdFile" ) or die "Cannot open $cmdFile\n";

  # Pass thru @lines array so that we can declare Fortran variables
  &writeFortranVars( \*O, @lines );

  # Pass thru @lines array again to write 
  &writeFortranCalls( \*O, @lines );

  #--------------------------------------------------
  # Cleanup and quit
  #--------------------------------------------------

  # Close output file
  close( O );

  # Return
  return( 0 );
}
#EOC
#------------------------------------------------------------------------------
#                  GEOS-Chem Global Chemical Transport Model                  #
#------------------------------------------------------------------------------
#BOP
#
# !IROUTINE: writeFortranVars
#
# !DESCRIPTION: Routine writeFortranVars generates the proper Fortran 
#  variable declarations that are needed for use with the NcdfUtilities 
#  library routines.
#\\
#\\
# !INTERFACE:
#
sub writeFortranVars($@) {
#
# !INPUT PARAMETERS:
#
  # $O     : File handle 
  # @lines : Contents of the resource file
  my ( $O, @lines ) = @_;
#
# !CALLING SEQUENCE:
#  &writeFortranVars( \*O, @lines );
#
# !REVISION HISTORY:
#  30 Jan 2012 - R. Yantosca - Initial version
#  31 Jan 2012 - R. Yantosca - Minor edits for consistency
#  01 Feb 2012 - R. Yantosca - Fix typo in output ProTeX header
#  09 Jul 2012 - R. Yantosca - Now make fId a local variable
#EOP
#------------------------------------------------------------------------------
#BOC
#
# !LOCAL VARIABLES:
#
  my @subStr = ();
  my $name   = "";
  my $value   = "";
  my $varName = "";
  my $varSize = "";
  my $varType = "";
  my $varDim  = "";
  my $nDims   = "";
  my @dims    = ();
  my $dimDef  = "";
  my $txt     = "";

  #-------------------------------------------------------
  # Write USE statements 
  #-------------------------------------------------------
  $txt .= <<EOF;
!EOC
!------------------------------------------------------------------------------
!                  GEOS-Chem Global Chemical Transport Model                  !
!------------------------------------------------------------------------------
!BOP
!
! !IROUTINE: read\_from\_netcdf\_file
!
! !DESCRIPTION: Routine to read variables and attributes from a netCDF
!  file.  This routine was automatically generated by the Perl script
!  NcdfUtilities/perl/ncCodeRead.
!\\\\
!\\\\
! !INTERFACE:
!
      SUBROUTINE READ_FROM_NETCDF_FILE
!
! !USES:
!
      ! Modules for netCDF read
      USE m_netcdf_io_open
      USE m_netcdf_io_get_dimlen
      USE m_netcdf_io_read
      USE m_netcdf_io_readattr
      USE m_netcdf_io_close

      IMPLICIT NONE

\#     include "netcdf.inc"
!
! !REMARKS:
!  Assumes that you have:
!  (1) A netCDF library (either v3 or v4) installed on your system
!  (2) The NcdfUtilities package (from Bob Yantosca) source code
!                                                                             .
!  Although this routine was generated automatically, some further
!  hand-editing may be required (i.e. to  specify the size of parameters, 
!  and/or to assign values to variables.  Also, you can decide how to handle
!  the variable attributes (or delete calls for reading attributes that you
!  do not need).
!
! !REVISION HISTORY:
!  30 Jan 2012 - R. Yantosca - Initial version
!EOP
!------------------------------------------------------------------------------
!BOC
!
! !LOCAL VARIABLES:
!
      INTEGER :: $F_ID    ! netCDF file ID
EOF

  #-------------------------------------------------------
  # Add spacer text to the file
  #-------------------------------------------------------
  $txt .= <<EOF;
      !=================================================================
      ! Variable declarations
      !=================================================================

EOF

  # Loop thru the LINES array
  for ( my $i=0; $i<scalar( @lines ); $i++ ) {

    # Skip separator line
    if ( $lines[$i] eq '#' ) {
      # Do nothing
    }

    #----------------------------------------------------
    # DIMENSIONS section
    #----------------------------------------------------
    elsif ( $lines[$i] =~ '!DIMENSIONS:' ) {

      while ( $lines[++$i] ne '' ) {

	#  Get the dimension name and its value
	( $name, $value ) = &splitLine( $lines[$i], '=' );
				# 
	# Store the value in a hash under its name
	$F_DIMS{$name} = $value;
      }
    }

    #----------------------------------------------------
    # VARIABLES section
    #----------------------------------------------------
    elsif ( $lines[$i] =~ '!VARIABLES:' ) {

      # Add a comment
      $txt .= "      ! Data arrays\n";

      while ( $lines[++$i] ne '' ) {

	# Skip comment characters
	if ( !( $lines[$i] =~ '#' ) ) { 

	  # Split the line 
	  ( $name, $value ) = &splitLine( $lines[$i], '=' );

	  # If the name field does not have a semicolon
	  # then it is a variable name and not an attribute
	  if ( !( $name =~ ':' ) ) { 

	    # Find the variable type and variable dimension(s)
	    ( $varType, $varDim ) = &splitLine( $value, '::' );
	    if ( $varType =~ 'REAL' ) { $varType = "$varType "; }
	    
	    # Get dimension information
	    @dims = split( ',', $varDim );
	    $nDims = scalar( @dims );

	    # Create the Fortran dimension string that is used to declare 
	    # the variable, i.e. (IIPAR,JJPAR,1).  Use the Perl hash %F_DIMS 
	    # to refer to the correponding value for each netCDF dimension.
	    $dimDef = "(";
	    for ( my $i=0; $i<$nDims; $i++ ) { 
	      while( my ( $key, $val ) = each( %F_DIMS ) ) {
		if ( $key =~ $dims[$i] ) {
		  $dimDef .= "$val";
		  if ( $i < $nDims-1 ) { $dimDef .= ","; }
		}
	      }	
	    }			
	    $dimDef .= ")";

	    # Definition string
	    $txt .= "      $varType            :: $name$dimDef\n";
	  }			
	}		      
      }
    }

  }

  #-------------------------------------------------------
  # OTHER VARIABLES section
  #-------------------------------------------------------
  $txt .= <<EOF2;

      ! Character strings
      CHARACTER(LEN=255) :: nc_dir             ! netCDF directory name
      CHARACTER(LEN=255) :: nc_file            ! netCDF file name
      CHARACTER(LEN=255) :: nc_path            ! netCDF path name
      CHARACTER(LEN=255) :: v_name             ! netCDF variable name 
      CHARACTER(LEN=255) :: a_name             ! netCDF attribute name
      CHARACTER(LEN=255) :: a_val              ! netCDF attribute value

      ! Arrays for netCDF start and count values
      INTEGER            :: st1d(1), ct1d(1)   ! For 1D arrays    
      INTEGER            :: st2d(2), ct2d(2)   ! For 2D arrays 
      INTEGER            :: st3d(3), ct3d(3)   ! For 3D arrays 
      INTEGER            :: st4d(4), ct4d(4)   ! For 4D arrays 
      INTEGER            :: st5d(5), ct5d(5)   ! For 5D arrays 
      INTEGER            :: st6d(6), ct6d(6)   ! For 6D arrays 
EOF2
  print $O "$txt\n";

  # Return
  return( 0 );
}
#EOC
#------------------------------------------------------------------------------
#                  GEOS-Chem Global Chemical Transport Model                  #
#------------------------------------------------------------------------------
#BOP
#
# !IROUTINE: writeFortranCalls
#
# !DESCRIPTION: Routine writeFortranCalls generates the proper calls to the
#  NcdfUtilities library routines for reading data from the netCDF file.
#\\
#\\
# !INTERFACE:
#
sub writeFortranCalls($@) {
#
# !INPUT PARAMETERS:
#
  # $O     : File handle 
  # @lines : Contents of the resource file
  my ( $O, @lines ) = @_;
#
# !CALLING SEQUENCE:
#  &writeFortranCalls( \*O, @lines );
#
# !REVISION HISTORY:
#  30 Jan 2012 - R. Yantosca - Initial version
#  31 Jan 2012 - R. Yantosca - Minor edits for consistency
#  26 Mar 2012 - R. Yantosca - Echo info about the file I/O to stdout
#  26 Mar 2012 - R. Yantosca - Now echo info about the file I/O to stdout
#EOP
#------------------------------------------------------------------------------
#BOC
#
# !LOCAL VARIABLES:
#
  my $attName  = "";
  my $varName  = "";
  my $varDim   = "";
  my $varType  = "";
  my $ncDir    = "";
  my $ncFile   = "";
  my $ncPath   = "";
  my $nDims    = "";
  my $start    = "";
  my $count    = "";
  my @dims     = ();
  my $name     = "";
  my $value    = "";
  my $txt      = "";

  # Loop thru each line in the file
  for ( my $i = 0; $i < scalar( @lines ); $i++ ) {
    				
    # Skip separator line
    if ( $lines[$i] eq '#' ) {
      # Do nothing
    }

    #----------------------------------------------------
    # FILENAME section
    #----------------------------------------------------
    elsif ( $lines[$i] =~ '!FILENAME:' ) {

      while ( $lines[++$i] ne '' ) {

	if ( $lines[$i] =~ 'netCDF FileName' ) {

	  # Split the line on the equals sign
	  ( $name, $value ) = &splitLine( $lines[$i], '=' );

	  # Full path name of file to read
	  $ncPath = $value;    

	  # Split path into filename & directory parts
	  ( $ncFile, $ncDir ) = &extractFile( $ncPath );

	  # Create string
	  $txt .= <<EOF;
      !=================================================================
      ! Open and read data from the netCDF file
      !=================================================================

      ! Open netCDF file
      nc_path = '$ncPath'
      CALL Ncop_Rd( $F_ID, TRIM(nc_path) )

      ! Echo info to stdout
      nc_file = '$ncFile'  
      nc_dir  = '$ncDir' 
      WRITE( 6, 100 ) REPEAT( '%', 79 )
      WRITE( 6, 110 ) TRIM(nc_file)
      WRITE( 6, 120 ) TRIM(nc_dir)

EOF
        }
      }
    }

    #----------------------------------------------------
    # VARIABLES section
    #----------------------------------------------------
    elsif ( $lines[$i] =~ '!VARIABLES:' ) {

      # Write fortran calls to define variables
      while ( $lines[++$i] ne '' ) {
	if ( !( $lines[$i] =~ '#' ) ) { 

	  # Split the line on the equals sign
	  ( $name, $value ) = &splitLine( $lines[$i], '=' );

          #----------------------------------------------
	  # If the $name field has a semicolon, 
	  # then it's a variable attribute
	  #----------------------------------------------
	  if ( $name =~ ':' ) {

	    # Split into the variable name and attribute name
	    ( $varName, $attName ) = &splitLine( $name, ':' );

	    # Create the text
	    $txt .= <<EOF2;
      ! Read the $varName:$attName attribute
      a_name = "$attName"
      CALL NcGet_Var_Attributes( $F_ID,TRIM(v_name),TRIM(a_name),a_val )

EOF2
	    # Write info about the variable we are reading to stdout
            if ( $attName =~ 'units' ) {
	      $txt .= <<EOF2a;
      ! Echo info to stdout
      WRITE( 6, 130 ) TRIM(v_name), TRIM(a_val)

EOF2a
            }

	  }

          #--------------------------------------------
          # If the $name field lacks a semicolon, 
          # then it's the variable name
	  #--------------------------------------------
          else {

	    # Add spacer text
	    $txt  .= "      !----------------------------------------\n";
	    $txt  .= "      ! VARIABLE: $name\n";
	    $txt  .= "      !----------------------------------------\n\n";

	    # Find the variable type and variable dimension(s)
	    ( $varType, $varDim ) = &splitLine( $value, '::' );

	    # Get dimension information
	    @dims = split( ',', $varDim );
	    $nDims = scalar( @dims );

	    # Write the variable name
	    $txt  .= <<EOF3;
      ! Variable name
      v_name = "$name"

EOF3

	    # Create the start array
	    $start = "st$nDims"."d";
	    $txt  .= "      ! Read $name from file\n";
	    $txt  .= "      $start   = (/ ";
	    for ( my $j=0; $j<$nDims; $j++ ) { 
	      $txt .= "1";
	      if ( $j < $nDims-1 ) { $txt .= ", "; }
	    }
	    $txt  .= " /)\n";

	    # Create the count array
	    $count = "ct$nDims"."d";
	    $txt  .= "      $count   = (/ ";
	    for ( my $j=0; $j<$nDims; $j++ ) { 
	      while( my( $key, $val ) = each( %F_DIMS ) ) {
		if ( $key =~ $dims[$j] ) {
		  $txt .= "$val";
		  if ( $j < $nDims-1 ) { $txt .= ", "; }
		}
	      }
	    }
	    $txt  .= " /)\n";

	    # Create the call to NcWr
	    $txt  .= <<EOF4;
      CALL NcRd( $name, $F_ID, TRIM(v_name), $start, $count )

EOF4
          }
	}
      }
    }
  }

  #------------------------------------------------------
  # Cleanup and quit
  #------------------------------------------------------

  # Closing text
  $txt .= <<EOF5;
      !=================================================================
      ! Cleanup and quit
      !=================================================================

      ! Close netCDF file
      CALL NcCl( $F_ID )

      ! Echo info to stdout
      WRITE( 6, 140 )
      WRITE( 6, 100 ) REPEAT( '%', 79 )

      ! FORMAT statements
 100  FORMAT( a                                              )
 110  FORMAT( '%% Opening file  : ',         a               )
 120  FORMAT( '%%  in directory : ',         a, / , '%%'     )
 130  FORMAT( '%% Successfully read ',       a, ' [', a, ']' )
 140  FORMAT( '%% Successfully closed file!'                 )

      END SUBROUTINE READ_FROM_NETCDF_FILE
!EOC
EOF5

  # Write Fortran cmds to file  
  print $O "$txt\n";	      

  # Return 
  return( 0 );
}
#EOC
#------------------------------------------------------------------------------
#                  GEOS-Chem Global Chemical Transport Model                  #
#------------------------------------------------------------------------------
#BOP
#
# !IROUTINE: main
#
# !DESCRIPTION: Routine main is the driver routine for the ncCodeRead script.
#\\
#\\
# !INTERFACE:
#
sub main() {
#
# !CALLING SEQUENCE:
#  &main();
#
# !REVISION HISTORY:
#  30 Jan 2012 - R. Yantosca - Initial version
#EOP
#------------------------------------------------------------------------------
#BOC

  # Error check arguments
  if ( scalar( @ARGV ) == 0 ) { 
    print "Usage: ncCodeRead RESOURCE-FILE\n"; 
    exit(1);
  }

  # Read settings and create output files
  &readRcFile( $ARGV[0] );

  # Return normally
  return( 0 );
}
#EOC

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

# Start main program
main();

# Exit normally
exit(0);
