MODULE ncdf_simple_tests_tgv
  use pio_tutil
  ! tgv in prefix corresponds to module name (ncdf_simple_tests_tgv)
  character(len=PIO_TF_MAX_STR_LEN), parameter :: tgv_fname = "pio_ncdf_test_file.nc"
  integer :: tgv_iotype
END MODULE ncdf_simple_tests_tgv

PIO_TF_AUTO_TEST_SUB_BEGIN test_create
  use ncdf_simple_tests_tgv
  Implicit none
  type(file_desc_t) :: pio_file
  character(len=PIO_TF_MAX_STR_LEN), parameter :: fname = "pio_test_create.nc"
  integer, parameter :: MAX_FLAGS = 3
  integer :: flags(MAX_FLAGS) = (/PIO_CLOBBER,&
                                  IOR(PIO_CLOBBER,PIO_64BIT_OFFSET),&
                                  IOR(PIO_CLOBBER,PIO_64BIT_DATA)&
                                  /)
  integer :: i
  integer :: ret
  
  do i=1,MAX_FLAGS
    PIO_TF_LOG(0,*) "Trying to create a file: type = ", tgv_iotype, ": flags = ", flags(i)

    ret = PIO_createfile(pio_tf_iosystem_, pio_file, tgv_iotype, fname, flags(i))
    PIO_TF_CHECK_ERR(ret, "Failed to create:" // trim(fname))

    call PIO_closefile(pio_file)

    call PIO_deletefile(pio_tf_iosystem_, fname)
  end do

PIO_TF_AUTO_TEST_SUB_END test_create

PIO_TF_AUTO_TEST_SUB_BEGIN test_clobber
  use ncdf_simple_tests_tgv
  Implicit none
  type(file_desc_t) :: pio_file
  character(len=PIO_TF_MAX_STR_LEN), parameter :: clob_fname = "pio_test_clobber.nc"
  integer :: ret
  
  ! Default is NOCLOBBER
  ret = PIO_createfile(pio_tf_iosystem_, pio_file, tgv_iotype, clob_fname)
  PIO_TF_CHECK_ERR(ret, "Failed to create:" // trim(clob_fname))

  call PIO_closefile(pio_file)

  ! Recrate with CLOBBER option - should erase existing file and create new one
  ret = PIO_createfile(pio_tf_iosystem_, pio_file, tgv_iotype, clob_fname, PIO_CLOBBER)
  PIO_TF_CHECK_ERR(ret, "Failed to create (with CLOBBER):" // trim(clob_fname))

  call PIO_closefile(pio_file)
  call PIO_deletefile(pio_tf_iosystem_, clob_fname)

PIO_TF_AUTO_TEST_SUB_END test_clobber

PIO_TF_AUTO_TEST_SUB_BEGIN test_redef_enddef
  use ncdf_simple_tests_tgv
  Implicit none
  type(file_desc_t) :: pio_file
  integer :: ret
  
  ret = PIO_openfile(pio_tf_iosystem_, pio_file, tgv_iotype, tgv_fname, PIO_write)
  PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(tgv_fname))

  ! A simple redef and then enddef
  ret = PIO_redef(pio_file)
  PIO_TF_CHECK_ERR(ret, "Failed to redef:" // trim(tgv_fname))

  ret = PIO_enddef(pio_file)
  PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(tgv_fname))

  call PIO_closefile(pio_file)

PIO_TF_AUTO_TEST_SUB_END test_redef_enddef

PIO_TF_AUTO_TEST_SUB_BEGIN test_def_dim
  use ncdf_simple_tests_tgv
  Implicit none
  type(file_desc_t) :: pio_file
  integer :: pio_dim
  integer :: ret
  
  ret = PIO_openfile(pio_tf_iosystem_, pio_file, tgv_iotype, tgv_fname, PIO_write)
  PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(tgv_fname))

  ! A simple redef and then enddef
  ret = PIO_redef(pio_file)
  PIO_TF_CHECK_ERR(ret, "Failed to redef:" // trim(tgv_fname))

  ret = PIO_def_dim(pio_file, 'dummy_dim_def_dim', 100, pio_dim)
  PIO_TF_CHECK_ERR(ret, "Failed to define dim:" // trim(tgv_fname))

  ret = PIO_enddef(pio_file)
  PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(tgv_fname))

  call PIO_closefile(pio_file)

PIO_TF_AUTO_TEST_SUB_END test_def_dim

PIO_TF_AUTO_TEST_SUB_BEGIN test_def_var
  use ncdf_simple_tests_tgv
  Implicit none
  type(file_desc_t) :: pio_file
  type(var_desc_t)  :: pio_var
  integer :: pio_dim
  integer :: ret
  
  ret = PIO_openfile(pio_tf_iosystem_, pio_file, tgv_iotype, tgv_fname, PIO_write)
  PIO_TF_CHECK_ERR(ret, "Failed to open:" // trim(tgv_fname))

  ! A simple redef and then enddef
  ret = PIO_redef(pio_file)
  PIO_TF_CHECK_ERR(ret, "Failed to redef:" // trim(tgv_fname))

  ret = PIO_def_dim(pio_file, 'dummy_dim_def_var', 100, pio_dim)
  PIO_TF_CHECK_ERR(ret, "Failed to define dim:" // trim(tgv_fname))

  ret = PIO_def_var(pio_file, 'dummy_var_def_var', PIO_int, (/pio_dim/), pio_var)
  PIO_TF_CHECK_ERR(ret, "Failed to define var:" // trim(tgv_fname))

  ret = PIO_enddef(pio_file)
  PIO_TF_CHECK_ERR(ret, "Failed to enddef:" // trim(tgv_fname))

  call PIO_closefile(pio_file)

PIO_TF_AUTO_TEST_SUB_END test_def_var

PIO_TF_TEMPLATE<PIO_TF_PREDEF_TYPENAME PIO_TF_DATA_TYPE, PIO_TF_PREDEF_TYPENAME PIO_TF_FC_DATA_TYPE>
PIO_TF_AUTO_TEST_SUB_BEGIN test_data_conversion
  use ncdf_simple_tests_tgv
  implicit none
  integer, parameter :: VEC_LOCAL_SZ = 7
  type(var_desc_t)  :: pio_var_int, pio_var_real, pio_var_double
  type(file_desc_t) :: pio_file
  character(len=PIO_TF_MAX_STR_LEN) :: data_fname = "pio_test_data_conversion.nc"
  character(len=*), parameter :: PIO_VAR_INT_NAME = 'PIO_TF_test_var_int'
  character(len=*), parameter :: PIO_VAR_REAL_NAME = 'PIO_TF_test_var_real'
  character(len=*), parameter :: PIO_VAR_DBL_NAME = 'PIO_TF_test_var_dbl'
  type(io_desc_t) :: wiodesc, riodesc
  integer, dimension(VEC_LOCAL_SZ) :: compdof, compdof_rel_disps
  PIO_TF_FC_DATA_TYPE, dimension(VEC_LOCAL_SZ) :: wbuf, rbuf
  integer, dimension(1) :: dims
  integer :: pio_dim
  integer :: i, ierr

  do i=1,VEC_LOCAL_SZ
    compdof_rel_disps(i) = i
  end do
  dims(1) = VEC_LOCAL_SZ * pio_tf_world_sz_
  compdof = VEC_LOCAL_SZ * pio_tf_world_rank_ + compdof_rel_disps
  wbuf = pio_tf_world_rank_

  ! Set the decomposition for writing data
  call PIO_initdecomp(pio_tf_iosystem_, PIO_TF_DATA_TYPE, dims, compdof, wiodesc)

  ! Set the decomposition for reading data
  call PIO_initdecomp(pio_tf_iosystem_, PIO_TF_DATA_TYPE, dims, compdof, riodesc)

  ierr = PIO_createfile(pio_tf_iosystem_, pio_file, tgv_iotype, data_fname, PIO_CLOBBER) 
  PIO_TF_CHECK_ERR(ierr, "Could not create file " // trim(data_fname))

  ierr = PIO_def_dim(pio_file, 'PIO_TF_test_dim', dims(1), pio_dim)
  PIO_TF_CHECK_ERR(ierr, "Failed to define a dim : " // trim(data_fname))

  ierr = PIO_def_var(pio_file, PIO_VAR_INT_NAME, PIO_int, (/pio_dim/), pio_var_int)
  PIO_TF_CHECK_ERR(ierr, "Failed to define a var of int type : " // trim(data_fname))

  ierr = PIO_def_var(pio_file, PIO_VAR_REAL_NAME, PIO_real, (/pio_dim/), pio_var_real)
  PIO_TF_CHECK_ERR(ierr, "Failed to define a var of real type : " // trim(data_fname))

  ierr = PIO_def_var(pio_file, PIO_VAR_DBL_NAME, PIO_double, (/pio_dim/), pio_var_double)
  PIO_TF_CHECK_ERR(ierr, "Failed to define a var of double type : " // trim(data_fname))

  ierr = PIO_enddef(pio_file)
  PIO_TF_CHECK_ERR(ierr, "Failed to end redef mode : " // trim(data_fname))

  ! Write the int type variable out (data conversion might occur: real => int, double => int)
  call PIO_write_darray(pio_file, pio_var_int, wiodesc, wbuf, ierr)
  PIO_TF_CHECK_ERR(ierr, "Failed to write darray on a var of int type : " // trim(data_fname))

  ! Write the real type variable out (data conversion might occur: int => real, double => real)
  call PIO_write_darray(pio_file, pio_var_real, wiodesc, wbuf, ierr)
  PIO_TF_CHECK_ERR(ierr, "Failed to write darray on a var of real type : " // trim(data_fname))

  ! Write the double type variable out (data conversion might occur: int => double, real => double)
  call PIO_write_darray(pio_file, pio_var_double, wiodesc, wbuf, ierr)
  PIO_TF_CHECK_ERR(ierr, "Failed to write darray on a var of double type : " // trim(data_fname))

#ifdef PIO_TEST_CLOSE_OPEN_FOR_SYNC
  call PIO_closefile(pio_file)

  ierr = PIO_openfile(pio_tf_iosystem_, pio_file, tgv_iotype, data_fname, PIO_nowrite)
  PIO_TF_CHECK_ERR(ierr, "Could not reopen file " // trim(data_fname))

  ierr = PIO_inq_varid(pio_file, PIO_VAR_INT_NAME, pio_var_int)
  PIO_TF_CHECK_ERR(ierr, "Cannot inq int var " // trim(data_fname))

  ierr = PIO_inq_varid(pio_file, PIO_VAR_REAL_NAME, pio_var_real)
  PIO_TF_CHECK_ERR(ierr, "Cannot inq real var " // trim(data_fname))

  ierr = PIO_inq_varid(pio_file, PIO_VAR_DBL_NAME, pio_var_double)
  PIO_TF_CHECK_ERR(ierr, "Cannot inq double var " // trim(data_fname))
#else
  call PIO_syncfile(pio_file)
#endif

  ! Read the int type variable back (data conversion might occur: int => real, int => double)
  rbuf = 0
  call PIO_read_darray(pio_file, pio_var_int, riodesc, rbuf, ierr)
  PIO_TF_CHECK_ERR(ierr, "Failed to read darray on a var of int type : " // trim(data_fname))

  PIO_TF_CHECK_VAL((rbuf, wbuf), "Got wrong val")

  ! Read the real type variable back (data conversion might occur: real => int, real => double)
  rbuf = 0
  call PIO_read_darray(pio_file, pio_var_real, riodesc, rbuf, ierr)
  PIO_TF_CHECK_ERR(ierr, "Failed to read darray on a var of real type : " // trim(data_fname))

  PIO_TF_CHECK_VAL((rbuf, wbuf), "Got wrong val")

  ! Read the double type variable back (data conversion might occur: double => int, double => real)
  rbuf = 0
  call PIO_read_darray(pio_file, pio_var_double, riodesc, rbuf, ierr)
  PIO_TF_CHECK_ERR(ierr, "Failed to read darray on a var of double type : " // trim(data_fname))

  PIO_TF_CHECK_VAL((rbuf, wbuf), "Got wrong val")

  call PIO_closefile(pio_file)
  call PIO_deletefile(pio_tf_iosystem_, data_fname);

  call PIO_freedecomp(pio_tf_iosystem_, wiodesc)
  call PIO_freedecomp(pio_tf_iosystem_, riodesc)

PIO_TF_AUTO_TEST_SUB_END test_data_conversion

PIO_TF_TEST_DRIVER_BEGIN
  use ncdf_simple_tests_tgv
  Implicit none
  type(file_desc_t) :: pio_file
  integer :: ret, i
  ! iotypes = valid NC types
  integer, dimension(:), allocatable :: iotypes
  character(len=PIO_TF_MAX_STR_LEN), dimension(:), allocatable :: iotype_descs
  integer :: num_iotypes

  num_iotypes = 0
  call PIO_TF_Get_nc_iotypes(iotypes, iotype_descs, num_iotypes) 
  do i=1,num_iotypes
    tgv_iotype = iotypes(i)
    ret = PIO_createfile(pio_tf_iosystem_, pio_file, tgv_iotype, tgv_fname)
    PIO_TF_CHECK_ERR(ret, "Failed to create:"//trim(iotype_descs(i))//":"//trim(tgv_fname))

    call PIO_closefile(pio_file)

    ! Make sure that global variables are set correctly before running the tests
    PIO_TF_AUTO_TESTS_RUN(trim(iotype_descs(i)))

    call PIO_deletefile(pio_tf_iosystem_, tgv_fname)
  end do
  if(allocated(iotypes)) then
    deallocate(iotypes)
    deallocate(iotype_descs)
  end if

PIO_TF_TEST_DRIVER_END
