!#########################################################################
!		
!    Copyright (C) 2003-2012 Department of Physics and Astronomy,
!                            University of Rochester,
!                            Rochester, NY
!
!    chombo_declarations.f90 is part of AstroBEAR.
!
!    AstroBEAR is free software: you can redistribute it and/or modify	  
!    it under the terms of the GNU General Public License as published by 
!    the Free Software Foundation, either version 3 of the License, or    
!    (at your option) any later version.
!
!    AstroBEAR is distributed in the hope that it will be useful, 
!    but WITHOUT ANY WARRANTY; without even the implied warranty of
!    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
!    GNU General Public License for more details.
!
!    You should have received a copy of the GNU General Public License
!    along with AstroBEAR.  If not, see <http://www.gnu.org/licenses/>.
!
!#########################################################################
!> @file chombo_declarations.f90
!! @brief Main file for module ChomboDeclarations

!> @defgroup ChomboDeclarations Chombo Declarations
!! @ingroup IOChomboGroup
!! @brief Declarations file for the Chombo output format.

!> Declarations file for the Chombo output format.
!! @author Brandon D. Shroyer
!! @date 10-12-2010
!! @ingroup ChomboDeclarations
MODULE ChomboDeclarations

#if defined _HDF5
  USE HDF5
  USE GlobalDeclarations
  USE HDF5Declarations
  USE IODeclarations
  IMPLICIT NONE
  SAVE
  PUBLIC

  LOGICAL, PARAMETER :: CHOMBO_HANDLE_READ = .FALSE.
  LOGICAL, PARAMETER :: CHOMBO_HANDLE_WRITE = .TRUE.

  INTEGER, PARAMETER :: COOLING_SOURCE_WRITE = 0
  INTEGER, PARAMETER :: CYLINDRICAL_SOURCE_WRITE = 1

  INTEGER, PARAMETER :: COOLING_SOURCE_READ = 2
  INTEGER, PARAMETER :: CYLINDRICAL_SOURCE_READ = 3

  INTEGER, PARAMETER :: CHOMBO_ROOT_LEVEL = -2
  INTEGER, PARAMETER :: CHOMBO_DOMAIN_LEVEL = -1

  !> An object containing information used to manage an hdf5 chombo file
  !! @details This structure contains pointers used to pass data between subroutines
  !! in a chombo restart so care should be taken to deallocate it properly
  TYPE ChomboHandle

     INTEGER(HID_T) :: file_handle
     INTEGER(HID_T) :: root_group_id
     INTEGER(HID_T) :: particle_group_id
     INTEGER(HID_T) :: source_group_id
     INTEGER(HID_T) :: level_id
     INTEGER(HID_T) :: child_level_id
     LOGICAL :: lWrite

     INTEGER(HSIZE_T) :: box_offset
     INTEGER(HSIZE_T) :: childbox_offset
     INTEGER(HSIZE_T) :: childbox_count_offset
     INTEGER(HSIZE_T) :: domainbox_offset
     INTEGER(HSIZE_T) :: q_offset
     INTEGER(HSIZE_T) :: aux_offset
     INTEGER(HSIZE_T) :: costmap_offset
     INTEGER(HSIZE_T) :: particle_offset
     INTEGER(HSIZE_T) :: source_offset

  END TYPE ChomboHandle

  TYPE(ChomboHandle), POINTER :: curr_handle

CONTAINS

  !> Allocates a new Chombo handle object and opens a file with it.
  !! @param filename The name of the file being opened.
  !! @param chandle A pointer to a chombo handle object.
  !! @param lWrite A logical flag that when true opens a file for write access
  SUBROUTINE CreateChomboHandle(filename, chandle, lWrite)

    CHARACTER(LEN=*) :: filename
    TYPE(ChomboHandle), POINTER :: chandle
    LOGICAL :: lWrite
    INTEGER :: i_err
    INTEGER(HID_T) :: hid_property_id

    ALLOCATE(chandle)

    chandle%box_offset = 0
    chandle%childbox_offset = 0
    chandle%childbox_count_offset = 0
    chandle%q_offset = 0
    chandle%aux_offset = 0
    chandle%particle_offset = 0
    chandle%source_offset = 0
    chandle%lWrite = lWrite
    ! Initializes the HDF5 library.  This function is safe to call
    ! even if it has already been called.
    CALL h5open_f(i_err)
    ! Create property list
    CALL h5pcreate_f(H5P_FILE_ACCESS_F, hid_property_id, i_err)

    IF (lParallelHDF5) THEN
#if defined _PHDF5
       CALL h5pset_fapl_mpio_f(hid_property_id, MPI_COMM_WORLD, MPI_INFO_NULL, i_err)
#else
       IF (MPI_ID == 0) WRITE(*,*) 'Parallel HDF5 support requested, but not available'
#endif
    END IF
    IF (lWrite) THEN
       ! Create chombo file.
       CALL h5fcreate_f(filename, H5F_ACC_TRUNC_F, chandle%file_handle, i_err, access_prp = hid_property_id)
       IF (MPI_ID == 0 .AND. i_err /= 0) THEN
          PRINT*, ''
          PRINT*, ''
          PRINT*, '******************************************************************************'
          PRINT*, '******************************************************************************'
          PRINT*, '**************       Unable to open file for writing.        *****************'
          PRINT*, '**************  Perhaps you need to create an out directory? *****************'
          PRINT*, '******************************************************************************'
          PRINT*, '******************************************************************************'
          STOP
       END IF
    ELSE
       ! Create read-only chombo file.
       CALL h5fopen_f(TRIM(filename), H5F_ACC_RDONLY_F, chandle%file_handle, i_err, access_prp = hid_property_id)
    END IF
    ! Close property list, now that it's purpose (file creation) has been fulfilled.
    CALL h5pclose_f(hid_property_id, i_err)
    ! Open the root group and save the handle.
    CALL h5gopen_f(chandle%file_handle, "/", chandle%root_group_id, i_err)

  END SUBROUTINE CreateChomboHandle

  !> Resets all offsets in the given handle to 0.
  !! @param chandle An active chombo handle.
  SUBROUTINE ClearChomboHandleOffsets(chandle)

    TYPE(ChomboHandle), POINTER :: chandle

    chandle%box_offset = 0
    chandle%childbox_offset = 0
    chandle%childbox_count_offset = 0
    chandle%q_offset = 0
    chandle%aux_offset = 0
    chandle%costmap_offset = 0

  END SUBROUTINE ClearChomboHandleOffsets

  !> Deallocates and nullifies a Chombo handle.  This process includes closing the root group and file.
  !! @param chandle A pointer to a chombo handle object that will be destroyed.
  SUBROUTINE CloseChomboHandle(chandle)

    TYPE(ChomboHandle), POINTER :: chandle

    INTEGER :: i_err


    IF (ASSOCIATED(chandle)) THEN

       CALL h5gclose_f(chandle%root_group_id, i_err)

       CALL h5fclose_f(chandle%file_handle, i_err)

       DEALLOCATE(chandle)
       NULLIFY(chandle)
    ELSE
       PRINT *, "CloseChomboHandleError:  handle not associated."
       STOP
    END IF

  END SUBROUTINE CloseChomboHandle

  !> Takes a level and returns a Chombo-style level_group name
  !! @param level An non-negative integer representing a level.
  !! @param level_name A character string to hold the level name.
  SUBROUTINE IO_GetLevelName(level, level_name)

    INTEGER :: level
    CHARACTER(LEN=*) :: level_name

    ! Convert the level integer into a character string.  This assumes that our code 
    ! will be using less than 100 additional levels of refinement, but that seems
    ! FAIRLY reasonable.
    IF (level >= 0 .AND. level < 100) THEN
       WRITE(level_name, '(A6,I1)') 'level_', level
    ELSE
       IF (level < 1) THEN
          WRITE(level_name, '(A12)') 'domain_level'
       ELSE
          WRITE(level_name, '(A6,I2)') 'level_', level
       END IF
    END IF

  END SUBROUTINE IO_GetLevelName

  !> Opens a level group handle and associates it with the level ID in the Chombo handle.
  !! @param chandle A ChomboHandle pointer.
  !! @param level The level of the group to be opened.
  SUBROUTINE IO_OpenCurrentLevel(chandle, level)

    TYPE(ChomboHandle), POINTER :: chandle
    INTEGER :: level

    CHARACTER(I_MAX_LEVELNAME_LENGTH) :: level_name
    INTEGER :: i_err


    ! Should fail on an invalid Chombo level.
    IF (level >= -1 .AND. level <= MaxLevel) THEN

       ! Obtain the name of the level group.
       CALL IO_GetLevelName(level, level_name)

       ! If this is a read option or a non-root level write option, then the current level
       ! has already been created, and you just open it.  Otherwise, you create a new group.
       IF (.NOT. chandle%lWrite) THEN
          ! Open the level group and save the handle.
          CALL h5gopen_f(chandle%root_group_id, TRIM(level_name), chandle%level_id, i_err)       
       ELSE
          ! Create level group.
          CALL h5gcreate_f(chandle%root_group_id, TRIM(level_name), chandle%level_id, i_err)

       END IF

    ELSE
       PRINT "('IO_OpenCurrentLevel() error: invalid level ', i8, '.')", level
       STOP
    END IF

  END SUBROUTINE IO_OpenCurrentLevel

  !> Closes the level group associated with a chombo file.
  !! @param chandle A ChomboHandle pointer.
  SUBROUTINE IO_CloseCurrentLevel(chandle)

    TYPE(ChomboHandle), POINTER :: chandle
    INTEGER :: i_err

    ! Close the level group handle associated with this chombo handle.
    CALL h5gclose_f(chandle%level_id, i_err)       

  END SUBROUTINE IO_CloseCurrentLevel


  !> Opens a new file group using the source group handle within a ChomboHandle object.
  !! @param chandle An active Chombo handle.
  !! @param source_name The name of the source group to be opened.
  !! @details This subroutine is called from the SourceControl module.  It returns the number of source objects.
  INTEGER FUNCTION Chombo_OpenSourceGroup(chandle, source_name)

    TYPE(ChomboHandle), POINTER :: chandle
    CHARACTER(LEN=*) :: source_name
    INTEGER :: i_err


    Chombo_OpenSourceGroup = 0

    ! Open the 'cooling_objects' group and save the handle.
    CALL h5gopen_f(chandle%root_group_id, source_name, chandle%source_group_id, i_err)

    ! Read in the number of source objects in this group and return the value.
    Chombo_OpenSourceGroup = Read_HDF5_Attribute_Int("num_objects", chandle%source_group_id)

  END FUNCTION Chombo_OpenSourceGroup

  !> Closes the source group handle within a ChomboHandle object.
  !! @param chandle An active Chombo handle.
  !! @details This subroutine is called from the SourceControl module.
  SUBROUTINE Chombo_CloseSourceGroup(chandle)

    TYPE(ChomboHandle), POINTER :: chandle
    INTEGER :: i_err

    IF (HandleIsValid(chandle%source_group_id)) THEN
       CALL h5gclose_f(chandle%source_group_id, i_err)
       chandle%source_offset = 0
    END IF

  END SUBROUTINE Chombo_CloseSourceGroup

  !> Opens a new file group using the particle group handle within a ChomboHandle object.
  !! @param chandle An active Chombo handle.
  !! @details This subroutine is called from the ParticleControl module.  On a read operation it returns the number of source objects.
  INTEGER FUNCTION Chombo_OpenParticleGroup(chandle)

    TYPE(ChomboHandle), POINTER :: chandle
    INTEGER :: i_err


    Chombo_OpenParticleGroup = 0

    IF (chandle%lWrite) THEN
       ! Create a new particles group within the root level
       CALL h5gcreate_f(chandle%root_group_id, "particles", chandle%particle_group_id, i_err)
    ELSE
       ! Open the 'cooling_objects' group and save the handle.
       CALL h5gopen_f(chandle%root_group_id, "particles", chandle%particle_group_id, i_err)

       ! Read in the number of source objects in this group and return the value.
       Chombo_OpenParticleGroup = Read_HDF5_Attribute_Int("num_particles", chandle%particle_group_id)
    END IF

  END FUNCTION Chombo_OpenParticleGroup

  !> Closes the particle group handle within a ChomboHandle object.
  !! @param chandle An active Chombo handle.
  !! @details This subroutine is called from the ParticleControl module.
  SUBROUTINE Chombo_CloseParticleGroup(chandle)

    TYPE(ChomboHandle), POINTER :: chandle
    INTEGER :: i_err

    IF (HandleIsValid(chandle%particle_group_id)) THEN
       CALL h5gclose_f(chandle%particle_group_id, i_err)
       chandle%particle_offset = 0
    END IF

  END SUBROUTINE Chombo_CloseParticleGroup


#endif
END MODULE ChomboDeclarations
!!@}
