!------ Routines for defining and obtaining info about dataset variables ------

! Replacement for fort-genvar.c

! Written by: Richard Weed, Ph.D.
!             Center for Advanced Vehicular Systems 
!             Mississippi State University
!             rweed@cavs.msstate.edu


! License (and other Lawyer Language)
 
! This software is released under the Apache 2.0 Open Source License. The
! full text of the License can be viewed at :
!
!   http:www.apache.org/licenses/LICENSE-2.0.html
!
! The author grants to the University Corporation for Atmospheric Research
! (UCAR), Boulder, CO, USA the right to revise and extend the software
! without restriction. However, the author retains all copyrights and
! intellectual property rights explicitly stated in or implied by the
! Apache license

! Version 1.: Sept. 2005 - Initial Cray X1 version
! Version 2.: May   2006 - Updated to support g95
! Version 3.: April 2009 - Updated for netCDF 4.0.1
! Version 4.: April 2010 - Updated for netCDF 4.1.1
! Version 5.: May   2014 - Ensure return error status checked from C API calls
! Version 6.: Jan.  2016 - General code cleanup. Replaced automatic arrays
!                          sized with NC_MAX_DIMS with allocatable arrays.
!                          Change name processing to reflect change in
!                          addCNullChar

!-------------------------------- nf_def_var -------------------------------
 Function nf_def_var(ncid, name, xtype, nvdims, vdims, varid) RESULT (status)

! Define name, datatype, number of dimensions, and dimension ids for a
! dataset variable. Returns variable id

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN)  :: ncid, xtype, nvdims
 Integer,          Intent(IN)  :: vdims(*)
 Integer,          Intent(OUT) :: varid
 Character(LEN=*), Intent(IN)  :: name

 Integer                       :: status

 Integer(C_INT)               :: cncid, cnvdims, cvarid, cstatus, cxtype
 Character(LEN=(LEN(name)+1)) :: cname
 Integer                      :: ie

 Integer(C_INT), ALLOCATABLE :: cvdims(:)

 cncid   = ncid
 cnvdims = nvdims
 cxtype  = xtype
 
! Check if a C NULL character was appended to name in calling routine

 cname = addCNullChar(name, ie)

! Flip dimids to C order and subtract -1 to yield C ids prior
! to calling nc_def_var. Replaces C utility f2c_dimids

 If (nvdims > 0) Then
   ALLOCATE(cvdims(nvdims))
   cvdims(1:nvdims) = vdims(nvdims:1:-1)-1
 Else
   ALLOCATE(cvdims(1))
   cvdims(1) = 0
 EndIf

 cstatus = nc_def_var(cncid, cname(1:ie), cxtype, cnvdims, cvdims, &  
                      cvarid)

 If (cstatus == NC_NOERR) Then
    ! Add one to returned C varid to yield FORTRAN id
      varid = cvarid + 1
 EndIf

 status = cstatus
 
 If (ALLOCATED(cvdims)) DEALLOCATE(cvdims)

 End Function nf_def_var
!-------------------------------- nf_inq_varndims --------------------------
 Function nf_inq_varndims(ncid, varid, vndims) RESULT (status)

! Get variable dimension for a given varid from NetCDF dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer, Intent(IN)  :: ncid, varid
 Integer, Intent(OUT) :: vndims

 Integer              :: status

 Integer(C_INT) :: cncid, cvarid, cvndims, cstatus

 cncid   = ncid
 cvarid  = varid - 1

 cstatus = nc_inq_varndims(cncid, cvarid, cvndims)

 If (cstatus == NC_NOERR) Then
    vndims = cvndims
 EndIf
 status = cstatus

 End Function nf_inq_varndims
!-------------------------------- nf_inq_var ----------------------------
 Function nf_inq_var(ncid, varid, name, xtype, ndims, dimids, natts) &
                     RESULT (status)

! Get variable name, data type, dimension length, dimension ids, and
! number of attributes for a given varid 

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN)  :: ncid, varid
 Character(LEN=*), Intent(OUT) :: name
 Integer,          Intent(OUT) :: dimids(*)
 Integer,          Intent(OUT) :: ndims, xtype, natts

 Integer                       :: status

 Integer(C_INT)               :: cncid, cstatus, cndims, cvarid, &
                                 cnatts, cstat1
 Integer(C_INT)               :: cxtype
 Character(LEN=NC_MAX_NAME+1) :: tmpname
 Integer                      :: nlen

 Integer(C_INT), ALLOCATABLE :: cdimids(:)

 cncid  = ncid
 cvarid = varid - 1 ! subtract -1 to yield cvarid

 nlen    = LEN(name)
 tmpname = REPEAT(" ", LEN(tmpname))
 name    = REPEAT(" ", nlen)

 cndims    = 0
 dimids(1) = 0
 xtype     = 0
 natts     = 0
 ndims     = 0

 cstat1  = nc_inq_varndims(cncid, cvarid, cndims)
 status  = cstat1

 If (cndims > 0) Then
   ALLOCATE(cdimids(cndims))
 Else
   ALLOCATE(cdimids(1))
 EndIf

 cdimids=0
 
 cstatus = nc_inq_var(cncid, cvarid, tmpname, cxtype, cndims, cdimids, cnatts)

 If (cstatus == NC_NOERR) Then
   xtype = cxtype
   natts = cnatts
   ndims = cndims

! Check tmpname for a C null character and strip it and trailing blanks

   name = stripCNullChar(tmpname, nlen)

! Reverse order of cdimids and add one to yield FORTRAN id numbers
! Replaces c2f_dimids C utility
 
   If (ndims > 0) Then
     dimids(1:ndims) = cdimids(ndims:1:-1)+1
   EndIf
 EndIf

 status = cstatus

 If (ALLOCATED(cdimids)) DEALLOCATE(cdimids)

 End Function nf_inq_var
!-------------------------------- nf_inq_vardimid -----------------------
 Function nf_inq_vardimid(ncid, varid, dimids) RESULT (status)

! Get variable dimension id for a dimension name

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer, Intent(IN)  :: ncid, varid
 Integer, Intent(OUT) :: dimids(*)

 Integer              :: status

 Integer(C_INT) :: cncid, cstatus, cstat2, cndims, cvarid
 Integer        :: ndims

 Integer(C_INT), ALLOCATABLE :: cvdimids(:)

 cncid     = ncid
 cvarid    = varid - 1 ! subtract -1 to get C id number
 dimids(1) = 0
 cndims    = 0
 ndims     = 0
 
 cstat2  = nc_inq_varndims(cncid, cvarid, cndims)

 If (cndims > 0) Then
   ALLOCATE(cvdimids(cndims))
 Else
   ALLOCATE(cvdimids(1))
 EndIf

 cvdimids = 0

 cstatus = nc_inq_vardimid(cncid, cvarid, cvdimids)

! Reverse order of cdimids and add 1 to yield FORTRAN id numbers
! Replaces c2f_dimids C utility
 
 If (cstatus == NC_NOERR) Then
   ndims = cndims
   If (ndims > 0) Then    
     dimids(1:ndims) = cvdimids(ndims:1:-1)+1
   EndIf
 Else
   ndims = 0
 EndIf
 
 status = cstatus

 If (ALLOCATED(cvdimids)) DEALLOCATE(cvdimids)

 End Function nf_inq_vardimid
!-------------------------------- nf_inq_varid --------------------------
 Function nf_inq_varid(ncid, name, varid) RESULT (status)

! Get variable id for a variable name from NetCDF dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN)  :: ncid
 Character(LEN=*), Intent(IN)  :: name
 Integer,          Intent(OUT) :: varid

 Integer                       :: status

 Integer(C_INT)               :: cncid, cvarid, cstatus
 Character(LEN=(LEN(name)+1)) :: cname
 Integer                      :: ie

 cncid = ncid

! Check name for a C NULL character added in calling routine

 cname = addCNullChar(name, ie)

 cstatus = nc_inq_varid(cncid, cname(1:ie), cvarid)

 If (cstatus == NC_NOERR) Then
    varid  = cvarid + 1  ! add one to get Fortran id number
 EndIf
 status = cstatus

 End Function nf_inq_varid
!-------------------------------- nf_inq_varname ------------------------
 Function nf_inq_varname (ncid, varid, name) RESULT (status)

! Get variable name for a given varid from NetCDF dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN)   :: ncid, varid
 Character(LEN=*), Intent(OUT)  :: name

 Integer                        :: status

 Integer(C_INT)               :: cncid, cvarid, cstatus
 Character(LEN=NC_MAX_NAME+1) :: tmpname 
 Integer                      :: nlen

 cncid   = ncid
 cvarid  = varid - 1 ! subtract one to get C id number

 nlen    = LEN(name)
 tmpname = REPEAT(" ", LEN(tmpname))
 name    = REPEAT(" ", nlen)

! Get tmpname from C interface

 cstatus = nc_inq_varname(cncid, cvarid, tmpname)

 ! Find first C null character in tmpname if present and set end of string

 If (cstatus == NC_NOERR) Then
    name = stripCNullChar(tmpname, nlen)
 EndIf
 status = cstatus

 End Function nf_inq_varname
!-------------------------------- nf_inq_vartype -------------------------
 Function nf_inq_vartype(ncid, varid, xtype) RESULT(status)

! Inquire about numeric type of variable attributes for NetCDF file id ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer, Intent(IN)  :: ncid, varid
 Integer, Intent(OUT) :: xtype

 Integer              :: status

 Integer(C_INT) :: cncid, cvarid, cstatus
 Integer(C_INT) :: cxtype

 cncid  = ncid
 cvarid = varid - 1 ! Subtract one to get C id number

 cstatus = nc_inq_vartype(cncid, cvarid, cxtype)

 If (cstatus == NC_NOERR) Then
    xtype  = cxtype
 EndIf
 status = cstatus

 End Function nf_inq_vartype
!-------------------------------- nf_inq_varnatts -----------------------
 Function nf_inq_varnatts(ncid, varid, nvatts) RESULT(status)

! Inquire about number of variable attributes for NetCDF file id ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer, Intent(IN)  :: ncid, varid
 Integer, Intent(OUT) :: nvatts

 Integer              :: status

 Integer(C_INT) :: cncid, cvarid, cnvatts, cstatus

 cncid  = ncid
 cvarid = varid - 1 ! subtract one to get C id number

 cstatus = nc_inq_varnatts(cncid, cvarid, cnvatts)

 If (cstatus == NC_NOERR) Then
    nvatts = cnvatts
 EndIf

 status = cstatus

 End Function nf_inq_varnatts
!-------------------------------- nf_rename_var -------------------------
 Function nf_rename_var(ncid, varid, name) RESULT (status)

! Rename dimension name for a given dimension id

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN) :: ncid, varid 
 Character(LEN=*), Intent(IN) :: name

 Integer                      :: status

 Integer(C_INT)               :: cncid, cvarid, cstatus
 Character(LEN=(LEN(name)+1)) :: cname
 Integer                      :: ie

 cncid  = ncid
 cvarid = varid - 1 ! Subtract one to get C id number

! Check name for a C NULL character added in calling routine

 cname = addCNullChar(name, ie)

 cstatus = nc_rename_var(cncid, cvarid, cname(1:ie))

 status = cstatus

 End Function nf_rename_var
!-------------------------------- nf_copy_var ---------------------------
 Function nf_copy_var(ncid_in, varid, ncid_out) RESULT(status)

! Copies a given variable from dataset ncid_in to dataset ncid_out

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer, Intent(IN) :: ncid_in, varid, ncid_out
 Integer             :: status

 Integer(C_INT) :: cncidin, cvarid, cncidout, cstatus

 cncidin  = ncid_in
 cncidout = ncid_out
 cvarid   = varid - 1 ! Subtract one to get C id number

 cstatus = nc_copy_var(cncidin, cvarid, cncidout)

 status = cstatus

 End Function nf_copy_var
