! See copyright notice in the COPYRIGHT file.
! ****************************************************************************** !
!> Contains data types and routines needed for coupling via apesmate
module tem_coupling_module

  ! include treelm modules
  use env_module,           only: rk, labelLen, pathLen, globalMaxLevels

  use aotus_module,         only: flu_State, aot_get_val, aoterr_Fatal
  use aot_table_module,     only: aot_table_open,  &
    &                             aot_table_close, &
    &                             aot_table_length

  use tem_logging_module,    only: logUnit
  use tem_aux_module,        only: tem_abort
  use tem_grow_array_module, only: grw_intArray_type, grw_realArray_type
  use tem_comm_module,       only: tem_communication_type


  implicit none

  private

  public :: tem_aps_coupling_type
  public :: tem_aps_load_coupling

  ! type which include all exchange points information
  type cpl_value_type
    !> number of points per level
    integer :: nPnts = 0

    !> Global process ids to evaluate the points
    !! It is deallocated after recvBuffer is filled
    integer, allocatable :: pntRanks(:)

    !> Evaluated variable value on each point.
    !! If variable is time-independent then values are evaluated and stored
    !! at initialization stage, in this case point arrays are not stored.
    !! nComp = nScalars in the tem_coupling_type%varnames
    !! Access: (iVal-1)*nComp + iComp
    !type(grw_realArray_type) :: evalVal
    real(kind=rk), allocatable :: evalVal(:)

    !> Receive communication buffer to fill evalVal
    type(tem_communication_type) :: recvBuffer
  end type cpl_value_type

  !> Coupling description defined in config file from load space time function
  !! which is called from load boundary condition or load sources
  type tem_aps_coupling_type

    !> Remote domain label to get data from
    character(len=labelLen) :: rem_domLabel
    !> Domain ID of remote domain label
    integer :: rem_domID

    !> Number of variables to get from remote domain
    integer :: nVars
    !> List of variables to get from domain
    character(len=labelLen), allocatable :: varNames(:)

    !> nScalars of varNames
    !! Must be same as nComps in stFun
    integer :: nScalars

    !> Used to decided whether this spacetime functions are used
    !! for surface or volume i.e boundary or source.
    !! Boundary is treated as surface and source as volume
    !! coupling type can be rather surface or volume.
    !! For boundary. isSurface = 0
    !! For volume, isSurface = 1
    integer :: isSurface = -1
    !> store value on each level
    type(cpl_value_type) :: valOnLvl(globalMaxLevels)
  end type tem_aps_coupling_type


contains


  ! ****************************************************************************
  !> This routine loads coupling defintion from boundary condition table
  subroutine tem_aps_load_coupling(me, thandle, conf)
    ! -------------------------------------------------------------------------!
    !> Coupling description to be filled from config file
    type(tem_aps_coupling_type), intent(out) :: me
    !> Boundary condition sub table
    integer, intent(in) :: thandle
    !> Lua script to obtain the configuration data from.
    type(flu_State), intent(in) :: conf
    ! -------------------------------------------------------------------------!
    integer :: iError
    integer, allocatable :: vError(:)
    ! -------------------------------------------------------------------------!

    ! get the domain name with whom we want to couple
    call aot_get_val( L       = conf,            &
      &               thandle = thandle,         &
      &               key     = 'domain_from',   &
      &               val     = me%rem_domLabel, &
      &               ErrCode = iError           )
    if (iError .NE. 0) then
      write(logUnit(1),*) ' No coupling domain for the coupling boundary' &
        &                 //' is defined, coupling can not work, abort...'
      call tem_abort
    end if

    ! get the list of variables
    ! look for the request variable table
    call aot_get_val( val       = me%varNames,    &
      &               ErrCode   = vError,         &
      &               maxLength = 100,            &
      &               L         = conf,           &
      &               thandle   = thandle,        &
      &               key       = 'input_varname' )

    if ( any(btest(vError, aoterr_Fatal)) ) then
      write(logUnit(1),*) 'ERROR: could not input_varname for coupling variable'
      call tem_abort()
    end if

    me%nVars = size(me%varNames)

   end subroutine tem_aps_load_coupling
  ! ****************************************************************************


end module tem_coupling_module
! ****************************************************************************** !
