!****h* root/fortran/test/tH5O_F03.f90
!
! NAME
!  tH5O_F03.f90
!
! FUNCTION
!  Test FORTRAN HDF5 H5O APIs which are dependent on FORTRAN 2003
!  features.
!
! COPYRIGHT
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!   Copyright by The HDF Group.                                               *
!   All rights reserved.                                                      *
!                                                                             *
!   This file is part of HDF5.  The full HDF5 copyright notice, including     *
!   terms governing use, modification, and redistribution, is contained in    *
!   the COPYING file, which can be found at the root of the source code       *
!   distribution tree, or in https://www.hdfgroup.org/licenses.               *
!   If you do not have access to either file, you may request a copy from     *
!   help@hdfgroup.org.                                                        *
! * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
!
!*****

! *****************************************
! ***        H 5 O   T E S T S
! *****************************************
MODULE visit_cb

  USE HDF5
  USE, INTRINSIC :: ISO_C_BINDING

  IMPLICIT NONE

  INTEGER, PARAMETER :: info_size = 9

  !-------------------------------------------------------------------------
  ! Function:    visit_obj_cb
  !
  ! Purpose:     Callback routine for visiting objects in a file
  !
  ! Return:      Success:        0
  !              Failure:        -1
  !-------------------------------------------------------------------------
  !
  ! Object visit structs
  TYPE, bind(c) :: obj_visit_t
     CHARACTER(KIND=C_CHAR), DIMENSION(1:180) :: path   ! Path to object
     INTEGER :: type_obj ! type of object
  END TYPE obj_visit_t

  TYPE, bind(c) :: ovisit_ud_t
     INTEGER :: idx              ! Index in object visit structure
     TYPE(obj_visit_t), DIMENSION(1:info_size) :: info   ! Pointer to the object visit structure to use
     INTEGER :: field
  END TYPE ovisit_ud_t

CONTAINS

! Compares the field values of a C H5O_info_t and a Fortran H5O_info_t.

  INTEGER FUNCTION compare_h5o_info_t( loc_id, oinfo_f, oinfo_c, field, full_f_field ) RESULT(status)

    IMPLICIT NONE
    INTEGER(HID_T)     :: loc_id
    TYPE(h5o_info_t)   :: oinfo_f
    TYPE(c_h5o_info_t) :: oinfo_c
    TYPE(H5O_TOKEN_T_F) :: token_c
    INTEGER :: field
    LOGICAL :: full_f_field ! All the fields of Fortran H5O_info_t where filled
!   local
    INTEGER(C_INT), DIMENSION(1:8) :: atime, btime, ctime, mtime
    INTEGER :: cmp_value
    INTEGER :: i
    INTEGER :: ierr
    INTEGER(HADDR_T) :: addr
    TYPE(H5O_TOKEN_T_F) :: token

    status = 0

    IF( (field .EQ. H5O_INFO_BASIC_F).OR.(field .EQ. H5O_INFO_ALL_F) )THEN
       IF( (oinfo_f%fileno.LE.0) .OR. (oinfo_c%fileno .NE. oinfo_f%fileno) )THEN
          status = -1
          RETURN
       ENDIF
       token_c%token = oinfo_c%token%token
       CALL H5Otoken_cmp_f(loc_id, oinfo_f%token, token_c, cmp_value, ierr)
       IF( (ierr .EQ. -1) .OR. (cmp_value .NE. 0) ) THEN
          status = -1
          RETURN
       ENDIF
       IF( (oinfo_f%type.LT.0) .OR. (oinfo_c%type .NE. oinfo_f%type) )THEN
          status = -1
          RETURN
       ENDIF
       IF( (oinfo_f%rc.LT.0) .OR. (oinfo_c%rc .NE. oinfo_f%rc) )THEN
          status = -1
          RETURN
       ENDIF

       CALL h5vlnative_token_to_addr_f(loc_id, oinfo_f%token, addr, ierr)
       IF( ierr .EQ. -1) THEN
          status = -1
          RETURN
       ENDIF
       CALL h5vlnative_addr_to_token_f(loc_id, addr, token, ierr)
       IF( ierr .EQ. -1) THEN
          status = -1
          RETURN
       ENDIF
       CALL H5Otoken_cmp_f(loc_id, oinfo_f%token, token, cmp_value, ierr)
       IF( (ierr .EQ. -1) .OR. (cmp_value .NE. 0) ) THEN
          status = -1
          RETURN
       ENDIF

    ENDIF

    IF((field .EQ. H5O_INFO_TIME_F).OR.(field .EQ. H5O_INFO_ALL_F))THEN

       atime(1:8) = INT(h5gmtime(oinfo_c%atime),C_INT)
       btime(1:8) = INT(h5gmtime(oinfo_c%btime),C_INT)
       ctime(1:8) = INT(h5gmtime(oinfo_c%ctime),C_INT)
       mtime(1:8) = INT(h5gmtime(oinfo_c%mtime),C_INT)

       DO i = 1, 8
          IF( (atime(i) .NE. oinfo_f%atime(i)) )THEN
             status = -1
             RETURN
          ENDIF

          IF( (btime(i) .NE. oinfo_f%btime(i)) )THEN
             status = -1
             RETURN
          ENDIF

          IF( (ctime(i) .NE. oinfo_f%ctime(i)) )THEN
             status = -1
             RETURN
          ENDIF

          IF( (mtime(i) .NE. oinfo_f%mtime(i)) )THEN
             status = -1
             RETURN
          ENDIF
       ENDDO

    ELSE IF(field .EQ. H5O_INFO_TIME_F.AND. full_f_field)THEN
       ! check other field values are not filled (using only a small subset to check)
       status = 0
       IF( oinfo_c%fileno .NE. oinfo_f%fileno) status = status + 1
       token_c%token = oinfo_c%token%token
       CALL H5Otoken_cmp_f(loc_id, oinfo_f%token, token_c, cmp_value, ierr)
       IF( (ierr .EQ. -1) .OR. (cmp_value .NE. 0) ) THEN
          status = -1
          RETURN
       ENDIF
       IF( oinfo_c%type .NE. oinfo_f%type) status = status + 1
       IF( oinfo_c%rc .NE. oinfo_f%rc) status = status + 1
       IF(status.EQ.0) THEN ! There was no difference found, which is only possible if the field was filled.
          status = -1
          RETURN
       ENDIF
       status = 0 ! reset
    ENDIF

    IF((field .EQ. H5O_INFO_NUM_ATTRS_F).OR.(field .EQ. H5O_INFO_ALL_F))THEN
       IF( (oinfo_f%num_attrs.LT.0) .OR. (oinfo_c%num_attrs .NE. oinfo_f%num_attrs) )THEN
          status = -1
          RETURN
       ENDIF
    ELSE IF( field .EQ. H5O_INFO_ALL_F.AND.full_f_field)THEN
       ! check other field values are not filled (using only a small subset to check)
       status = 0
       IF( oinfo_c%fileno .NE. oinfo_f%fileno) status = status + 1
       token_c%token = oinfo_c%token%token
       CALL H5Otoken_cmp_f(loc_id, oinfo_f%token, token_c, cmp_value, ierr)
       IF( (ierr .EQ. -1) .OR. (cmp_value .NE. 0) ) THEN
          status = -1
          RETURN
       ENDIF
       IF( oinfo_c%type .NE. oinfo_f%type) status = status + 1
       IF( oinfo_c%rc .NE. oinfo_f%rc) status = status + 1
       IF(status.EQ.0) THEN ! There was no difference found, which is only possible if the field was filled.
          status = -1
          RETURN
       ENDIF
       status = 0 ! reset
    ENDIF

  END FUNCTION compare_h5o_info_t

  INTEGER FUNCTION visit_obj_cb( group_id, name, oinfo_c, op_data) bind(C)

    IMPLICIT NONE

    INTEGER(HID_T), VALUE :: group_id
    CHARACTER(LEN=1), DIMENSION(1:180) :: name
    CHARACTER(LEN=180) :: name2
    TYPE(c_h5o_info_t) :: oinfo_c
    TYPE(ovisit_ud_t) :: op_data
    TYPE(h5o_info_t) ::  oinfo_f
!
!   MEMBER      | TYPE | MEANING                 | RANGE
! A(1) = tm_sec	  int   seconds after the minute   0-61*
! A(2) = tm_min	  int   minutes after the hour     0-59
! A(3) = tm_hour  int   hours since midnight       0-23
! A(4) = tm_mday  int   day of the month           1-31
! A(5) = tm_mon	  int   months since January       0-11
! A(6) = tm_year  int   years since 1900
! A(7) = tm_wday  int   days since Sunday          0-6
! A(8) = tm_yday  int   days since January 1       0-365
! A(9) = tm_isdst int   Daylight Saving Time flag
!
    INTEGER :: len, i
    INTEGER :: idx
    INTEGER :: ierr

    visit_obj_cb = 0

    ! Since the name is generated in C and passed to a Fortran string, it
    ! will be NULL terminated, so we need to find the end of the string.

    DO len = 1, 180
       IF(name(len) .EQ. C_NULL_CHAR) EXIT
    ENDDO

    len = len - 1

    ! Check for correct object information
    name2(1:180) = ""
    DO i = 1, len
       name2(i:i) = name(i)(1:1)
    ENDDO

    IF(op_data%field .EQ. H5O_INFO_ALL_F)THEN

       idx = op_data%idx

       DO i = 1, len
          IF(op_data%info(idx)%path(i)(1:1) .NE. name(i)(1:1))THEN
             visit_obj_cb = -1
             RETURN
          ENDIF

          IF(op_data%info(idx)%type_obj .NE. oinfo_c%type)THEN
             visit_obj_cb = -1
             RETURN
          ENDIF
       ENDDO

    ENDIF

    ! Check H5Oget_info_by_name_f; if partial field values were filled correctly
    CALL H5Oget_info_by_name_f(group_id, name2, oinfo_f, ierr)
    visit_obj_cb =  compare_h5o_info_t( group_id, oinfo_f, oinfo_c, op_data%field, .TRUE. )
    IF(visit_obj_cb.EQ.-1) RETURN

    ! Check H5Oget_info_by_name_f, only check field values
    CALL H5Oget_info_by_name_f(group_id, name2, oinfo_f, ierr, fields = op_data%field)
    visit_obj_cb =  compare_h5o_info_t(group_id, oinfo_f, oinfo_c, op_data%field, .FALSE. )
    IF(visit_obj_cb.EQ.-1) RETURN

    IF(op_data%idx.EQ.1)THEN

       ! Check H5Oget_info_f, only check field values
       CALL H5Oget_info_f(group_id, oinfo_f, ierr, fields = op_data%field)
       visit_obj_cb =  compare_h5o_info_t(group_id, oinfo_f, oinfo_c, op_data%field, .FALSE.  )
       IF(visit_obj_cb.EQ.-1) RETURN

       ! Check H5Oget_info_f; if partial field values where filled correctly
       CALL H5Oget_info_f(group_id, oinfo_f, ierr)
       visit_obj_cb =  compare_h5o_info_t(group_id, oinfo_f, oinfo_c, op_data%field, .TRUE.  )
       IF(visit_obj_cb.EQ.-1) RETURN

    ENDIF

    ! Advance to next location in expected output
    op_data%idx = op_data%idx + 1

  END FUNCTION visit_obj_cb

END MODULE visit_cb

MODULE TH5O_F03

  USE HDF5
  USE TH5_MISC
  USE ISO_C_BINDING

CONTAINS
!***************************************************************
!**
!**  test_h5o_refcount(): Test H5O refcounting functions.
!**
!***************************************************************

SUBROUTINE test_h5o_refcount(total_error)

  IMPLICIT NONE

  INTEGER, INTENT(INOUT) :: total_error

  CHARACTER(LEN=11), PARAMETER :: FILENAME = "th5o_ref.h5"
  INTEGER, PARAMETER :: DIM0 = 5
  INTEGER, PARAMETER :: DIM1 = 10
  INTEGER(hid_t) :: fid ! HDF5 File ID
  INTEGER(hid_t) :: grp, dset, dtype, dspace ! Object identifiers
  TYPE(h5o_info_t) :: oinfo                  ! Object info struct
  INTEGER(hsize_t), DIMENSION(1:2) :: dims
  INTEGER :: error                           ! Value returned from API calls

  ! Create a new HDF5 file
  CALL h5fcreate_f(FILENAME,H5F_ACC_TRUNC_F,fid,error)
  CALL check("h5fcreate_f", error, total_error)

  ! Create a group, dataset, and committed datatype within the file
  ! Create the group
  CALL h5gcreate_f(fid, "group", grp, error)
  CALL check("h5gcreate_f",error, total_error)

  ! Commit the type inside the group
  CALL h5tcopy_f(H5T_NATIVE_INTEGER, dtype, error)
  CALL check("H5Tcopy_f",error, total_error)
  CALL h5tcommit_f(fid, "datatype", dtype, error)
  CALL check("h5tcommit_f", error, total_error)

  ! Create the data space for the dataset.
  dims(1) = DIM0
  dims(2) = DIM1

  CALL h5screate_simple_f(2, dims, dspace, error)
  CALL check("h5screate_simple_f", error, total_error)

  ! Create the dataset.
  CALL h5dcreate_f(fid, "dataset", H5T_NATIVE_INTEGER, dspace, dset, error)
  CALL check("h5dcreate_f", error, total_error)
  CALL h5sclose_f(dspace, error)
  CALL check("h5sclose_f", error, total_error)

  ! Get ref counts for each object.  They should all be 1, since each object has a hard link.
  CALL h5oget_info_by_name_f(fid, "group", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF
  CALL h5oget_info_by_name_f(fid, "datatype", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF
  CALL h5oget_info_by_name_f(fid, "dataset", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF

  ! Check h5oget_info
  CALL h5oget_info_f(grp, oinfo, error)
  CALL check("h5oget_info_f", error, total_error)
  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_f", -1, total_error)
  ENDIF
  IF(oinfo%type.NE.H5O_TYPE_GROUP_F)THEN
     CALL check("h5oget_info_f", -1, total_error)
  ENDIF

  ! Increment each object's reference count.
  CALL h5oincr_refcount_f(grp, error)
  CALL check("h5oincr_refcount_f", error, total_error)
  CALL h5oincr_refcount_f(dtype, error)
  CALL check("h5oincr_refcount_f", error, total_error)
  CALL h5oincr_refcount_f(dset, error)
  CALL check("h5oincr_refcount_f", error, total_error)

  ! Get ref counts for each object.  They should all be 2 now.
  CALL h5oget_info_by_name_f(fid, "group", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.2)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF
  CALL h5oget_info_by_name_f(fid, "datatype", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.2)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF
  CALL h5oget_info_by_name_f(fid, "dataset", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.2)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF

  ! Decrement the reference counts and check that they decrease back to 1.
  CALL h5odecr_refcount_f(grp, error)
  CALL check("h5oincr_refcount_f", error, total_error)
  CALL h5odecr_refcount_f(dtype, error)
  CALL check("h5oincr_refcount_f", error, total_error)
  CALL h5odecr_refcount_f(dset, error)
  CALL check("h5oincr_refcount_f", error, total_error)

  CALL h5oget_info_by_name_f(fid, "group", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF
  CALL h5oget_info_by_name_f(fid, "datatype", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF
  CALL h5oget_info_by_name_f(fid, "dataset", oinfo, error)
  CALL check("h5oget_info_by_name_f", error, total_error)
  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_name_f", -1, total_error)
  ENDIF

  CALL h5gclose_f(grp, error)
  CALL check("h5gclose_f",error, total_error)
  CALL h5tclose_f(dtype, error)
  CALL check("h5tclose_f",error, total_error)
  CALL h5dclose_f(dset, error)
  CALL check("h5dclose_f",error, total_error)
  CALL h5fclose_f(fid, error)
  CALL check("h5fclose_f",error, total_error)

END SUBROUTINE test_h5o_refcount

!****************************************************************
!**
!**  test_obj_visit(): Test H5O visit functions.
!**
!****************************************************************

SUBROUTINE test_obj_visit(total_error)

  USE visit_cb

  IMPLICIT NONE

  INTEGER, INTENT(INOUT) :: total_error

  TYPE(ovisit_ud_t), TARGET :: udata ! User-data for visiting
  INTEGER(hid_t) :: fid = -1
  TYPE(C_PTR) :: f_ptr
  TYPE(C_FUNPTR) :: fun_ptr
  CHARACTER(LEN=180) :: object_name
  INTEGER :: ret_val
  INTEGER :: error

  ! Construct "interesting" file to visit
  CALL build_visit_file(fid)

  ! Initialize udata for testing purposes
  udata%info(1)%path(1:1) ="."
  udata%info(1)%type_obj = H5O_TYPE_GROUP_F
  udata%info(2)%path(1:12) = &
       (/"D","a","t","a","s","e","t","_","z","e","r","o"/)
  udata%info(2)%type_obj =H5O_TYPE_DATASET_F
  udata%info(3)%path(1:6) = &
       (/"G","r","o","u","p","1"/)
  udata%info(3)%type_obj = H5O_TYPE_GROUP_F
  udata%info(4)%path(1:18) =&
       (/"G","r","o","u","p","1","/","D","a","t","a","s","e","t","_","o","n","e"/)
  udata%info(4)%type_obj = H5O_TYPE_DATASET_F
  udata%info(5)%path(1:13) =&
       (/"G","r","o","u","p","1","/","G","r","o","u","p","2"/)
  udata%info(5)%type_obj = H5O_TYPE_GROUP_F
  udata%info(6)%path(1:25) =&
       (/"G","r","o","u","p","1","/","G","r","o","u","p","2","/","D","a","t","a","s","e","t","_","t","w","o"/)
  udata%info(6)%type_obj = H5O_TYPE_DATASET_F
  udata%info(7)%path(1:22) =&
       (/"G","r","o","u","p","1","/","G","r","o","u","p","2","/","T","y","p","e","_","t","w","o"/)
  udata%info(7)%type_obj = H5O_TYPE_NAMED_DATATYPE_F
  udata%info(8)%path(1:15) =&
       (/"G","r","o","u","p","1","/","T","y","p","e","_","o","n","e"/)
  udata%info(8)%type_obj = H5O_TYPE_NAMED_DATATYPE_F
  udata%info(9)%path(1:9) =&
       (/"T","y","p","e","_","z","e","r","o"/)
  udata%info(9)%type_obj = H5O_TYPE_NAMED_DATATYPE_F

  ! Visit all the objects reachable from the root group (with file ID)

  fun_ptr = C_FUNLOC(visit_obj_cb)
  f_ptr = C_LOC(udata)

  ! Test h5ovisit_f
  udata%field = H5O_INFO_ALL_F
  udata%idx = 1
  CALL h5ovisit_f(fid, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error)
  CALL check("h5ovisit_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_f", -1, total_error)
  ENDIF

  ! Test fields option
  udata%field = H5O_INFO_ALL_F
  udata%idx = 1
  CALL h5ovisit_f(fid, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_f", -1, total_error)
  ENDIF
  udata%field = H5O_INFO_BASIC_F
  udata%idx = 1
  CALL h5ovisit_f(fid, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_f", -1, total_error)
  ENDIF
  udata%field = H5O_INFO_TIME_F
  udata%idx = 1
  CALL h5ovisit_f(fid, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_f", -1, total_error)
  ENDIF
  udata%field = H5O_INFO_NUM_ATTRS_F
  udata%idx = 1
  CALL h5ovisit_f(fid, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_f", -1, total_error)
  ENDIF

  ! Test h5ovisit_by_name_f
  object_name = "/"
  udata%idx = 1
  udata%field = H5O_INFO_ALL_F
  CALL h5ovisit_by_name_f(fid, object_name, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_by_name_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_by_name_f", -1, total_error)
  ENDIF

  ! Test fields option
  udata%idx = 1
  udata%field = H5O_INFO_BASIC_F
  CALL h5ovisit_by_name_f(fid, object_name, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_by_name_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_by_name_f", -1, total_error)
  ENDIF
  udata%idx = 1
  udata%field = H5O_INFO_TIME_F
  CALL h5ovisit_by_name_f(fid, object_name, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_by_name_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_by_name_f", -1, total_error)
  ENDIF
  udata%idx = 1
  udata%field =  H5O_INFO_NUM_ATTRS_F
  CALL h5ovisit_by_name_f(fid, object_name, H5_INDEX_NAME_F, H5_ITER_INC_F, fun_ptr, f_ptr, ret_val, error, fields=udata%field)
  CALL check("h5ovisit_by_name_f", error, total_error)
  IF(ret_val.LT.0)THEN
     CALL check("h5ovisit_by_name_f", -1, total_error)
  ENDIF

  CALL h5fclose_f(fid, error)
  CALL check("h5fclose_f",error, total_error)

END SUBROUTINE test_obj_visit

!****************************************************************
!**
!**  test_obj_info(): Test H5O info functions.
!**
!****************************************************************

SUBROUTINE test_obj_info(total_error)

  IMPLICIT NONE

  INTEGER, INTENT(INOUT) :: total_error

  INTEGER(hid_t) :: fid = -1             ! File ID
  INTEGER(hid_t) :: gid = -1, gid2 = -1  ! Group IDs
  INTEGER(hid_t) :: did                  ! Dataset ID
  INTEGER(hid_t) :: sid                  ! Dataspace ID
  TYPE(hobj_ref_t_f), TARGET :: wref     ! Reference to write
  TYPE(hobj_ref_t_f), TARGET :: rref     ! Reference to read
  TYPE(H5O_info_t) :: oinfo              ! Object info struct
  INTEGER :: error
  TYPE(C_PTR) :: f_ptr

  CHARACTER(LEN=6) :: GROUPNAME   =    "/group"
  CHARACTER(LEN=6) :: GROUPNAME2  =    "group2"
  CHARACTER(LEN=6) :: GROUPNAME3  =    "group3"
  CHARACTER(LEN=5) :: DSETNAME    =    "/dset"
  CHARACTER(LEN=5) :: DSETNAME2   =    "dset2"

  ! Create file with a group and a dataset containing an object reference to the group
  CALL h5fcreate_f("get_info.h5", H5F_ACC_TRUNC_F, fid, error)
  CALL check("h5fcreate_f",error, total_error)

  ! Create dataspace to use for dataset
  CALL h5screate_f(H5S_SCALAR_F, sid, error)
  CALL check("h5screate_f",error,total_error)

  ! Create group to refer to
  CALL h5gcreate_f(fid,  GROUPNAME, gid, error)
  CALL check("h5gcreate_f",error,total_error)

  ! Create nested groups
  CALL h5gcreate_f(gid,  GROUPNAME2, gid2, error)
  CALL check("h5gcreate_f",error,total_error)
  CALL h5gclose_f(gid2, error)
  CALL check("h5gclose_f",error,total_error)

  CALL h5gcreate_f(gid,  GROUPNAME3, gid2, error)
  CALL check("h5gcreate_f",error,total_error)
  CALL h5gclose_f(gid2, error)
  CALL check("h5gclose_f",error,total_error)

  ! Create bottom dataset
  CALL h5dcreate_f(gid, DSETNAME2, H5T_NATIVE_INTEGER, sid, did, error)
  CALL check("h5dcreate_f",error, total_error)

  CALL h5dclose_f(did, error)
  CALL check("h5dclose_f", error, total_error)

  CALL h5gclose_f(gid, error)
  CALL check("h5gclose_f",error,total_error)

  ! Create dataset
  CALL h5dcreate_f(fid, DSETNAME, H5T_STD_REF_OBJ, sid, did, error)
  CALL check("h5dcreate_f",error, total_error)

  f_ptr = C_LOC(wref)

  ! Create reference to group
  CALL h5rcreate_f(fid, GROUPNAME, H5R_OBJECT_F, f_ptr, error)
  CALL check("h5rcreate_f",error, total_error)

  ! Write reference to disk
  CALL h5dwrite_f(did, H5T_STD_REF_OBJ, f_ptr, error)
  CALL check("h5dwrite_f",error, total_error)

  ! Close objects
  CALL h5dclose_f(did, error)
  CALL check("h5dclose_f", error, total_error)
  CALL h5sclose_f(sid, error)
  CALL check("h5sclose_f", error, total_error)
  CALL h5fclose_f(fid, error)
  CALL check("h5fclose_f", error, total_error)

  ! Re-open file
  CALL h5fopen_f("get_info.h5", H5F_ACC_RDWR_F, fid, error)
  CALL check("h5fopen_f", error, total_error)

  ! Re-open dataset
  CALL h5dopen_f(fid, DSETNAME, did, error)
  CALL check("h5dopen_f", error, total_error)

  ! Read in the reference

  f_ptr = C_LOC(rref)

  CALL h5dread_f(did, H5T_STD_REF_OBJ, f_ptr, error)
  CALL check("H5Dread_f",error, total_error)

  ! Dereference to get the group

  CALL h5rdereference_f(did, H5R_OBJECT_F, f_ptr, gid, error)
  CALL check("h5rdereference_f", error, total_error)

  CALL h5oget_info_by_idx_f(gid, ".", H5_INDEX_NAME_F, H5_ITER_INC_F, 0_hsize_t, oinfo, error)
  CALL check("h5oget_info_by_idx_f", error, total_error)

  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_idx_f", -1, total_error)
  ENDIF
  IF(oinfo%type.NE.H5O_TYPE_DATASET_F)THEN
     CALL check("h5oget_info_by_idx_f", -1, total_error)
  ENDIF

  ! Check partial fields
  CALL h5oget_info_by_idx_f(gid, ".", H5_INDEX_NAME_F, H5_ITER_INC_F, 0_hsize_t, oinfo, error, fields=H5O_INFO_BASIC_F )
  CALL check("h5oget_info_by_idx_f", error, total_error)

  IF(oinfo%rc.NE.1)THEN
     CALL check("h5oget_info_by_idx_f", -1, total_error)
  ENDIF
  IF(oinfo%type.NE.H5O_TYPE_DATASET_F)THEN
     CALL check("h5oget_info_by_idx_f", -1, total_error)
  ENDIF

  CALL h5oget_info_by_idx_f(gid, ".", H5_INDEX_NAME_F, H5_ITER_INC_F, 0_hsize_t, oinfo, error, fields=H5O_INFO_TIME_F )
  CALL check("h5oget_info_by_idx_f", error, total_error)
  ! These field values should not be filled
  IF(oinfo%rc.EQ.1)THEN
     CALL check("h5oget_info_by_idx_f", -1, total_error)
  ENDIF
  IF(oinfo%type.EQ.H5O_TYPE_DATASET_F)THEN
     CALL check("h5oget_info_by_idx_f", -1, total_error)
  ENDIF

  ! Close objects
  CALL h5dclose_f(did, error)
  CALL check("h5dclose_f", error, total_error)
  CALL h5gclose_f(gid, error)
  CALL check("h5sclose_f", error, total_error)
  CALL h5fclose_f(fid, error)
  CALL check("h5fclose_f", error, total_error)

END SUBROUTINE test_obj_info

!-------------------------------------------------------------------------
! Function:    build_visit_file
!
! Purpose:     Build an "interesting" file to use for visiting links & objects
!-------------------------------------------------------------------------
!

SUBROUTINE build_visit_file(fid)

  IMPLICIT NONE

  INTEGER(hid_t) :: fid                   ! File ID
  INTEGER(hid_t) :: gid = -1, gid2 = -1   ! Group IDs
  INTEGER(hid_t) :: sid = -1              ! Dataspace ID
  INTEGER(hid_t) :: did = -1              ! Dataset ID
  INTEGER(hid_t) :: tid = -1              ! Datatype ID
  INTEGER(hid_t) :: aid = -1, aid2 = -1, aid3 = -1 ! Attribute ID
  CHARACTER(LEN=20) :: filename = 'visit.h5'
  INTEGER :: error

  ! Create file for visiting
  CALL H5Fcreate_f(filename, H5F_ACC_TRUNC_F, fid, error)

  ! Create group
  CALL H5Gcreate_f(fid, "/Group1", gid, error)

  ! Create nested group
  CALL H5Gcreate_f(gid, "Group2", gid2, error)

  CALL H5Screate_f(H5S_SCALAR_F, sid, error)
  CALL H5Acreate_f(gid2, "Attr1", H5T_NATIVE_INTEGER, sid, aid, error)
  CALL H5Acreate_f(gid2, "Attr2", H5T_NATIVE_INTEGER, sid, aid2, error)
  CALL H5Acreate_f(gid2, "Attr3", H5T_NATIVE_INTEGER, sid, aid3, error)
  CALL H5Aclose_f(aid,error)
  CALL H5Aclose_f(aid2,error)
  CALL H5Aclose_f(aid3,error)
  CALL H5Sclose_f(sid,error)

  ! Close groups
  CALL h5gclose_f(gid2, error)
  CALL h5gclose_f(gid, error)

  ! Create soft links to groups created
  CALL H5Lcreate_soft_f("/Group1", fid, "/soft_one", error)
  CALL H5Lcreate_soft_f("/Group1/Group2", fid, "/soft_two", error)

  ! Create dangling soft link
  CALL H5Lcreate_soft_f("nowhere", fid, "/soft_dangle", error)

  ! Create hard links to all groups
  CALL H5Lcreate_hard_f(fid, "/", fid, "hard_zero", error)
  CALL H5Lcreate_hard_f(fid, "/Group1", fid, "hard_one", error)
  CALL H5Lcreate_hard_f(fid, "/Group1/Group2", fid, "hard_two", error)

  ! Create loops w/hard links
  CALL H5Lcreate_hard_f(fid, "/Group1", fid, "/Group1/hard_one", error)
  CALL H5Lcreate_hard_f(fid, "/", fid, "/Group1/Group2/hard_zero", error)

  ! Create dataset in each group
  CALL H5Screate_f(H5S_SCALAR_F, sid, error)

  CALL H5Dcreate_f(fid, "/Dataset_zero", H5T_NATIVE_INTEGER, sid, did, error)
  CALL H5Dclose_f(did, error)

  CALL H5Dcreate_f(fid, "/Group1/Dataset_one", H5T_NATIVE_INTEGER, sid, did, error)
  CALL H5Dclose_f(did, error)

  CALL H5Dcreate_f(fid, "/Group1/Group2/Dataset_two", H5T_NATIVE_INTEGER, sid, did, error)
  CALL H5Dclose_f(did, error)

  CALL H5Sclose_f(sid, error)

  ! Create named datatype in each group
  CALL H5Tcopy_f(H5T_NATIVE_INTEGER, tid, error)

  CALL H5Tcommit_f(fid, "/Type_zero", tid, error)
  CALL H5Tclose_f(tid, error)

  CALL H5Tcopy_f(H5T_NATIVE_INTEGER, tid, error)
  CALL H5Tcommit_f(fid, "/Group1/Type_one", tid, error)
  CALL H5Tclose_f(tid, error)

  CALL H5Tcopy_f(H5T_NATIVE_INTEGER, tid, error)
  CALL H5Tcommit_f(fid, "/Group1/Group2/Type_two", tid, error)
  CALL H5Tclose_f(tid, error)

END SUBROUTINE build_visit_file

END MODULE TH5O_F03
