module classes_component_dipole_moments

use, intrinsic :: iso_fortran_env, only: DP => REAL64
use data_constants, only: num_dimensions
use procedures_checks, only: check_positive
use classes_coordinates, only: Abstract_Coordinates
use classes_component_coordinates, only: Abstract_Component_Coordinates

implicit none

private

    type, extends(Abstract_Coordinates), abstract, public :: Abstract_Component_Dipole_Moments
    private
        class(Abstract_Component_Coordinates), pointer :: orientations => null()
        real(DP) :: norm = 0._DP
    contains
        procedure :: construct => Abstract_construct
        procedure :: destroy => Abstract_destroy
        procedure :: get_num => Abstract_get_num
        procedure :: get_norm => Abstract_get_norm
        procedure :: get => Abstract_get
    end type Abstract_Component_Dipole_Moments

    type, extends(Abstract_Component_Dipole_Moments), public :: Concrete_Component_Dipole_Moments

    end type Concrete_Component_Dipole_Moments

    type, extends(Abstract_Component_Dipole_Moments), public :: Null_Component_Dipole_Moments
    contains
        procedure :: construct => Null_construct
        procedure :: destroy => Null_destroy
        procedure :: get_num => Null_get_num
        procedure :: get_norm => Null_get_norm
        procedure :: get => Null_get
    end type Null_Component_Dipole_Moments

contains

!implementation Abstract_Component_Dipole_Moments

    subroutine Abstract_construct(this, norm, orientations)
        class(Abstract_Component_Dipole_Moments), intent(out) :: this
        real(DP), intent(in) :: norm
        class(Abstract_Component_Coordinates), target, intent(in) :: orientations

        call check_positive("Abstract_construct", "norm", norm)
        this%norm = norm
        this%orientations => orientations
    end subroutine Abstract_construct

    subroutine Abstract_destroy(this)
        class(Abstract_Component_Dipole_Moments), intent(inout) :: this

        this%orientations => null()
    end subroutine Abstract_destroy

    pure integer function Abstract_get_num(this) result(num_moments)
        class(Abstract_Component_Dipole_Moments), intent(in) :: this

        num_moments = this%orientations%get_num()
    end function Abstract_get_num

    pure real(DP) function Abstract_get_norm(this) result(norm)
        class(Abstract_Component_Dipole_Moments), intent(in) :: this

        norm = this%norm
    end function Abstract_get_norm

    pure function Abstract_get(this, i_particle) result(dipole_moment)
        class(Abstract_Component_Dipole_Moments), intent(in) :: this
        integer, intent(in) :: i_particle
        real(DP) :: dipole_moment(num_dimensions)

        dipole_moment = this%norm * this%orientations%get(i_particle)
    end function Abstract_get

!end implementation Abstract_Component_Dipole_Moments

!implementation Null_Component_Dipole_Moments

    subroutine Null_construct(this, norm, orientations)
        class(Null_Component_Dipole_Moments), intent(out) :: this
        real(DP), intent(in) :: norm
        class(Abstract_Component_Coordinates), target, intent(in) :: orientations
    end subroutine Null_construct

    subroutine Null_destroy(this)
        class(Null_Component_Dipole_Moments), intent(inout) :: this
    end subroutine Null_destroy

    pure integer function Null_get_num(this) result(num_moments)
        class(Null_Component_Dipole_Moments), intent(in) :: this
        num_moments = 0
    end function Null_get_num

    pure real(DP) function Null_get_norm(this) result(norm)
        class(Null_Component_Dipole_Moments), intent(in) :: this
        norm = 0._DP
    end function Null_get_norm

    pure function Null_get(this, i_particle) result(dipole_moment)
        class(Null_Component_Dipole_Moments), intent(in) :: this
        integer, intent(in) :: i_particle
        real(DP) :: dipole_moment(num_dimensions)
        dipole_moment = 0._DP
    end function Null_get

!end implementation Null_Component_Dipole_Moments

end module classes_component_dipole_moments
