! Define an ion data structure
module MW_ion
   use MW_kinds, only: wp, PARTICLE_NAME_LEN
   implicit none
   private

   ! public types
   public :: MW_ion_t

   ! public subroutines
   public :: define_type
   public :: void_type
   public :: print_type
   public :: identify
   public :: get_mass
   public :: copy
   public :: set_mobility

   ! Ion datatype
   type MW_ion_t
      integer                          :: count = 0               !< number of ions of this type
      integer                          :: offset = 0              !< Offset in ions data arrays
      character(len=PARTICLE_NAME_LEN) :: name = ""               !< name
      real(wp)                         :: charge = 0.0_wp         !< electric charge
      real(wp)                         :: polarizability = 0.0_wp !< polarizability
      real(wp)                         :: selfdaimD = 0.0_wp      !< deformability 1
      real(wp)                         :: selfdaimbeta = 0.0_wp   !< deformability 2
      real(wp)                         :: selfdaimzeta = 0.0_wp   !< deformability 3
      real(wp)                         :: mass = 0.0_wp           !< mass in amu
      logical                          :: mobile = .true.         !< does ion move or not?
      logical                          :: is_fourth_site = .false.         !< is the ion the fourth site of a (water) model?
      logical                          :: dump_efg = .false.      !< does ion need the efg to be dumped?
      logical                          :: dump_lammps = .true.    !< does ion need the lammps data to be dumped?
      logical                          :: dump_xyz = .true.       !< does ion 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_ion_t

contains

   !================================================================================
   ! Define the data structure
   subroutine define_type(this, name, n, charge, polarizability, selfdaimD, selfdaimbeta,selfdaimzeta,  mass, mobile, &
         is_fourth_site, dump_efg, dump_lammps, dump_xyz, dump_trajectories, dump_pdb)
      use MW_kinds, only: wp
      implicit none
      type(MW_ion_t),   intent(inout) :: this         ! structure to be defined

      character(len=*), intent(in) :: name            ! name of the ion
      integer,          intent(in) :: n               ! number of ions of this type
      real(wp),         intent(in) :: charge          ! electric charge
      real(wp),         intent(in) :: polarizability  ! polarizability
      real(wp),         intent(in) :: selfdaimD       ! deformability
      real(wp),         intent(in) :: selfdaimbeta    ! deformability
      real(wp),         intent(in) :: selfdaimzeta    ! deformability
      real(wp),         intent(in) :: mass            ! mass (in amu)
      logical,          intent(in) :: mobile          ! mobility
      logical,          intent(in) :: is_fourth_site  ! flag if fourth site
      logical,          intent(in), optional :: dump_efg     ! if the efg needs to be dumped
      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

      this%name           = name
      this%count          = n
      this%offset         = 0
      this%charge         = charge
      this%polarizability = polarizability
      this%selfdaimD      = selfdaimD 
      this%selfdaimbeta   = selfdaimbeta
      this%selfdaimzeta   = selfdaimzeta
      this%mass           = mass
      this%mobile         = mobile
      this%is_fourth_site = is_fourth_site

      if (present(dump_efg)) then
         this%dump_efg = dump_efg
      else
         this%dump_efg = .false.
      end if

      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

   !================================================================================
   ! Void the data structure
   subroutine void_type(this)
      implicit none
      type(MW_ion_t), intent(inout) :: this

      this%name           = ""
      this%count          = 0
      this%offset         = 0
      this%charge         = 0.0_wp
      this%polarizability = 0.0_wp
      this%selfdaimD      = 0.0_wp
      this%selfdaimbeta   = 0.0_wp
      this%selfdaimzeta   = 0.0_wp
      this%mass           = 0.0_wp
      this%mobile         = .true.
      this%is_fourth_site = .false.
      this%dump_efg       = .false.
      this%dump_lammps    = .true.
      this%dump_xyz       = .true.
      this%dump_trajectories = .true.
      this%dump_pdb       = .true.
   end subroutine void_type

   !================================================================================
   ! Print the data structure to ounit
   subroutine print_type(this, ounit)
      implicit none
      type(MW_ion_t), intent(in) :: this          ! structure to be printed
      integer,        intent(in) :: ounit         ! output unit

      if (this%mobile) then
         write(ounit,'("|ion| ",a8,": ",i8," mobile ions with q = ",es12.5," alpha = ",'//&
               'es12.5," m = ",es12.5," and deformabilities",3es12.5)') &
               this%name, this%count, this%charge, this%polarizability, this%mass,&
               this%selfdaimD,this%selfdaimbeta,this%selfdaimzeta
      else
         write(ounit,'("|ion| ",a8,": ",i8," fixed  ions with q = ",es12.5," alpha = ",'//&
               'es12.5," m = ",es12.5," and deformabilities",3es12.5)') &
               this%name, this%count, this%charge, this%polarizability, this%mass,&
               this%selfdaimD,this%selfdaimbeta,this%selfdaimzeta
      end if

   end subroutine print_type

   !================================================================================
   ! copy the src data structure into dest
   subroutine copy(dest, src)
      implicit none
      type(MW_ion_t), intent(inout) :: dest !> destination data structure
      type(MW_ion_t), intent(in)    :: src  !> source data structure

      dest%name   = src%name
      dest%count  = src%count
      dest%offset = src%offset
      dest%charge = src%charge
      dest%polarizability = src%polarizability
      dest%selfdaimD = src%selfdaimD
      dest%selfdaimbeta = src%selfdaimbeta
      dest%selfdaimzeta = src%selfdaimzeta
      dest%mass   = src%mass
      dest%mobile = src%mobile
      dest%is_fourth_site = src%is_fourth_site
      dest%dump_efg = src%dump_efg
      dest%dump_lammps = src%dump_lammps
      dest%dump_xyz = src%dump_xyz
      dest%dump_trajectories = src%dump_trajectories
      dest%dump_pdb = src%dump_pdb
   end subroutine copy

   !================================================================================
   ! Search for an ion 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(ion_name, ions, ion_index)
      implicit none
      character(*),                 intent(in)  :: ion_name  ! name of ion to search
      type(MW_ion_t), dimension(:), intent(in)  :: ions      ! list of ions
      integer,                      intent(out) :: ion_index ! index of ion in the list

      integer :: i

      ion_index = 0
      do i = 1, size(ions,1)
         if (ion_name == ions(i)%name) then
            ion_index = i
            exit
         end if
      end do
   end subroutine identify

   !================================================================================
   ! Returns the mass of an ion in a list identified by its name
   !
   subroutine get_mass(ion_name, ions, ion_mass)
      use MW_kinds, only: wp
      use MW_errors, only: MW_errors_parameter_error => parameter_error
      implicit none
      character(*),                 intent(in)  :: ion_name ! name of ion to search for
      type(MW_ion_t), dimension(:), intent(in)  :: ions     ! list of ions
      real(wp),                     intent(out) :: ion_mass ! mass of the ion

      integer :: ion_index

      ion_mass = 0.0_wp
      call identify(ion_name, ions, ion_index)
      if (ion_index <= 0) then
         call  MW_errors_parameter_error("MW_ion_getmass", "ions.f90", &
               "ion_name", ion_name)
      end if
      ion_mass = ions(ion_index)%mass

   end subroutine get_mass

   !================================================================================
   ! Returns the mobile character of an ion
   !
   subroutine set_mobility(this, mobile)
      implicit none
      type(MW_ion_t), intent(inout) :: this     ! ion
      logical,        intent(in)    :: mobile   ! can this ion move?
      this%mobile = mobile
   end subroutine set_mobility


end module MW_ion
