module MW_dihedrals
#ifndef MW_SERIAL
   use MPI
#endif
   use MW_timers
   use MW_kinds, only: wp
   use MW_localwork, only: MW_localwork_t
   use MW_parallel, only: MW_COMM_WORLD
   use MW_molecule, only: MW_molecule_t
   use MW_box, only: MW_box_t
   use MW_ion, only: MW_ion_t
   implicit none
   private

   public :: forces
   public :: energy
contains

   ! ================================================================================
   !> Compute the forces due to dihedral potential
   subroutine forces(num_pbc, localwork, box, molecules, ions, xyz_ions, force,stress_tensor)
      implicit none
      ! Parameters
      ! ----------
      integer, intent(in) :: num_pbc !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in) :: localwork
      type(MW_box_t), intent(in) :: box
      type(MW_molecule_t), intent(in) :: molecules(:)
      type(MW_ion_t), intent(in) :: ions(:)
      real(wp), intent(in) :: xyz_ions(:,:)
      real(wp), intent(inout) :: force(:,:)
      real(wp), intent(inout) :: stress_tensor(:,:)
      integer :: ierr,count

      select case(num_pbc)
      case(2)
         call forces_2DPBC(localwork, box, molecules, ions, xyz_ions, force,stress_tensor)
      case(3)
         call forces_3DPBC(localwork, box, molecules, ions, xyz_ions, force,stress_tensor)
      end select
#ifndef MW_SERIAL
      count = size(force,1)*size(force,2)
      call MPI_Allreduce(MPI_IN_PLACE, force, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)

      count = size(stress_tensor,1)*size(stress_tensor,2)      ! this should always be 9
      call MPI_Allreduce(MPI_IN_PLACE, stress_tensor, count, MPI_DOUBLE_PRECISION, MPI_SUM, MW_COMM_WORLD, ierr)      
#endif
   end subroutine forces

   ! ================================================================================
   !> Compute the forces due to dihedral potential
   subroutine energy(num_pbc, localwork, box, molecules, ions, xyz_ions, h)
      implicit none
      ! Parameters
      ! ----------
      integer, intent(in) :: num_pbc !< number of periodic boundary conditions
      type(MW_localwork_t), intent(in) :: localwork
      type(MW_box_t), intent(in) :: box
      type(MW_molecule_t), intent(in) :: molecules(:)
      type(MW_ion_t), intent(in) :: ions(:)
      real(wp), intent(in) :: xyz_ions(:,:)
      real(wp), intent(inout) :: h
      integer :: ierr

      select case(num_pbc)
      case(2)
         call energy_2DPBC(localwork, box, molecules, ions, xyz_ions, h)
      case(3)
         call energy_3DPBC(localwork, box, molecules, ions, xyz_ions, h)
      end select
#ifndef MW_SERIAL
      call MPI_Allreduce(MPI_IN_PLACE, h, 1, MPI_DOUBLE_PRECISION, MPI_SUM,MW_COMM_WORLD, ierr)
#endif
   end subroutine energy

   ! ================================================================================
   include 'dihedrals_2DPBC.inc'
   include 'dihedrals_3DPBC.inc'
   ! ================================================================================
   include 'minimum_image_displacement_2DPBC.inc'
   include 'minimum_image_displacement_3DPBC.inc'
   include 'minimum_image_distance_2DPBC.inc'
   include 'minimum_image_distance_3DPBC.inc'
   ! ================================================================================
end module MW_dihedrals
