! See copyright notice in the COPYRIGHT file.
!> Module to create Treelm environment for unit testcases.
module mus_utestEnv_module
  use, intrinsic :: iso_c_binding, only: C_NEW_LINE, c_loc
  use env_module,           only: rk, labelLen
  use tem_param_module,     only: rho0
  use tem_bc_prop_module,   only: tem_bc_prop_type, load_tem_bc_prop,         &
    &                             tem_empty_bc_prop
  use tem_property_module,  only: prp_hasbnd
  use flu_binding,          only: flu_state
  use treelmesh_module,     only: treelmesh_type
  use aotus_module,         only: open_config_chunk
  use treelmesh_module,     only: load_tem
  use tem_general_module,   only: tem_general_type, tem_load_general, tem_start
  use tem_logging_module,   only: tem_logging_load_primary
  use tem_varSys_module,    only: tem_varSys_type, tem_varSys_init, &
    &                             tem_varSys_append_stateVar,       &
    &                             tem_varSys_proc_point,            &
    &                             tem_varSys_proc_element,          &
    &                             tem_varSys_proc_setParams,        &
    &                             tem_varSys_proc_getParams,        &
    &                             tem_varSys_proc_setupIndices,     &
    &                             tem_varSys_proc_getValOfIndex

  use mus_field_prop_module,  only: mus_field_prop_type
  use mus_varSys_module, only: mus_varSys_solverData_type

  implicit none

  private

  public :: init_fieldProp
  public :: init_varSys
  public :: load_env

  character, parameter :: nl = C_NEW_LINE

  character(len=200), parameter :: cubeconf =                                  &
    &   'mesh = {' // nl                                                       &
    & //'         predefined = "cube",' // nl                                  &
    & //'         origin = {0.0, 0.0, 0.0},' // nl                             &
    & //'         length=1.0,' // nl                                           &
    & //'         refinementLevel = 1' // nl                                   &
    & //'       }' // nl

contains

  !> Create the treelm environment for unit tests.
  subroutine load_env(tree, boundary, general )
    !---------------------------------------------------------------------------
    type(treelmesh_type), intent(out) :: tree
    type(tem_bc_prop_type), intent(out) :: boundary 
    type(tem_general_type), intent(out) :: general
    !---------------------------------------------------------------------------
    type(flu_state) :: conf
    integer :: iProp
    !---------------------------------------------------------------------------

    ! Init the Treelm environment
    call tem_start('MUSUBI unit test', 'utest', general)

    print *, "Hello from loadenv" 
    ! Open the configuration file 
    call open_config_chunk(L = conf, chunk = trim(cubeconf))

    ! load and initialize logUnit
    call tem_logging_load_primary(conf = conf,              &
      &                           rank = general%proc%rank  )

    call tem_load_general( me = general, conf = conf )

    ! Load the mesh first.
    call load_tem(me = tree, conf = conf, myPart = general%proc%rank, &
      &           nParts = general%proc%comm_size, comm = general%proc%comm )

    ! Load the properties now.
    do iprop = 1, size(tree%Property)
      ! Is the current property setting the prp_hasBnd bit?
      if (tree%global%property(iprop)%bitpos == prp_hasBnd) then
        ! Found the property setting the prp_hasBnd, load the
        ! Boundaries accordingly and leave the loop.
        call load_tem_bc_prop(me = boundary,                &
          &    offset = tree%Property(iprop)%Offset,        &
          &    nElems = tree%Property(iprop)%nElems,        &
          &    basename = trim(tree%global%dirname)//'bnd', &
          &    comm = general%proc%comm,                    & 
          &    mypart = general%proc%rank )
        exit
      end if
    end do

  end subroutine load_env

  ! fill fieldProp with values of omega and rho0
  subroutine init_fieldProp( fieldProp, omega, rho0 )
    type( mus_field_prop_type ) :: fieldProp
    real(kind=rk) :: omega
    real(kind=rk) :: rho0

    allocate( fieldProp%fluid%omLvl(1) )
    allocate( fieldProp%fluid%dtLvl(1) )
    allocate( fieldProp%fluid%wN(1)    )

    fieldProp%fluid%omega    = omega
    fieldProp%fluid%omLvl(:) = omega
    fieldProp%fluid%dtLvl(:) = 1.0d0
    fieldProp%fluid%les%c_s  = 0.17_rk
    fieldProp%fluid%wN(:)    = omega

    allocate( fieldProp%fluid%mrt(1)   )
    allocate( fieldProp%fluid%mrt(1)%s_mrt(19) )
    fieldProp%fluid%mrt(1)%s_mrt(:) = omega

  end subroutine init_fieldProp

  ! init varSys and append pdf variable to varSys
  ! this routine is used by all compute kernel utests 
  subroutine init_varSys( varSys, sysName, QQ, solverData )
    type( tem_varSys_type ) :: varSys
    character(len=labelLen), intent(in) :: sysName
    integer, intent(in) :: QQ
    type(mus_varSys_solverData_type), target :: solverData

    integer :: pdfPos
    logical :: wasAdded
    procedure(tem_varSys_proc_point), pointer :: get_point => NULL()
    procedure(tem_varSys_proc_element), pointer :: get_element => NULL()
    procedure(tem_varSys_proc_setParams), pointer :: set_params => null()
    procedure(tem_varSys_proc_getParams), pointer :: get_params => null()
    procedure(tem_varSys_proc_setupIndices), pointer :: &
      &                                      setup_indices => null()
    procedure(tem_varSys_proc_getValOfIndex), pointer :: &
      &                                       get_valOfIndex => null()

    call tem_varSys_init( varSys, sysName )

    call tem_varSys_append_stateVar( me             = varSys,            &
      &                              varName        = 'pdf',             &
      &                              nComponents    = QQ,                &
      &                              method_data    = c_loc(solverData), &
      &                              get_point      = get_point,         &
      &                              get_element    = get_element,       &
      &                              set_params     = set_params,        &
      &                              get_params     = get_params,        &
      &                              setup_indices  = setup_indices,     &
      &                              get_valOfIndex = get_valOfIndex,    &
      &                              pos            = pdfPos,            &
      &                              wasAdded       = wasAdded           )

  end subroutine init_varsys

end module mus_utestEnv_module
