!=========================================================================
!
! Included from file wfn_io_hdf5.f90.
! You are not expected to understand this. --JIM
!
!=========================================================================

#ifdef READ
  #define HDF5_READ_WRITE(x) hdf5_read ## x
  #define READ_WRITE(x) read ## x
  #define INTENT out
  #define FLAVOR_INTENT inout
  #define H5D_READ_WRITE call h5dread_f
  #define H5F_FILE_ACCESS H5F_ACC_RDONLY_F
#else
  #define HDF5_READ_WRITE(x) hdf5_write ## x
  #define READ_WRITE(x) write ## x
  #define INTENT in
  #define FLAVOR_INTENT in
  #define H5D_READ_WRITE call h5dwrite_f
  #define H5F_FILE_ACCESS H5F_ACC_RDWR_F
#endif

#ifdef HDF5
  #define NAME(x) READ_WRITE(_hdf5 ## x)
#endif

#ifdef TEMP_WFN_DATA
  #ifdef TEMP_COMPLEX
    #define TEMP_SCALAR complex(DPC)
    #define MPI_TEMP_SCALAR MPI_COMPLEX_DPC
    #define LONGNAME(x) NAME(x ## _complex)
  #else
    #define TEMP_SCALAR real(DPC)
    #define MPI_TEMP_SCALAR MPI_REAL_DP
    #define LONGNAME(x) NAME(x ## _real)
  #endif
#endif

! begin read/write header
#ifdef TEMP_HEADER
subroutine NAME(_header_type)(sFileName, sheader, iflavor, kp, gvec, syms, crys)
  character(len=*), intent(in) :: sFileName
  character(len=3), intent(inout) :: sheader
  integer, intent(FLAVOR_INTENT) :: iflavor
  type(kpoints), intent(INTENT) :: kp
  type(gspace), intent(INTENT) :: gvec
  type(symmetry), intent(INTENT) :: syms
  type(crystal), intent(INTENT) :: crys
  
  ! set values based on epsilon calculation
  logical :: is_get=.false.
  logical :: wfnflag=.true.
  
  PUSH_SUB(NAME(_header_type))
  
  if (peinf%inode == 0) then
    call READ_WRITE(_info)(TRUNC(sFileName),iflavor)
    call READ_WRITE(_kpoints)(TRUNC(sFileName),kp)
    call READ_WRITE(_gspace)(TRUNC(sFileName),gvec)
    call READ_WRITE(_symmetry)(TRUNC(sFileName),syms)
    call READ_WRITE(_crystal)(TRUNC(sFileName),crys)
  endif
#ifdef READ
#ifdef MPI
  if (peinf%npes > 1) then
    call MPI_BCAST(is_get, 1, MPI_LOGICAL, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(kp%nspin, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(gvec%ng, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(syms%ntran, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(syms%cell_symmetry, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%nat, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(gvec%ecutrho, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(gvec%FFTgrid, 3, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%celvol, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%alat, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%avec, 9, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%adot, 9, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%recvol, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%blat, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%bvec, 9, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%bdot, 9, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(syms%mtrx(1,1,1), 3*3*48, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(syms%tnp, 3*48, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    if (wfnflag) then
      call MPI_BCAST(kp%nrk, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%mnband, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%ngkmax, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%ecutwfc, 1, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%kgrid, 3, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%shift, 3, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    endif
  endif
#endif
  if(peinf%inode > 0 ) then
    allocate(crys%atyp (crys%nat))
    allocate(crys%apos (3, crys%nat))
    if (wfnflag) then
      allocate(kp%ngk (kp%nrk))
      allocate(kp%w (kp%nrk))
      allocate(kp%rk (3, kp%nrk))
      allocate(kp%ifmin (kp%nrk, kp%nspin))
      allocate(kp%ifmax (kp%nrk, kp%nspin))
      allocate(kp%el (kp%mnband, kp%nrk, kp%nspin))
      allocate(kp%occ (kp%mnband, kp%nrk, kp%nspin))
    endif
  endif
#endif
  
#if defined READ && defined MPI
  if (peinf%npes > 1) then
    call MPI_BCAST(crys%atyp, crys%nat, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    call MPI_BCAST(crys%apos, 3*crys%nat, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    if (wfnflag) then
      call MPI_BCAST(kp%ngk, kp%nrk, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%w, kp%nrk, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%rk, 3*kp%nrk, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%ifmin, kp%nspin*kp%nrk, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%ifmax, kp%nspin*kp%nrk, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%el(1,1,1), kp%mnband*kp%nspin*kp%nrk, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
      call MPI_BCAST(kp%occ(1,1,1), kp%mnband*kp%nspin*kp%nrk, MPI_DOUBLE_PRECISION, 0, MPI_COMM_WORLD, mpierr)
    endif
  endif
#endif
  POP_SUB(NAME(_header_type))
end subroutine NAME(_header_type)

subroutine READ_WRITE(_info)(sFileName, iflavor)
  character(len=*), intent(in) :: sFileName
  integer, intent(INTENT) :: iflavor
  
  integer(HID_T) :: hidFile
  integer :: iError
  
  PUSH_SUB(READ_WRITE(_info))
  
  call h5fopen_f(sFileName, H5F_FILE_ACCESS, hidFile, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/info', iflavor, iError)
  call h5fclose_f(hidFile, iError)
  
  POP_SUB(READ_WRITE(_info))
end subroutine READ_WRITE(_info)
  
subroutine READ_WRITE(_gspace)(sFileName,gvec)
  character(len=*), intent(in) :: sFileName
  type(gspace), intent(INTENT) :: gvec
  
  integer(HID_T) :: hidFile
  integer :: iError
  
  PUSH_SUB(READ_WRITE(_gspace))
  
  call h5fopen_f(sFileName, H5F_FILE_ACCESS, hidFile, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/gspace/ng', gvec%ng, iError)
  call HDF5_READ_WRITE(_double)(hidFile, '/mf_header/gspace/ecutrho', gvec%ecutrho, iError)
  call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/gspace/FFTgrid', (/3/), gvec%FFTgrid, iError)
  call h5fclose_f(hidFile, iError)
  
  POP_SUB(READ_WRITE(_gspace))
end subroutine READ_WRITE(_gspace)

subroutine NAME(_gvectors)(sFileName, ng, gvec)
  character(len=*), intent(in) :: sFileName
  integer, intent(in) :: ng !< used size of array
  integer, intent(INTENT) :: gvec(:, :) !< (3, ng_bound)
  
  integer(HID_T) :: hidFile
  integer :: iError
  logical :: bcast_, dont_read_
  
  PUSH_SUB(NAME(_gvectors))
  
  dont_read_=.false.
  bcast_=.not. dont_read_
  
  if(peinf%inode == 0) then
    call h5fopen_f(sFileName, H5F_FILE_ACCESS, hidFile, iError)
    call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/gspace/components', (/3,ng/), gvec, iError)
    call h5fclose_f(hidFile, iError)
  endif

#ifdef READ
#ifdef MPI
  if(peinf%npes > 1) then
    if(bcast_) then
      call MPI_BCAST(gvec(1,1), 3 * ng, MPI_INTEGER, 0, MPI_COMM_WORLD, mpierr)
    endif
  endif
#endif
#endif
  
  POP_SUB(NAME(_gvectors))
end subroutine NAME(_gvectors)

subroutine READ_WRITE(_symmetry)(sFileName,syms)
  character(len=*), intent(in) :: sFileName
  type(symmetry), intent(INTENT) :: syms
  
  integer(HID_T) :: hidFile
  integer :: iError
  
  PUSH_SUB(READ_WRITE(_symmetry))
  
  call h5fopen_f(sFileName, H5F_FILE_ACCESS, hidFile, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/symmetry/ntran', syms%ntran, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/symmetry/cell_symmetry', syms%cell_symmetry, iError)
  call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/symmetry/mtrx', (/3, 3, 48/), syms%mtrx, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/symmetry/tnp', (/3, 48/), syms%tnp, iError)
  call h5fclose_f(hidFile, iError)
  
  POP_SUB(READ_WRITE(_symmetry))
end subroutine READ_WRITE(_symmetry)

subroutine READ_WRITE(_crystal)(sFileName,crys)
  character(len=*), intent(in) :: sFileName
  type(crystal), intent(INTENT) :: crys
  
  integer(HID_T) :: hidFile
  integer :: iError
  
  PUSH_SUB(READ_WRITE(_crystal))
  
  call h5fopen_f(sFileName, H5F_FILE_ACCESS, hidFile, iError)
  call HDF5_READ_WRITE(_double)(hidFile, '/mf_header/crystal/celvol', crys%celvol, iError)
  call HDF5_READ_WRITE(_double)(hidFile, '/mf_header/crystal/recvol', crys%recvol, iError)
  call HDF5_READ_WRITE(_double)(hidFile, '/mf_header/crystal/alat', crys%alat, iError)
  call HDF5_READ_WRITE(_double)(hidFile, '/mf_header/crystal/blat', crys%blat, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/crystal/nat', crys%nat, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/crystal/avec', (/3, 3/), crys%avec, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/crystal/bvec', (/3, 3/), crys%bvec, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/crystal/adot', (/3, 3/), crys%adot, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/crystal/bdot', (/3, 3/), crys%bdot, iError)
  
#ifdef READ
  SAFE_ALLOCATE(crys%atyp, (crys%nat))
  SAFE_ALLOCATE(crys%apos, (3,crys%nat))
#endif
  
  call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/crystal/atyp', (/crys%nat/), crys%atyp, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/crystal/apos', (/3, crys%nat/), crys%apos, iError)
  call h5fclose_f(hidFile, iError)
  
  POP_SUB(READ_WRITE(_crystal))
end subroutine READ_WRITE(_crystal)

subroutine READ_WRITE(_kpoints)(sFileName,kp)
  character(len=*), intent(in) :: sFileName
  type(kpoints), intent(INTENT) :: kp
  
  integer(HID_T) :: hidFile
  integer :: iError
  
  PUSH_SUB(READ_WRITE(_kpoints))
  
  call h5fopen_f(sFileName, H5F_FILE_ACCESS, hidFile, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/kpoints/nspin', kp%nspin, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/kpoints/nrk', kp%nrk, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/kpoints/mnband', kp%mnband, iError)
  call HDF5_READ_WRITE(_int)(hidFile, '/mf_header/kpoints/ngkmax', kp%ngkmax, iError)
  call HDF5_READ_WRITE(_double)(hidFile, '/mf_header/kpoints/ecutwfc', kp%ecutwfc, iError)
  call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/kpoints/kgrid', (/3/), kp%kgrid, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/kpoints/shift', (/3/), kp%shift, iError)
  
#ifdef READ
  SAFE_ALLOCATE(kp%ngk, (kp%nrk))
  SAFE_ALLOCATE(kp%ifmin, (kp%nrk, kp%nspin))
  SAFE_ALLOCATE(kp%ifmax, (kp%nrk, kp%nspin))
  SAFE_ALLOCATE(kp%w, (kp%nrk))
  SAFE_ALLOCATE(kp%rk, (3,kp%nrk))
  SAFE_ALLOCATE(kp%el, (kp%mnband,kp%nrk,kp%nspin))
  SAFE_ALLOCATE(kp%occ, (kp%mnband,kp%nrk,kp%nspin))
#endif
  
  call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/kpoints/ngk', (/kp%nrk/), kp%ngk, iError)
  call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/kpoints/ifmin', (/kp%nrk, kp%nspin/), kp%ifmin, iError)
  call HDF5_READ_WRITE(_int_array)(hidFile, '/mf_header/kpoints/ifmax', (/kp%nrk, kp%nspin/), kp%ifmax, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/kpoints/w', (/kp%nrk/), kp%w, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/kpoints/rk', (/3, kp%nrk/), kp%rk, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/kpoints/el', (/kp%mnband, kp%nrk, kp%nspin/), kp%el, iError)
  call HDF5_READ_WRITE(_double_array)(hidFile, '/mf_header/kpoints/occ', (/kp%mnband, kp%nrk, kp%nspin/), kp%occ, iError)
  call h5fclose_f(hidFile, iError)
  
  POP_SUB(READ_WRITE(_kpoints))
end subroutine READ_WRITE(_kpoints)
#endif
! end read/write header



#undef READ_WRITE
#undef INTENT
#undef FLAVOR_INTENT
#undef NAME
#undef TEMP_SCALAR
#undef MPI_TEMP_SCALAR
#undef LONGNAME

#undef HDF5_READ_WRITE
#undef H5D_READ_WRITE
#undef H5F_FILE_ACCESS
