module MW_electrode
   use MW_kinds, only: wp, PARTICLE_NAME_LEN
   use MW_constants, only: twopi
   implicit none
   private

   ! public types
   public :: MW_electrode_t

   ! public subroutines
   public :: void_type   ! (this)
   public :: define_type ! (this, n, eta, V)
   public :: print_type ! (this, ounit)
   public :: identify
   public :: set_comz
   integer, parameter :: FORCES_NUM_TERMS = 10      !< number of terms stored for forces
                  !< tot, C_sr_gg, C_sr_pg, C_keq0_gg, C_keq0_pg, C_lr, LJ, FT, Steele, DAIM
   ! Electrode datatype
   type MW_electrode_t
      integer                      :: count = 0    !< number of atoms in this type
      integer                      :: offset = 0   !< offset in atoms data array
      character(PARTICLE_NAME_LEN) :: name = ""    !< Name of this electrode
      real(wp)                     :: eta = 0.0_wp !< charge gaussian width
      real(wp)                     :: charge = 0.0_wp !< charge gaussian runtime value
      real(wp)                     :: V = 0.0_wp   !< potential applied to this electrode
      logical                      :: piston = .false. !< 1 if npt-piston simulation
      real(wp)                     :: piston_pressure = 0.0_wp !< pressure if npt-piston
      real(wp), allocatable        :: force_ions(:,:) !< total force in z direction from ions
      real(wp)                     :: velocity = 0.0_wp !< velocity in z
      real(wp)                     :: mass = 0.0_wp
      real(wp)                     :: comz = 0.0_wp
      real(wp)                     :: comz0 = 0.0_wp
      real(wp)                     :: ltf = 0.0_wp !< Thomas-Fermi length
      real(wp)                     :: voronoi_volume = 0.0_wp !< Voronoi_volume of the electrode atoms
      logical                      :: dump_lammps = .true.    !< does the electrode type need the lammps data to be dumped?
      logical                      :: dump_xyz = .true.       !< does the electrode type need the xyz data to be dumped?
      logical                      :: dump_trajectories = .true. !< does ion need the trajectories data to be dumped?
      logical                      :: dump_pdb = .true.       !< does ion need the pdb data to be dumped?
   end type MW_electrode_t

contains

   !================================================================================
   !> Define an electrode
   !
   subroutine define_type(this, name, n, eta, charge, V, piston, pressure, mass, ltf, voronoi, &
                  dump_lammps, dump_xyz, dump_trajectories, dump_pdb)
      use MW_kinds, only: wp
      use MW_errors, only: MW_errors_allocate_error => allocate_error
      implicit none
      ! Parameters
      ! ----------
      type(MW_electrode_t), intent(inout) :: this
      character(*), intent(in) :: name !< electrode name
      integer, intent(in) :: n !< number of atoms in this electrode
      real(wp), intent(in) :: eta  !< charge gaussian width
      real(wp), intent(in) :: charge  !< charge gaussian
      real(wp), intent(in) :: V !< potential applied to this electrode
      logical, intent(in) :: piston !< 1 if npt-piston simulation
      real(wp), intent(in) :: pressure !< pressure applied to this electrode
      real(wp), intent(in) :: mass !< total mass of this electrode
      real(wp), intent(in) :: ltf
      real(wp), intent(inout) :: voronoi
      logical,  intent(in), optional :: dump_lammps  ! if the lammps data needs to be dumped
      logical,  intent(in), optional :: dump_xyz     ! if the xyz data needs to be dumped
      logical,  intent(in), optional :: dump_trajectories  ! if the trajectories data needs to be dumped
      logical,  intent(in), optional :: dump_pdb     ! if the pdb data needs to be dumped

      integer :: ierr

      this%count = n
      this%offset = 0
      this%name = name
      this%eta = eta
      this%charge = charge
      this%V = V
      this%piston = piston
      this%piston_pressure = pressure
      allocate(this%force_ions(3, FORCES_NUM_TERMS), stat=ierr)
      if (ierr /= 0) then
         call MW_errors_allocate_error("allocate_data_arrays", "electrode.f90", ierr)
      end if
      this%force_ions(:,:) = 0.0_wp
      this%mass = mass
      this%velocity = 0.0_wp
      this%comz = 0.0_wp
      this%comz0 = 0.0_wp
      this%ltf = ltf
      if (voronoi == 0.0_wp) then
         voronoi = (twopi/eta)**(3./2.)
      endif
      this%voronoi_volume = voronoi

      if (present(dump_lammps)) then
         this%dump_lammps = dump_lammps
      else
         this%dump_lammps = .true.
      end if

      if (present(dump_xyz)) then
         this%dump_xyz = dump_xyz
      else
         this%dump_xyz = .true.
      end if

      if (present(dump_trajectories)) then
         this%dump_trajectories = dump_trajectories
      else
         this%dump_trajectories = .true.
      end if

      if (present(dump_pdb)) then
         this%dump_pdb = dump_pdb
      else
         this%dump_pdb = .true.
      end if


   end subroutine define_type

   !================================================================================
   !> Print an electrode data structure
   subroutine set_comz(this, comz)
      implicit none
      type(MW_electrode_t), intent(inout) :: this
      real(wp), intent(in) :: comz 

      this%comz0 = comz
      this%comz = comz

   end subroutine set_comz

   !================================================================================
   !> Void an electrode data structure
   subroutine void_type(this)
      use MW_errors, only: MW_errors_deallocate_error => deallocate_error
      implicit none
      type(MW_electrode_t), intent(inout) :: this
      integer :: ierr

      this%count = 0
      this%offset = 0
      this%name = ""
      this%eta = 0.0_wp
      this%charge = 0.0_wp
      this%V = 0.0_wp
      this%piston = .false.
      this%piston_pressure = 0.0_wp
      if (allocated(this%force_ions)) then
         deallocate(this%force_ions, stat=ierr)
         if (ierr /= 0) then
            call MW_errors_deallocate_error("deallocate_data_arrays", "electrode.f90", ierr)
         end if
      end if
      this%mass = 0.0_wp
      this%velocity = 0.0_wp
      this%ltf = 0.0_wp
      this%voronoi_volume = 0.0_wp
      this%dump_lammps = .true.
      this%dump_xyz = .true.
      this%dump_trajectories = .true.
      this%dump_pdb = .true.

   end subroutine void_type

   !================================================================================
   !> Print an electrode data structure
   subroutine print_type(this, ounit)
      implicit none
      type(MW_electrode_t), intent(in) :: this
      integer,              intent(in) :: ounit

      write(ounit, '("|electrode| ",a8,": ",i6," atoms at potential V = ",es12.5," eta = ",es12.5)') &
            this%name, this%count, this%V, this%eta
      if (this%piston) then
         write(ounit, '("|electrode| ",a8,": npt-piston at pressure ",es12.5," mass = ",es12.5)') &
            this%name, this%piston_pressure, this%mass
      end if
   end subroutine print_type

   !================================================================================
   ! Search for an electrode in a list and return the index in the list
   !
   ! The first matching index is returned.
   ! 0 is returned if no match found
   subroutine identify(electrode_name, electrodes, electrode_index)

      character(*),                 intent(in)  :: electrode_name  ! name of electrode to search
      type(MW_electrode_t), dimension(:), intent(in)  :: electrodes      ! list of electrodes
      integer,                      intent(out) :: electrode_index ! index of electrode in the list

      integer :: i

      electrode_index = 0
      do i = 1, size(electrodes,1)
         if (electrode_name == electrodes(i)%name) then
            electrode_index = i
            exit
         end if
      end do
   end subroutine identify

end module MW_electrode
