!> Module used to store algorithm parameters
module MW_algorithms
   use MW_cg, only: MW_cg_t
   use MW_cgaim, only: MW_cgaim_t
   use MW_maze, only: MW_maze_t
   use MW_matrix_inversion, only: MW_matrix_inversion_t
   implicit none
   private

   ! Public type
   public :: MW_algorithms_t

   ! Public subroutine
   public :: void_type
   public :: print_type
   public :: print_statistics

   type MW_algorithms_t
      ! Electrode charge computation algorithms
      ! ---------------------------------------
      type(MW_cg_t) :: cg                                !< CG algorithm parameters
      type(MW_cgaim_t) :: cgaim                          !< CG algorithm parameters
      type(MW_maze_t) :: maze                      !< Massless SHAKE algorithm parameters
      type(MW_matrix_inversion_t) :: matrix_inversion    !< Matrix Inversion
      logical :: constant_charge                         !< T if no constant potential
   end type MW_algorithms_t

contains

   ! ================================================================================
   ! Void algorithms parameters
   subroutine void_type(this)
      use MW_cg, only: MW_cg_void_type => void_type
      use MW_cgaim, only: MW_cgaim_void_type => void_type
      use MW_maze, only: MW_maze_void_type => void_type
      use MW_matrix_inversion, only: MW_matrix_inversion_void_type => void_type
      implicit none
      ! Parameter
      ! ---------
      type(MW_algorithms_t), intent(inout) :: this

      ! CG parameters
      call MW_cg_void_type(this%cg)
      call MW_cgaim_void_type(this%cgaim)
      call MW_maze_void_type(this%maze)
      call MW_matrix_inversion_void_type(this%matrix_inversion)
      this%constant_charge = .false.

   end subroutine void_type

   !================================================================================
   ! Print algorithm parameters
   subroutine print_type(this, ounit, system)
      use MW_cg, only: MW_cg_print_type => print_type
      use MW_cgaim, only: MW_cgaim_print_type => print_type
      use MW_maze, only: MW_maze_print_type => print_type
      use MW_matrix_inversion, only: MW_matrix_inversion_print_type => print_type
      use MW_system, only: MW_system_t

      implicit none
      ! Parameter
      ! ---------
      type(MW_algorithms_t), intent(in) :: this
      type(MW_system_t), intent(in) :: system
      integer, intent(in) :: ounit

      write(ounit, *)
      write(ounit, '("Algorithms parameters:")')
      write(ounit, '("======================")')

      if (system%pimrun.and.system%elecrun) then
         write(ounit, '("Dipoles and Electrode Charges parameters:")')
         write(ounit, '("----------------------")')
         if (this%constant_charge) then
            write(ounit, '("constant charges on electrodes")')
         end if
         if (this%maze%calc_dip.and.this%maze%calc_elec) then
            write(ounit, '("maze")')
            write(ounit, '("-------")')
            call MW_maze_print_type(this%maze, ounit)
         else if (this%matrix_inversion%calc_dip.and.this%matrix_inversion%calc_elec) then
            write(ounit, '("matrix_inversion")')
            write(ounit, '("----------------")')
            call MW_matrix_inversion_print_type(this%matrix_inversion, ounit)
         else
            write(ounit, '("cg")')
            write(ounit, '("--")')
            call MW_cg_print_type(this%cg, ounit)
         end if
         write(ounit,*)
         if (system%aimrun) then
            write(ounit, '("Ion radius parameters:")')
            write(ounit, '("----------------------")')
            write(ounit, '("cgaim")')
            write(ounit, '("-----")')
            call MW_cgaim_print_type(this%cgaim, ounit)
            write(ounit,*)
         end if
      else if (system%pimrun) then
         write(ounit, '("Dipoles parameters:")')
         write(ounit, '("----------------------")')
         if (this%maze%calc_dip) then
            write(ounit, '("maze")')
            write(ounit, '("-------")')
            call MW_maze_print_type(this%maze, ounit)
         else if (this%matrix_inversion%calc_dip) then
            write(ounit, '("matrix_inversion")')
            write(ounit, '("----------------")')
            call MW_matrix_inversion_print_type(this%matrix_inversion, ounit)
         else
            write(ounit, '("cg")')
            write(ounit, '("--")')
            call MW_cg_print_type(this%cg, ounit)
         end if
         write(ounit,*)
            if (system%aimrun) then
            write(ounit, '("Ion radius parameters:")')
            write(ounit, '("----------------------")')
            write(ounit, '("cgaim")')
            write(ounit, '("-----")')
            call MW_cgaim_print_type(this%cgaim, ounit)
            write(ounit,*)
         end if
      else if (system%elecrun) then
         write(ounit, '("Electrodes parameters:")')
         write(ounit, '("----------------------")')
         if (this%constant_charge) then
            write(ounit, '("constant charge")')
         else
            if (this%maze%calc_elec) then
               write(ounit, '("maze")')
               write(ounit, '("-------")')
               call MW_maze_print_type(this%maze, ounit)
            else if (this%matrix_inversion%calc_elec) then
               write(ounit, '("matrix_inversion")')
               write(ounit, '("----------------")')
               call MW_matrix_inversion_print_type(this%matrix_inversion, ounit)
            else
               write(ounit, '("cg")')
               write(ounit, '("-------")')
               call MW_cg_print_type(this%cg, ounit)
            end if
         end if
         write(ounit,*)
      end if

   end subroutine print_type

   !================================================================================
   ! Print algorithm statistics
   subroutine print_statistics(this, ounit, do_output, system)
      use MW_cg, only: MW_cg_print_statistics => print_statistics
      use MW_cgaim, only: MW_cgaim_print_statistics => print_statistics
      use MW_maze, only: MW_maze_print_statistics => print_statistics
      use MW_matrix_inversion, only: MW_matrix_inversion_print_statistics => print_statistics
      use MW_timers, only: TIMER_DIAG_IO, MW_timers_start, MW_timers_stop
      use MW_system, only: MW_system_t

      implicit none
      ! Parameter
      ! ---------
      type(MW_algorithms_t), intent(in) :: this
      type(MW_system_t), intent(in) :: system
      integer, intent(in) :: ounit
      logical, intent(in) :: do_output !< flag if the local process performs output
      call MW_timers_start(TIMER_DIAG_IO)

      if (do_output) then
         if (system%pimrun.and.system%elecrun) then
            if (this%maze%calc_dip.and.this%maze%calc_elec) then
               call MW_maze_print_statistics(this%maze, ounit)
            else if (this%matrix_inversion%calc_dip.and.this%matrix_inversion%calc_elec) then
               call MW_matrix_inversion_print_statistics(this%matrix_inversion, ounit)
            else
               call MW_cg_print_statistics(this%cg, ounit)
            end if
            if (system%aimrun) then
               call MW_cgaim_print_statistics(this%cgaim, ounit)
            end if
         else if (system%pimrun) then
            if (this%maze%calc_dip) then
               call MW_maze_print_statistics(this%maze, ounit)
            else if (this%matrix_inversion%calc_dip) then
               call MW_matrix_inversion_print_statistics(this%matrix_inversion, ounit)
            else
               call MW_cg_print_statistics(this%cg, ounit)
            end if
            if (system%aimrun) then
               call MW_cgaim_print_statistics(this%cgaim, ounit)
            end if
         else if (system%elecrun) then
            if (.not. this%constant_charge) then
               if (this%maze%calc_elec) then
                  call MW_maze_print_statistics(this%maze, ounit)
               else if (this%matrix_inversion%calc_elec) then
                  call MW_matrix_inversion_print_statistics(this%matrix_inversion, ounit)
               else
                  call MW_cg_print_statistics(this%cg, ounit)
               end if
            end if
         end if

      end if

      call MW_timers_stop(TIMER_DIAG_IO)
   end subroutine print_statistics

end module MW_algorithms
