! 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_last_time_step_t type and related functions

!> Holds values from the previous time-step needed to perform calculations.
module monarch_last_time_step

  use monarch_constants,                    only : mint, mflt, mdbl
  use monarch_util,                         only : monarch_dimensions_t

  implicit none
  private

  public :: monarch_last_time_step_t

  !> Holds values from the previous time-step needed to perform calculations
  type :: monarch_last_time_step_t
  private
    !> Task memory dimensions
    type(monarch_dimensions_t) :: mem_dims
    !> Accumulated precipitation [m]
    real(kind=mdbl), allocatable :: accumulated_precip( :,: )
    !> Convective precipitation [m]
    real(kind=mdbl), allocatable :: convective_precip( :,: )
  contains
    !> Update the values for the last time step
    procedure :: update
    !> Get the accumulated precipitation [m]
    procedure :: accumulated_precip__m
    !> Get the convective precipitaiton [m]
    procedure :: convective_precip__m
    !> Finalize a last_time_step_t object
    final :: finalize
  end type monarch_last_time_step_t

  !> Last time step constructor
  interface monarch_last_time_step_t
    procedure :: constructor
  end interface monarch_last_time_step_t

contains

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

  !> Create a new monarch_last_time_step_t object
  !!
  !! Create a new monarch_last_time_step_t object, set the model dimensions based
  !! on information in a monarch_diagnostics_t object, and initialize all values
  !! to correspond to t = -1 * dt.
  function constructor( diag ) result( new_obj )

    use monarch_diagnostics,                only : monarch_diagnostics_t

    !> A new monarch_last_time_step_t object
    type(monarch_last_time_step_t), pointer :: new_obj
    !> Diagnostics object with system dimensions
    type(monarch_diagnostics_t) :: diag

    integer(kind=mint) :: WE_s, WE_e, SN_s, SN_e

    ! Create a new last time step object
    allocate( new_obj )

    ! Get the system dimensions
    new_obj%mem_dims = diag%task_memory_dimensions( )
    WE_s             = new_obj%mem_dims%lower%WE_idx( )
    WE_e             = new_obj%mem_dims%upper%WE_idx( )
    SN_s             = new_obj%mem_dims%lower%SN_idx( )
    SN_e             = new_obj%mem_dims%upper%SN_idx( )

    ! Allocate space for the data arrays
    allocate( new_obj%accumulated_precip( WE_s:WE_e, SN_s:SN_e ) )
    allocate( new_obj%convective_precip( WE_s:WE_e, SN_s:SN_e ) )

    ! Initialize the arrays
    new_obj%accumulated_precip( :,: ) = 0.0
    new_obj%convective_precip( :,: ) = 0.0

  end function constructor

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

  !> Update the values of the last time step
  subroutine update( this, diag )

    use monarch_diagnostics,                only : monarch_diagnostics_t

    !> Last time step object
    class(monarch_last_time_step_t), intent(inout) :: this
    !> Diagnostics object to use to get new values
    type(monarch_diagnostics_t), intent(in) :: diag

    integer(kind=mint) :: WE_idx, SN_idx, layer_idx

    ! Update the arrays
    call diag%copy_accumulated_precip__m( this%accumulated_precip,           &
                                          this%mem_dims )
    call diag%copy_convective_precip__m( this%convective_precip,             &
                                          this%mem_dims )

  end subroutine update

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

  !> Get the accumulated precipitation [m]
  pure function accumulated_precip__m( this, indices ) result( precip )

    use monarch_util,                       only : monarch_point_t

    !> Accumulated precipitation [m]
    real(kind=mdbl) :: precip
    !> Last time step object
    class(monarch_last_time_step_t), intent(in) :: this
    !> Column indices
    type(monarch_point_t), intent(in) :: indices

    precip = this%accumulated_precip( indices%WE_idx( ), indices%SN_idx( ) )

  end function accumulated_precip__m

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

  !> Get the convective precipitaiton [m]
  pure function convective_precip__m( this, indices ) result( precip )

    use monarch_util,                       only : monarch_point_t

    !> Convective precipitation [m]
    real(kind=mdbl) :: precip
    !> Last time step object
    class(monarch_last_time_step_t), intent(in) :: this
    !> Column indices
    type(monarch_point_t), intent(in) :: indices

    precip = this%convective_precip( indices%WE_idx( ), indices%SN_idx( ) )

  end function convective_precip__m

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

  !> Finalize a last_time_step_t object
  subroutine finalize( this )

    !> Last time step object
    type(monarch_last_time_step_t), intent(inout) :: this

    if( allocated( this%accumulated_precip ) ) &
        deallocate( this%accumulated_precip )
    if( allocated( this%convective_precip ) ) &
        deallocate( this%convective_precip )

  end subroutine finalize

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

end module monarch_last_time_step
