module classes_translated_positions

use, intrinsic :: iso_fortran_env, only: DP => REAL64
use data_constants, only: num_dimensions
use procedures_errors, only: warning_continue
use procedures_checks, only: check_array_size, check_positive, check_increase_factor
use classes_periodic_box, only: Abstract_Periodic_Box
use classes_component_coordinates, only: Abstract_Component_Coordinates
use module_move_tuning, only: Concrete_Move_Tuning_Parameters, set_increase_factor
use classes_moved_coordinates, only: Abstract_Moved_Coordinates

implicit none

private

    type, extends(Abstract_Moved_Coordinates), public :: Concrete_Translated_Positions
    private
        class(Abstract_Periodic_Box), pointer :: periodic_box => null()
        class(Abstract_Component_Coordinates), pointer :: positions => null()
        real(DP) :: delta(num_dimensions) = 0._DP
        type(Concrete_Move_Tuning_Parameters) :: tuning_parameters
        real(DP) :: current_increase_factor = 1._DP
        logical :: max_factor_reached = .false.
    contains
        procedure :: construct => Concrete_construct
        procedure :: destroy => Concrete_destroy
        procedure :: increase_delta => Concrete_increase_delta
        procedure :: decrease_delta => Concrete_decrease_delta
        procedure :: get => Concrete_get
    end type Concrete_Translated_Positions

contains

    subroutine Concrete_construct(this, periodic_box, positions, initial_delta, tuning_parameters)
        class(Concrete_Translated_Positions), intent(out) :: this
        class(Abstract_Periodic_Box), target, intent(in) :: periodic_box
        class(Abstract_Component_Coordinates), target, intent(in) :: positions
        real(DP), intent(in) :: initial_delta(:)
        type(Concrete_Move_Tuning_Parameters), intent(in) :: tuning_parameters

        this%periodic_box => periodic_box
        this%positions => positions
        call check_array_size("Concrete_Translated_Positions: construct", "initial_delta", &
            initial_delta, num_dimensions)
        call check_positive("Concrete_Translated_Positions: construct", "initial_delta", &
            initial_delta)
        this%delta = initial_delta

        call check_increase_factor("Concrete_Translated_Positions: construct", "increase_factor", &
            tuning_parameters%increase_factor)
        this%tuning_parameters%increase_factor = tuning_parameters%increase_factor
        this%current_increase_factor = this%tuning_parameters%increase_factor
        call check_increase_factor("Concrete_Translated_Positions: construct", &
            "increase_factor_max", tuning_parameters%increase_factor_max)
        this%tuning_parameters%increase_factor_max = tuning_parameters%increase_factor_max
    end subroutine Concrete_construct

    subroutine Concrete_destroy(this)
        class(Concrete_Translated_Positions), intent(inout) :: this

        this%positions => null()
        this%periodic_box => null()
    end subroutine Concrete_destroy

    subroutine Concrete_increase_delta(this)
        class(Concrete_Translated_Positions), intent(inout) :: this

        if (this%max_factor_reached) return
        call set_increase_factor("Concrete_Translated_Positions", this%current_increase_factor, &
            this%tuning_parameters, this%max_factor_reached)
        this%delta = this%current_increase_factor * this%delta
    end subroutine Concrete_increase_delta

    subroutine Concrete_decrease_delta(this)
        class(Concrete_Translated_Positions), intent(inout) :: this

        this%delta = this%delta / this%current_increase_factor
    end subroutine Concrete_decrease_delta

    function Concrete_get(this, i_particle) result(moved_position)
        real(DP) :: moved_position(num_dimensions)
        class(Concrete_Translated_Positions), intent(in) :: this
        integer, intent(in) :: i_particle

        real(DP) :: rand(num_dimensions)

        call random_number(rand)
        moved_position = this%positions%get(i_particle) + (rand - 0.5_DP) * this%delta
        moved_position = this%periodic_box%folded(moved_position)
    end function Concrete_get

end module classes_translated_positions
