!> Define an electrode from the user point of view
module MW_electrode_parameters
   use MW_kinds, only: wp, PARTICLE_NAME_LEN
   use MW_species_parameters, only: SPECIES_NAME_LENGTH
   use MW_species, only: MW_species_t, &
         MW_species_identify => identify, &
         MW_species_set_electrode => set_electrode
   use MW_errors, only: MW_errors_runtime_error => runtime_error
   use MW_configuration_line, only: MW_configuration_line_t, &
         MW_configuration_line_seek_next_data => seek_next_data, &
         MW_configuration_line_get_word => get_word, &
         max_word_length
   implicit none
   private

   ! Public type
   ! -----------
   public :: MW_electrode_parameters_t

   ! Public subroutines
   ! ------------------
   public :: read_parameters
   public :: void_type
   public :: print_type
   public :: set_offsets
   public :: set_species_index
   public :: set_index

   integer, parameter, public :: ELECTRODE_NAME_LENGTH = PARTICLE_NAME_LEN

   type MW_electrode_parameters_t
      character(ELECTRODE_NAME_LENGTH) :: name = "" !< name of this electrode
      integer                          :: index = 0 !< index of this electrode
      character(SPECIES_NAME_LENGTH)   :: species_name = ""
      integer                          :: species_index = 0  !< Species type used to fill this electrode
      integer                          :: count = 0    !< number of atoms in this electrode (same as species%count
      integer                          :: offset = 0   !< offset in charge data array for this electrode
      real(wp)                         :: potential = 0.0_wp   !< potential applied to this electrode
      logical                          :: piston = .false. !< 1 if npt-piston simulation
      real(wp)                         :: pressure = 0.0_wp !< pressure if npt-piston
      real(wp)                         :: thomas_fermi_length
      real(wp)                         :: voronoi_volume
   end type MW_electrode_parameters_t

contains

   !================================================================================
   !> Void an electrode data structure
   subroutine void_type(this)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: this
      this%name = ""
      this%index = 0
      this%species_name = ""
      this%species_index = 0
      this%count = 0
      this%offset = 0
      this%potential = 0.0_wp
      this%piston = .false.
      this%pressure = 0.0_wp
      this%thomas_fermi_length = 0.0_wp
      this%voronoi_volume = 0.0_wp
   end subroutine void_type

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

      write(ounit,'("|electrode| name: ",a16)') this%name
      write(ounit,'("|electrode| species: ",a8," (",i3,")")') this%species_name, this%species_index
      write(ounit,'("|electrode| count: ",i8," offset: ", i8)') this%count, this%offset
      write(ounit,'("|electrode| potential: ",es12.5)') this%potential
      if (this%piston) then
         write(ounit,'("|electrode| npt-piston at pressure: ",es12.5)') this%pressure
      end if

   end subroutine print_type

   ! ================================================================================
   !> Set the name of the electrode
   subroutine set_name(this, name)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: this
      character(len=*),                intent(in)    :: name
      this%name = name
   end subroutine set_name

   ! ================================================================================
   !> Set the index of the electrode
   subroutine set_index(this, index)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: this
      integer, intent(in) :: index
      this%index = index
   end subroutine set_index

   ! ================================================================================
   !> Set the species type name for the electrode
   subroutine set_species_name(this, species_name)
      implicit none
      ! Parameters
      ! ----------
      type(MW_electrode_parameters_t), intent(inout) :: this
      character(*),                    intent(in)    :: species_name       !< name of the species

      this%species_name = species_name
   end subroutine set_species_name

   ! ================================================================================
   !> Set the species type index for the electrode
   subroutine set_species_index(this, species)
      implicit none
      ! Parameters
      ! ----------
      type(MW_electrode_parameters_t), intent(inout) :: this
      type(MW_species_t),              intent(inout) :: species(:) !< array of species

      ! Local
      ! -----
      integer :: species_index
      character(256) :: errmsg

      call MW_species_identify(this%species_name, species, species_index)
      if (species_index > 0) then
         this%species_index = species_index
         call MW_species_set_electrode(species(species_index), this%name, this%index)
      else
         write(errmsg,'("Unknown species name,",a8,", for electrode ",a8)') &
               this%species_name, this%name
         call MW_errors_runtime_error("set_species","electrode_parameters.f90", &
               errmsg)
      end if

   end subroutine set_species_index

   ! ================================================================================
   !> Set the species type for the electrode
   subroutine set_potential(this, potential)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: this
      real(wp),                        intent(in)    :: potential
      this%potential = potential
   end subroutine set_potential

   ! ================================================================================
   !> Set the pressure for the electrode
   subroutine set_pressure(this, pressure, direction)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: this
      real(wp),                        intent(in)    :: pressure, direction
      this%piston = .true.
      this%pressure = direction * pressure
   end subroutine set_pressure

   ! ================================================================================
   !> Set offsets for a list of electrodes
   subroutine set_offsets(electrodes)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: electrodes(:)

      integer :: i, n, offset
      n = size(electrodes,1)
      offset = 0
      do i = 1, n
         electrodes(i)%offset = offset
         offset = offset + electrodes(i)%count
      end do
   end subroutine set_offsets

   ! ================================================================================
   !> Set the thomas_fermi length of the electrode
   subroutine set_thomas_fermi_length(this, thomas_fermi_length)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: this
      real(wp), intent(in) :: thomas_fermi_length
      this%thomas_fermi_length = thomas_fermi_length
   end subroutine set_thomas_fermi_length

   ! ================================================================================
   !> Set the voronoi volume of the electrode atoms
   subroutine set_voronoi_volume(this, voronoi_volume)
      implicit none
      type(MW_electrode_parameters_t), intent(inout) :: this
      real(wp), intent(in) :: voronoi_volume
      this%voronoi_volume = voronoi_volume
   end subroutine set_voronoi_volume

   !================================================================================
   !> Read electrode parameters from runtime.inpt
   !!
   !! Block format:
   !! -------------
   !! electrode_type *block*
   !!   name *string*
   !!   species *string*
   !!   potential *real*
   !!   piston *real* *int*
   !!
   !! Parameters
   !! ----------
   !! funit : integer
   !!    handle to the runtime input file
   !!
   !! line_num : integer
   !!    current line number in the runtime input file
   subroutine read_parameters(this, funit, line_num, compute_force)
      implicit none
      ! Parameters
      ! ----------
      type(MW_electrode_parameters_t), intent(inout) :: this
      integer, intent(in) :: funit
      integer, intent(inout) :: line_num
      logical, intent(inout) :: compute_force

      ! Local
      ! -----
      type(MW_configuration_line_t) :: config_line
      character(max_word_length) :: keyword
      character(ELECTRODE_NAME_LENGTH) :: name
      character(SPECIES_NAME_LENGTH) :: species
      real(wp) :: potential, pressure, direction, thomas_fermi_length, voronoi_volume
      integer :: name_defined, species_defined, potential_defined, piston_defined
      integer :: thomas_fermi_length_defined, voronoi_volume_defined
      character(256) :: errmsg

      ! Void data structure
      call void_type(this)

      ! Set default values
      name = "****"
      species = "****"
      potential = 0.0_wp
      pressure = 0.0_wp
      direction = 1.0_wp
      thomas_fermi_length = 0.0_wp
      voronoi_volume = 0.0_wp

      ! Flag each section as undefined (line number will be stored)
      name_defined = 0
      species_defined = 0
      potential_defined = 0
      piston_defined = 0
      thomas_fermi_length_defined = 0
      voronoi_volume_defined = 0

      call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      keywordLoop: do while(config_line%num_words > 0)

         ! Look for keyword and read corresponding value
         call MW_configuration_line_get_word(config_line, 1, keyword)

         select case(keyword)

         case("name")
            if (name_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |name|. First definition at line ",i4,".")') &
                     line_num, name_defined
               call MW_errors_runtime_error("read_parameters", "electrode_parameters.f90", errmsg)
            end if
            name_defined = line_num

            ! Read name parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for name parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_parameters","electrode_parameters.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, name)

         case("species")
            if (species_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |species|. First definition at line ",i4,".")') &
                     line_num, species_defined
               call MW_errors_runtime_error("read_parameters", "electrode_parameters.f90", errmsg)
            end if
            species_defined = line_num

            ! Read species name parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for species parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_parameters","electrode_parameters.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, species)

         case("potential")
            if (potential_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |potential|. First definition at line ",i4,".")') &
                     line_num, potential_defined
               call MW_errors_runtime_error("read_parameters", "electrode_parameters.f90", errmsg)
            end if
            potential_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for potential parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_parameters","electrode_parameters.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, potential)

         case("piston")
            if (piston_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |piston|. First definition at line ",i4,".")') &
                     line_num, piston_defined
               call MW_errors_runtime_error("read_parameters", "electrode_parameters.f90", errmsg)
            end if
            piston_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 3) then
               write(errmsg,'("Invalid format for potential parameters at line ",i4)') line_num
               call MW_errors_runtime_error("read_parameters","electrode_parameters.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, pressure)
            call MW_configuration_line_get_word(config_line, 3, direction)

         case("thomas_fermi_length")
            if (thomas_fermi_length_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |thomas_fermi_length|. First definition at line ",i4,".")') &
                     line_num, thomas_fermi_length_defined
               call MW_errors_runtime_error("read_parameters", "electrode_parameters.f90", errmsg)
            end if
            thomas_fermi_length_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for Thomas-Fermi length at line ",i4)') line_num
               call MW_errors_runtime_error("read_parameters","electrode_parameters.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, thomas_fermi_length)

         case("voronoi_volume")
            if (voronoi_volume_defined > 0) then
               write(errmsg,'("At line ",i4,", redefinition of |voronoi_volume|. First definition at line ",i4,".")') &
                     line_num, voronoi_volume_defined
               call MW_errors_runtime_error("read_parameters", "electrode_parameters.f90", errmsg)
            end if
            voronoi_volume_defined = line_num

            ! Read length parameters
            if (config_line%num_words /= 2) then
               write(errmsg,'("Invalid format for Voronoi volume at line ",i4)') line_num
               call MW_errors_runtime_error("read_parameters","electrode_parameters.f90", &
                     errmsg)
            end if
            call MW_configuration_line_get_word(config_line, 2, voronoi_volume)

         case default
            ! Keyword not expected in box section assume end of box section
            ! Backspace to previous line and give hand back to parent box
            backspace(funit)
            line_num = line_num - 1
            exit keywordLoop

         end select
         call MW_configuration_line_seek_next_data(config_line, funit, line_num)
      end do keywordLoop

      call set_name(this, name)
      call set_species_name(this, species)
      call set_potential(this, potential)
      if (piston_defined > 0) then
         call set_pressure(this, pressure, direction)
         compute_force = .true.
      end if
      call set_thomas_fermi_length(this, thomas_fermi_length)
      call set_voronoi_volume(this, voronoi_volume)

   end subroutine read_parameters

end module MW_electrode_parameters
