module procedures_dipolar_interactions_facades_factory

use data_input_prefixes, only: volume_change_prefix
use json_module, only: json_file
use procedures_errors, only: error_exit
use types_environment_wrapper, only: Environment_Wrapper
use procedures_environment_inquirers, only: periodicity_is_xyz, total_volume_can_change
use types_component_wrapper, only: Component_Wrapper
use procedures_mixture_total_moments_factory, only: set_are_dipolar
use types_dipolar_interactions_dynamic_wrapper, only: Dipolar_Interactions_Dynamic_Wrapper
use types_dipolar_interactions_static_wrapper, only: Dipolar_Interactions_Static_Wrapper
use classes_dipolar_interactions_facade, only: Abstract_Dipolar_Interactions_Facade, &
    Scalable_Dipolar_Interactions_Facade, Unscalable_Dipolar_Interactions_Facade, &
    Null_Dipolar_Interactions_Facade
use procedures_exploration_inquirers, only: property_measure_pressure => measure_pressure

implicit none

private
public :: create, destroy

contains

    subroutine create(facades, environment, components, dipolar_interactions_dynamic, &
        dipolar_interactions_static, exploring_data)
        class(Abstract_Dipolar_Interactions_Facade), allocatable, intent(out) :: facades(:)
        type(Environment_Wrapper), intent(in) :: environment
        type(Component_Wrapper), intent(in) :: components(:, :)
        type(Dipolar_Interactions_Dynamic_Wrapper), intent(in) :: dipolar_interactions_dynamic(:)
        type(Dipolar_Interactions_Static_Wrapper), intent(in) :: dipolar_interactions_static(:)
        type(json_file), optional, intent(inout) :: exploring_data

        integer :: i_box
        logical :: boxes_size_can_change, measure_pressure
        logical :: are_dipolar(size(components, 1), size(components, 2))

        if (present(exploring_data)) then
            measure_pressure = property_measure_pressure(exploring_data, volume_change_prefix)
        else
            measure_pressure = .false.
        end if

        boxes_size_can_change = total_volume_can_change(environment%beta_pressure) .or. &
            size(environment%periodic_boxes) > 1
        call set_are_dipolar(are_dipolar, components)
        if ((boxes_size_can_change .or. measure_pressure) .and. any(are_dipolar)) then
            if (all(periodicity_is_xyz(environment%periodic_boxes))) then
                allocate(Scalable_Dipolar_Interactions_Facade :: &
                    facades(size(dipolar_interactions_static)))
            else
                allocate(Unscalable_Dipolar_Interactions_Facade :: &
                    facades(size(dipolar_interactions_static)))
            end if
        else
            allocate(Null_Dipolar_Interactions_Facade :: facades(size(dipolar_interactions_static)))
        end if

        select type (facades)
            type is (Scalable_Dipolar_Interactions_Facade)
                do i_box = 1, size(facades)
                    call facades(i_box)%construct(components(:, i_box), &
                        dipolar_interactions_dynamic(i_box), dipolar_interactions_static(i_box))
                end do
            type is (Unscalable_Dipolar_Interactions_Facade)
                do i_box = 1, size(facades)
                    call facades(i_box)%construct(environment%periodic_boxes(i_box), &
                        components(:, i_box), dipolar_interactions_dynamic(i_box), &
                        dipolar_interactions_static(i_box))
                end do
            type is (Null_Dipolar_Interactions_Facade)
            class default
                call error_exit("procedures_dipolar_interactions_facades_factory: create: "//&
                    "facades type is unknown.")
        end select
    end subroutine create

    subroutine destroy(facades)
        class(Abstract_Dipolar_Interactions_Facade), allocatable, intent(inout) :: facades(:)

        integer :: i_box

        if (allocated(facades)) then
            do i_box = size(facades), 1, -1
                call facades(i_box)%destroy()
            end do
            deallocate(facades)
        end if
    end subroutine destroy

end module procedures_dipolar_interactions_facades_factory
