!---------- Routines to put/get attribute data of various data types ----------

! Replacement for fort-attio.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 Center 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 to Netcdf 4.0.1 
! Version 4.: April 2010 - Updated to Netcdf 4.1.1 
! Version 5.: Feb.  2013 - bug fixes for fortran 4.4
! Version 6:  Jan.  2016 - General code cleanup. Changed processing of
!                          name strings to reflect change to addCNullChar
          
!--------------------------------- nf_put_att_text ---------------------------
 Function nf_put_att_text(ncid, varid, name, nlen, text) RESULT(status)

! Write variable or global attribute text string to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                      :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen

 cname = addCNullChar(name, ie)
 
 cstatus = nc_put_att_text(cncid, cvarid, cname(1:ie), cnlen, &
           text)

 status = cstatus

 End Function nf_put_att_text
!--------------------------------- nf_put_att_text_a ------------------------
 Function nf_put_att_text_a(ncid, varid, name, nlen, text) RESULT(status)

! New routine to support passing an array of single characters
! Write variable or global attribute array of characters to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                      :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen

 cname = addCNullChar(name, ie)
 
 cstatus = nc_put_att_text(cncid, cvarid, cname(1:ie), cnlen, &
                           text)

 status = cstatus

 End Function nf_put_att_text_a
!--------------------------------- nf_put_att_int1 -------------------------
 Function nf_put_att_int1(ncid, varid, name, xtype, nlen, i1vals) &
                          RESULT(status)

! Write variable or global attribute byte data to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN) :: ncid, varid, nlen, xtype
 Character(LEN=*), Intent(IN) :: name
 Integer(NFINT1),  Intent(IN) :: i1vals(*)

 Integer                      :: status

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

 If (C_SIGNED_CHAR < 0) Then ! schar not supported by processor
   status = NC_EBADTYPE
   RETURN
 EndIf
 
 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen
 cxtype = xtype

! Check for C null char on name and add one
 
 cname = addCNullChar(name, ie)

#if NF_INT1_IS_C_SIGNED_CHAR 
 cstatus = nc_put_att_schar(cncid, cvarid, cname(1:ie), &
                           cxtype, cnlen, i1vals) 
#elif NF_INT1_IS_C_SHORT
 cstatus = nc_put_att_short(cncid, cvarid, cname(1:ie), &
                            cxtype, cnlen, i1vals)
#elif NF_INT1_IS_C_INT
 cstatus = nc_put_att_int(cncid, cvarid, cname(1:ie), &
                            cxtype, cnlen, i1vals)
#elif NF_INT1_IS_C_LONG
 cstatus = nc_put_att_long(cncid, cvarid, cname(1:ie), &
                           cxtype, cnlen, i1vals)
#endif
 status = cstatus

 End Function nf_put_att_int1
!--------------------------------- nf_put_att_int2 -------------------------
 Function nf_put_att_int2(ncid, varid, name, xtype, nlen, i2vals) &
                          RESULT(status)

! Write variable or global attribute 16 bit integer data to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN) :: ncid, varid, nlen, xtype
 Character(LEN=*), Intent(IN) :: name
 Integer(NFINT2),  Intent(IN) :: i2vals(*)

 Integer                      :: status

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

 If (C_SHORT < 0) Then ! short not supported by processor
   status = NC_EBADTYPE
   Return
 EndIf
 
 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen
 cxtype = xtype

 cname = addCNullChar(name, ie)

#if NF_INT2_IS_C_SHORT 
 cstatus = nc_put_att_short(cncid, cvarid, cname(1:ie), &
                            cxtype, cnlen, i2vals) 
#elif NF_INT2_IS_C_INT 
 cstatus = nc_put_att_int(cncid, cvarid, cname(1:ie), &
                           cxtype, cnlen, i2vals)
#elif NF_INT2_IS_C_LONG 
 cstatus = nc_put_att_long(cncid, cvarid, cname(1:ie), &
                           cxtype, cnlen, i2vals)
#endif 
 status = cstatus

 End Function nf_put_att_int2
!--------------------------------- nf_put_att_int --------------------------
 Function nf_put_att_int(ncid, varid, name, xtype, nlen, ivals) &
                         RESULT(status)

! Write variable or global attribute default integer data to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN) :: ncid, varid, nlen, xtype
 Character(LEN=*), Intent(IN) :: name
 Integer(NFINT),   Intent(IN) :: ivals(*)

 Integer                      :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen
 cxtype = xtype

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)

#if NF_INT_IS_C_INT 
 cstatus = nc_put_att_int(cncid, cvarid, cname(1:ie), &
                          cxtype, cnlen, ivals) 
#elif NF_INT_IS_C_LONG 
 cstatus = nc_put_att_long(cncid, cvarid, cname(1:ie), &
                          cxtype, cnlen, ivals) 
#endif
 status = cstatus

 End Function nf_put_att_int
!--------------------------------- nf_put_att_int64 -------------------------
 Function nf_put_att_int64(ncid, varid, name, xtype, nlen, i8vals) &
                           RESULT(status)

! Write variable or global attribute 64 bit integer data to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN) :: ncid, varid, nlen, xtype
 Character(LEN=*), Intent(IN) :: name
 Integer(NFINT8),  Intent(IN) :: i8vals(*)

 Integer                      :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen
 cxtype = xtype

 cname = addCNullChar(name, ie)

#if NF_INT8_IS_C_SHORT
 cstatus = nc_put_att_short(cncid, cvarid, cname(1:ie), &
                            cxtype, cnlen, i8vals)
#elif NF_INT8_IS_C_INT
 cstatus = nc_put_att_int(cncid, cvarid, cname(1:ie), &
                           cxtype, cnlen, i8vals)
#elif NF_INT8_IS_C_LONG
 cstatus = nc_put_att_long(cncid, cvarid, cname(1:ie), &
                           cxtype, cnlen, i8vals)
#elif NF_INT8_IS_C_LONG_LONG
 cstatus = nc_put_att_longlong(cncid, cvarid, cname(1:ie), &
                           cxtype, cnlen, i8vals)
#endif
 status = cstatus

 End Function nf_put_att_int64
!--------------------------------- nf_put_att_real -------------------------
 Function nf_put_att_real(ncid, varid, name, xtype, nlen, rvals) &
                          RESULT(status)

! Write variable or global attribute Real(RK4) data to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN) :: ncid, varid, nlen, xtype
 Character(LEN=*), Intent(IN) :: name
 Real(NFREAL),     Intent(IN) :: rvals(*)

 Integer                      :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen
 cxtype = xtype

! Check for C null char and add one if missing
 
 cname = addCNullChar(name, ie)

#if NF_REAL_IS_C_DOUBLE 
 cstatus = nc_put_att_double(cncid, cvarid, cname(1:ie), &
                             cxtype, cnlen, rvals) 
#else
 cstatus = nc_put_att_float(cncid, cvarid, cname(1:ie), &
                            cxtype, cnlen, rvals) 
#endif
 status = cstatus

 End Function nf_put_att_real
!--------------------------------- nf_put_att_double -----------------------
 Function nf_put_att_double(ncid, varid, name, xtype, nlen, dvals) &
                               RESULT(status)

! Write variable or global attribute Real(RK8) to dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,          Intent(IN) :: ncid, varid, nlen, xtype
 Character(LEN=*), Intent(IN) :: name
 Real(RK8),        Intent(IN) :: dvals(*)

 Integer                      :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cnlen  = nlen
 cxtype = xtype

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)
 
 cstatus = nc_put_att_double(cncid, cvarid, cname(1:ie), &
                             cxtype, cnlen, dvals) 

 status = cstatus

 End Function nf_put_att_double
!--------------------------------- nf_put_att --------------------------------
 Function nf_put_att(ncid, varid, name, xtype, nlen, value) RESULT(status)

! Write global attribute of any type. We use a C character
! string as the dummy arguments for the values

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,                Intent(IN)         :: ncid, varid, nlen, xtype
 Character(LEN=*),       Intent(IN)         :: name
 Character(KIND=C_CHAR), Intent(IN), TARGET :: value(*)

 Integer                                    :: status

 Integer(C_INT)               :: cncid, cvarid, cstatus, cxtype
 Integer(C_SIZE_T)            :: cnlen
 Type(C_PTR)                  :: cvalueptr
 Character(LEN=(LEN(name)+1)) :: cname
 Integer                      :: ie

 cncid     = ncid
 cvarid    = varid -1 ! Subtract 1 to get C varid
 cxtype    = xtype
 cnlen     = nlen
 cvalueptr = C_LOC(value)
 cname     = REPEAT(" ",LEN(cname))
 cname     = addCNullChar(name, ie)

 cstatus = nc_put_att(cncid, cvarid, cname(1:ie), cxtype, cnlen, cvalueptr)

 status = cstatus

 End Function nf_put_att
!--------------------------------- nf_get_att_text -----------------------
 Function nf_get_att_text(ncid, varid, name, text) RESULT(status)

! Read variable or global attribute character string from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 text   = REPEAT(" ", LEN(text))

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)
 
 cstatus = nc_get_att_text(cncid, cvarid, cname(1:ie), text)

 status = cstatus

 End Function nf_get_att_text
!--------------------------------- nf_get_att_text_a -----------------------
 Function nf_get_att_text_a(ncid, varid, name, text) RESULT(status)

! New routine to support passing an array of single characters
! Read variable or global attribute array of characters from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

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

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)
 
 cstatus = nc_get_att_text(cncid, cvarid, cname(1:ie), text)

 status = cstatus

 End Function nf_get_att_text_a
!--------------------------------- nf_get_att_int1 -------------------------
 Function nf_get_att_int1(ncid, varid, name, i1vals) RESULT(status)

! Read variable or global attribute BYTE integer data from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

 If (C_SIGNED_CHAR < 0) Then ! schar not supported by processor
   status = NC_EBADTYPE
   RETURN
 EndIf
 
 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)

#if NF_INT1_IS_C_SIGNED_CHAR 
 cstatus = nc_get_att_schar(cncid, cvarid, cname(1:ie), i1vals)
#elif NF_INT1_IS_C_SHORT
 cstatus = nc_get_att_short(cncid, cvarid, cname(1:ie), i1vals)
#elif NF_INT1_IS_C_INT
 cstatus = nc_get_att_int(cncid, cvarid, cname(1:ie), i1vals)
#elif NF_INT1_IS_C_LONG
 cstatus = nc_get_att_long(cncid, cvarid, cname(1:ie), i1vals)
#endif
 status = cstatus

 End Function nf_get_att_int1
!--------------------------------- nf_get_att_int2 --------------------------
 Function nf_get_att_int2(ncid, varid, name, i2vals) RESULT(status)

! Read variable or global attribute 16 bit integer data from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

 If (C_SHORT < 0) Then ! short not supported by processor
   status = NC_EBADTYPE
   RETURN
 EndIf
 
 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)
 
#if NF_INT2_IS_C_SHORT
 cstatus = nc_get_att_short(cncid, cvarid, cname(1:ie), i2vals) 
#elif NF_INT2_IS_C_INT
 cstatus = nc_get_att_int(cncid, cvarid, cname(1:ie), i2vals) 
#elif NF_INT2_IS_C_LONG
 cstatus = nc_get_att_long(cncid, cvarid, cname(1:ie), i2vals) 
#endif
 status = cstatus

 End Function nf_get_att_int2
!--------------------------------- nf_get_att_int ---------------------------
 Function nf_get_att_int(ncid, varid, name, ivals) RESULT(status)

! Read variable or global attribute default Integer data from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

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

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)

#if NF_INT_IS_C_INT 
 cstatus = nc_get_att_int(cncid, cvarid, cname(1:ie), ivals)
#elif NF_INT_IS_C_LONG
 cstatus = nc_get_att_long(cncid, cvarid, cname(1:ie), ivals)
#endif
 status = cstatus

 End Function nf_get_att_int
!--------------------------------- nf_get_att_int64 --------------------------
 Function nf_get_att_int64(ncid, varid, name, i8vals) RESULT(status)

! Read variable or global attribute 64-bit Integer data from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

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

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)

#if NF_INT8_IS_C_SHORT
 cstatus = nc_get_att_short(cncid, cvarid, cname(1:ie), i8vals)
#elif NF_INT8_IS_C_INT
 cstatus = nc_get_att_int(cncid, cvarid, cname(1:ie), i8vals)
#elif NF_INT8_IS_C_LONG
 cstatus = nc_get_att_long(cncid, cvarid, cname(1:ie), i8vals)
#elif NF_INT8_IS_C_LONG_LONG
 cstatus = nc_get_att_longlong(cncid, cvarid, cname(1:ie), i8vals)
#endif
 status = cstatus

 End Function nf_get_att_int64
!--------------------------------- nf_get_att_real -------------------------
 Function nf_get_att_real(ncid, varid, name, rvals) RESULT(status)

! Read variable or global attribute Real(RK4) data from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

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

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)

#if NF_REAL_IS_C_DOUBLE 
 cstatus = nc_get_att_double(cncid, cvarid, cname(1:ie), rvals) 
#else
 cstatus = nc_get_att_float(cncid, cvarid, cname(1:ie), rvals) 
#endif
 status = cstatus

 End Function nf_get_att_real
!--------------------------------- nf_get_att_double -----------------------
 Function nf_get_att_double(ncid, varid, name, dvals) RESULT(status)

! Read variable or global attribute Real(RK8) data from dataset ncid

 USE netcdf_nc_interfaces

 Implicit NONE

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

 Integer                       :: status

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

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

! Check for C null char and add one if missing

 cname = addCNullChar(name, ie)
 
 cstatus = nc_get_att_double(cncid, cvarid, cname(1:ie), dvals)

 status = cstatus

 End Function nf_get_att_double
!--------------------------------- nf_get_att --------------------------------
 Function nf_get_att(ncid, varid, name, value) RESULT(status)

! Get global attribute of any type. We use a C character
! string as the dummy arguments for the values. Don't supply calling
! program with an explicit interface. Just use external

 USE netcdf_nc_interfaces

 Implicit NONE

 Integer,                Intent(IN)    :: ncid, varid
 Character(LEN=*),       Intent(IN)    :: name
 Character(KIND=C_CHAR), Intent(INOUT) :: value(*)

 Integer                               :: status

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

 cncid  = ncid
 cvarid = varid -1 ! Subtract 1 to get C varid
 cname  = REPEAT(" ",LEN(cname))
 cname = addCNullChar(name, ie)

 cstatus = nc_get_att(cncid, cvarid, cname(1:ie), value)

 status = cstatus

 End Function nf_get_att
