! Copyright 2008-2021 Earth Sciences Department, BSC-CNS
!
! This file is part of MONARCH.
!
! MONARCH 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.
!
! MONARCH 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 MONARCH. If not, see <http://www.gnu.org/licenses/>.

!> \file
!> The monarch_aerosol_representation_ptr and
!! monarch_aerosol_group_collection_ptr types

!> Types for building and managing sets of aerosol representations and their
!! group collections
module monarch_aerosol_representation_pointer
! MONARCH always has MPI
#define MONARCH_USE_MPI

  use monarch_aero_rep_fixed
  use monarch_aerosol_representation
  use monarch_constants,                       only : mint

  implicit none
  private

  public :: monarch_aerosol_representation_ptr,                              &
            monarch_aerosol_group_collection_ptr

  ! Aerosol representation types
  !> Original MONARCH fixed aerosol representation
  integer(kind=mint), parameter :: AERO_REP_FIXED    = 1

  ! Aerosol group types
  !> Groups of the MONARCH fixed aerosol representation
  integer(kind=mint), parameter :: AERO_GROUPS_FIXED = 1

  !> Pointer for building aerosol representation arrays
  type monarch_aerosol_representation_ptr
    !> Pointer to an aerosol representation
    class(monarch_aerosol_representation_t), pointer :: val => null( )
  contains
    !> Dereference the pointer
    procedure :: dereference
    !> Determine the number of bytes required to pack the given object
    procedure :: pack_size
    !> Pack the given object to the buffer, advancing position
    procedure :: bin_pack
    !> Unpack the given object from the buffer, advancing position
    procedure :: bin_unpack
    !> Finalize the pointer
    final :: ptr_finalize
  end type monarch_aerosol_representation_ptr

  !> Pointer for building arrays of group collections
  type :: monarch_aerosol_group_collection_ptr
    class(monarch_aerosol_group_collection_t), pointer :: val => null()
  contains
    !> Determine the number of bytes required to pack the given object
    procedure :: pack_size => group_pack_size
    !> Pack the given object to the buffer, advancing position
    procedure :: bin_pack => group_bin_pack
    !> Unpack the given object from the buffer, advancing position
    procedure :: bin_unpack => group_bin_unpack
  end type monarch_aerosol_group_collection_ptr

contains
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Dereference an aerosol_representation pointer
  elemental subroutine dereference( this )

    !> Pointer to an aerosol representation
    class(monarch_aerosol_representation_ptr), intent(inout) :: this

    this%val => null( )

  end subroutine dereference

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Finalize an aerosol_representation pointer
  elemental subroutine ptr_finalize( this )

    !> Pointer to an aerosol representation
    type(monarch_aerosol_representation_ptr), intent(inout) :: this

    if( associated( this%val ) ) deallocate( this%val )

  end subroutine ptr_finalize

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Determine the number of bytes required to pack the given object
  integer(kind=mint) function pack_size( this, comm )

    use monarch_mpi,           only : mnrc_mpi_pack_size_integer

    !> Pointer to an aerosol representation
    class(monarch_aerosol_representation_ptr), intent(in) :: this
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

    pack_size = mnrc_mpi_pack_size_integer( 1, comm )                      + &
                this%val%pack_size( comm )

  end function pack_size

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Pack the given object to the buffer, advancing position
  subroutine bin_pack( this, buffer, pos, comm )

    use monarch_mpi,           only : mnrc_mpi_pack_integer
    use monarch_util,          only : assert, die_msg

    !> Pointer to an aerosol representation
    class(monarch_aerosol_representation_ptr), intent(in) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer(kind=mint), intent(inout) :: pos
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

#ifdef MONARCH_USE_MPI
    integer(kind=mint) :: prev_position

    prev_position = pos
    select type( aero_rep => this%val )
      type is( monarch_aero_rep_fixed_t )
        call mnrc_mpi_pack_integer( buffer, pos, AERO_REP_FIXED, comm )
      class default
        call die_msg( 881154944, "Invalid aerosol representation type" )
    end select
    call this%val%bin_pack( buffer, pos, comm )
    call assert( 932275957, &
         pos - prev_position <= this%pack_size( comm ) )
#endif

  end subroutine bin_pack

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Unpack the given object from the buffer, advancing position
  subroutine bin_unpack( this, buffer, pos, comm, int_state )

    use module_SOLVER_INTERNAL_STATE,       only : solver_internal_state
    use monarch_mpi,                        only : mnrc_mpi_unpack_integer
    use monarch_util,                       only : assert, die_msg, to_string

    !> Pointer to an aerosol representation
    class(monarch_aerosol_representation_ptr), intent(inout) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer(kind=mint), intent(inout) :: pos
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm
    !> Solver internal state
    type(solver_internal_state), target, optional, intent(inout) :: int_state

#ifdef MONARCH_USE_MPI
    integer(kind=mint) :: prev_position, aero_rep_type

    prev_position = pos
    call mnrc_mpi_unpack_integer( buffer, pos, aero_rep_type, comm )
    select case( aero_rep_type )
      case( AERO_REP_FIXED )
        if( present( int_state ) ) then
          this%val => monarch_aero_rep_fixed_t( int_state )
        else
          this%val => monarch_aero_rep_fixed_t( )
        endif
      case default
        call die_msg( 396388300, "Invalid aerosol representation type: "//   &
                      to_string( aero_rep_type ) )
    end select
    call this%val%bin_unpack( buffer, pos, comm )
    call assert( 959885559, &
         pos - prev_position <= this%pack_size( comm ) )
#endif

  end subroutine bin_unpack

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Determine the number of bytes required to pack the given object
  integer(kind=mint) function group_pack_size( this, comm )

    use monarch_mpi,           only : mnrc_mpi_pack_size_integer

    !> Pointer to an aerosol group collection
    class(monarch_aerosol_group_collection_ptr), intent(in) :: this
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

    group_pack_size = mnrc_mpi_pack_size_integer( 1, comm )                + &
                this%val%pack_size( comm )

  end function group_pack_size

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Pack the given object to the buffer, advancing position
  subroutine group_bin_pack( this, buffer, pos, comm )
#ifdef MONARCH_USE_MPI
    use monarch_mpi,           only : mnrc_mpi_pack_integer
    use monarch_util,          only : assert, die_msg
#endif

    !> Pointer to an aerosol group collection
    class(monarch_aerosol_group_collection_ptr), intent(in) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer(kind=mint), intent(inout) :: pos
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm

#ifdef MONARCH_USE_MPI
    integer(kind=mint) :: prev_position

    prev_position = pos
    select type( aero_groups => this%val )
      type is( monarch_aero_groups_fixed_t )
        call mnrc_mpi_pack_integer( buffer, pos, AERO_REP_FIXED, comm )
      class default
        call die_msg( 439338528, "Invalid aerosol group collection type" )
    end select
    call this%val%bin_pack( buffer, pos, comm )
    call assert( 381499969, &
         pos - prev_position <= this%pack_size( comm ) )
#endif

  end subroutine group_bin_pack

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  !> Unpack the given object from the buffer, advancing position
  subroutine group_bin_unpack( this, buffer, pos, comm, aero_rep )
#ifdef MONARCH_USE_MPI
    use monarch_mpi,           only : mnrc_mpi_unpack_integer
    use monarch_util,          only : assert, die_msg, to_string
#endif

    !> Pointer to an aerosol group collection
    class(monarch_aerosol_group_collection_ptr), intent(inout) :: this
    !> Memory buffer
    character, intent(inout) :: buffer( : )
    !> Current buffer position
    integer(kind=mint), intent(inout) :: pos
    !> MPI communicator
    integer(kind=mint), intent(in) :: comm
    !> Pointer to the parent aerosol representation
    class(monarch_aerosol_representation_t), target, intent(in) :: aero_rep

#ifdef MONARCH_USE_MPI
    type(monarch_aero_groups_fixed_t), pointer :: new_group
    integer(kind=mint) :: prev_position, aero_groups_type

    prev_position = pos
    call mnrc_mpi_unpack_integer( buffer, pos, aero_groups_type, comm )
    select case( aero_groups_type )
      case( AERO_GROUPS_FIXED )
        allocate( new_group )
        this%val => new_group
      case default
        call die_msg( 709831924, "Invalid aerosol group collection type: "//   &
                      to_string( aero_groups_type ) )
    end select
    call this%val%bin_unpack( buffer, pos, comm )
    call assert( 764311710, &
         pos - prev_position <= this%pack_size( comm ) )
#endif

  end subroutine group_bin_unpack

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

end module monarch_aerosol_representation_pointer
