module MW_lookup_table
   use MW_kinds, only: wp
   implicit none
   private

   ! Public type
   ! -----------
   public :: MW_lut_t

   ! Public subroutines
   ! ------------------
   public :: lut_init
   public :: lut_void
   public :: lut_push
   public :: lut_push_energies
   public :: lut_get
   public :: lut_check
   public :: lut_set
   public :: lut_set_all
   public :: lut_print
   public :: CHARLENGTH

   integer,               parameter   :: CHARLENGTH = 128

   type MW_lut_t
     character(CHARLENGTH), allocatable :: keys(:)
     real(wp),              allocatable :: values(:)
     integer                            :: lut_index
   end type MW_lut_t

contains

   subroutine lut_init(this, nb_items)
      type(MW_lut_t), intent(inout) :: this
      integer, intent(in)  :: nb_items
      integer              :: status
      allocate(this%keys(nb_items), stat=status)
      allocate(this%values(nb_items), stat=status)
      this%lut_index = 0 
   end subroutine lut_init
 
   subroutine lut_void(this)
      type(MW_lut_t), intent(inout) :: this
      integer              :: status
      deallocate(this%keys, stat=status)
      deallocate(this%values, stat=status)
      this%lut_index = -1 
   end subroutine lut_void
 
   subroutine lut_push(this, key, value)
      type(MW_lut_t), intent(inout) :: this
      character(*), intent(in)      :: key
      real(wp)        , intent(in)  :: value
      
      this%lut_index = this%lut_index + 1
      if(this%lut_index > Size(this%keys, 1)) call print_error("Error: table is already full")
      
      this%keys(this%lut_index) = key
      this%values(this%lut_index) = value
   end subroutine lut_push
 
   subroutine lut_push_energies(this, value)
      type(MW_lut_t), intent(inout) :: this
      real(wp)        , intent(in)  :: value
      call lut_push(this, "Hamiltonian", value) ! previously energy(1)
      call lut_push(this, "Kinetic", value)      ! previously energy(2)
      call lut_push(this, "Potential", value)      ! previously energy(3)
      call lut_push(this, "Coulomb", value)      ! previously energy(4)
      call lut_push(this, "Intramolecular", value)      ! previously energy(5)
      call lut_push(this, "van_der_Waals", value)      ! previously energy(6)
      call lut_push(this, "Total", value)      ! previously energy(7)
      call lut_push(this, "Thermostat", value)      ! previously energy(8)
      call lut_push(this, "Barostat", value)      ! 
      call lut_push(this, "Coulomb_lr", value)      ! previously energy(9)
      call lut_push(this, "Coulomb_keq0", value)      ! previously energy(10)
      call lut_push(this, "Coulomb_sr", value)      ! previously energy(11)
      call lut_push(this, "Coulomb_self", value)      ! previously energy(12)
      call lut_push(this, "Coulomb_molecule", value)      ! previously energy(13)
      call lut_push(this, "Harmonic_bond", value)      ! previously energy(14)
      call lut_push(this, "Harmonic_angle", value)      ! previously energy(15)
      call lut_push(this, "Dihedrals", value)      ! previously energy(16)
      call lut_push(this, "Fumi-Tosi", value)      ! previously energy(17)
      call lut_push(this, "Fumi-Tosi_molecule", value)      ! previously energy(18)
      call lut_push(this, "XFT", value)      ! previously energy(17)
      call lut_push(this, "XFT_molecule", value)      ! previously energy(18)
      call lut_push(this, "Lennard-Jones", value)      ! previously energy(19)
      call lut_push(this, "Lennard-Jones_cutoff", value)      ! previously energy(20)
      call lut_push(this, "DAIM", value)      ! previously energy(21)
      call lut_push(this, "DAIM_self", value)      ! previously energy(22)
      call lut_push(this, "Piston_work", value)      ! previously energy(23)
      call lut_push(this, "Plumed_bias", value) 
      call lut_push(this, "Thomas_Fermi", value) 
      call lut_push(this, "External_field", value) 
      call lut_push(this, "Steele", value) 
   end subroutine lut_push_energies
 
   subroutine lut_set(this, key, value)
      type(MW_lut_t), intent(inout) :: this
      character(*), intent(in)     :: key
      real(wp)        , intent(in)     :: value
      integer                      :: local_index
      logical                      :: found
      
      found = .FALSE. 
      if(this%lut_index .lt. Size(this%keys, 1)) call print_error("Error: the table is not yet full") 
      
      do local_index = 1,Size(this%keys,1)
        if(trim(this%keys(local_index)) == trim(key)) THEN 
          this%values(local_index) = value
          found = .TRUE.
        endif
      enddo 
      
      if(.NOT.found) call print_error("Unknown key "//key)
   end subroutine lut_set
 
   subroutine lut_set_all(this, value)
      type(MW_lut_t), intent(inout) :: this
      real(wp)     , intent(in)     :: value
      integer                      :: local_index
      
      do local_index = 1,Size(this%keys,1)
          this%values(local_index) = value
      enddo 
   end subroutine lut_set_all
 
   function lut_get(this, key) result(value)
      type(MW_lut_t), intent(in) :: this
      character(*), intent(in)   :: key
      real(wp)                   :: value
      integer                    :: local_index
      logical                    :: found
      
      found = .FALSE. 
      if(this%lut_index .lt. Size(this%keys, 1)) call print_error("Error: the table is not yet full") 
      
      do local_index = 1,Size(this%keys,1)
        if(trim(this%keys(local_index)) == trim(key)) THEN 
          value = this%values(local_index)
          found = .TRUE.
        endif
      enddo 
      
      if(.NOT.found) call print_error("Unknown key "//key)
   end function lut_get
 
   subroutine lut_check(this, key, found)
      type(MW_lut_t), intent(in) :: this
      character(*), intent(in)   :: key
      integer                    :: local_index
      logical                    :: found
      
      found = .FALSE. 
      if(this%lut_index .lt. Size(this%keys, 1)) call print_error("Error: the table is not yet full") 
      
      do local_index = 1,Size(this%keys,1)
        if(trim(this%keys(local_index)) == trim(key)) THEN 
          found = .TRUE.
        endif
      enddo 
      
   end subroutine lut_check
 
   subroutine lut_print(this)
      type(MW_lut_t), intent(in) :: this
      integer  :: local_index 
      
      print*, "Contents of the luttable:"
      do local_index = 1,Size(this%keys,1)
        print*, trim(this%keys(local_index)), " = ", this%values(local_index)
      enddo
   end subroutine lut_print
 
   subroutine print_error(text)
      character(*) :: text
      print*, text
      STOP
   end subroutine print_error
end module MW_lookup_table
