!> @file
!!  Modules to handle input variables
!! @author
!!    Copyright (C) 2010-2013 BigDFT group
!!    This file is distributed under the terms of the
!!    GNU General Public License, see ~/COPYING file
!!    or http://www.gnu.org/copyleft/gpl.txt .
!!    For the list of contributors, see ~/AUTHORS 
!! @todo
!! Add a mechanism to give a warning for unused keywords (TD)
!!   - add a dictionary of keywords used in the file (should be accelerated process_line)
!!   - fo a new list with all keywords
!!   - for each call to input_var remove keywords inthe previous line
!!   - Check if all keywords were used.


!> Module which defines all basic operations to handle input variables
module module_input

   use yaml_strings, only: read_fraction_string,operator(.eqv.)
   implicit none
   private

   !> Error ids for this module.
   integer, parameter, public :: INPUT_VAR_NOT_IN_LIST      = 1
   integer, parameter, public :: INPUT_VAR_NOT_IN_RANGE     = 2
   integer, parameter, public :: INPUT_VAR_CONVERSION_ERROR = 3
   integer, parameter, public :: INPUT_VAR_ARG_ERROR        = 99

   !> Max line of a file
   integer, parameter :: nmax_lines=500
   !> Max length of a line
   integer, parameter :: max_length=92
   character(len=max_length) :: input_file,input_radical,input_type,line_being_processed
   logical :: output,lmpinit
   integer :: iline_parsed,iline_written,iargument,ipos,nlines_total
   integer :: stdout=6
   integer, dimension(:), allocatable :: parsed_lines
   character(len=max_length), dimension(:), allocatable :: inout_lines


   interface input_var
      module procedure var_character, var_logical, var_integer, &
         &   var_integer_array, var_double, var_keyword, var_ids,&
         & var_double_compulsory,var_real_compulsory,var_int_compulsory,var_logical_compulsory,&
         &   var_char_compulsory
   end interface

   public :: input_set_file,input_set_stdout
   public :: input_var
   public :: input_free


contains


   subroutine input_set_stdout(unit)
     implicit none
     integer, intent(in) :: unit
     
     stdout=unit
   end subroutine input_set_stdout


   subroutine input_set_file(iproc, dump, filename, exists,comment_file_usage)
     use wrapper_MPI, only: fmpi_bcast
     use module_bigdft_mpi
     implicit none
     
      integer, intent(in) :: iproc
      logical, intent(in) :: dump
      character(len = *), intent(in) :: filename,comment_file_usage
      logical, intent(out) :: exists

      character(len=max_length), dimension(nmax_lines) :: lines
      integer :: i,nlines,ierror,ierr=0 !for MPIfake BCAST

      !no line parsed if the file not exists
      iline_parsed=0
      !no line has been written in the output
      iline_written=0
      !no argument has been read yet
      iargument=1
      !the present line is empty
      line_being_processed=repeat(' ',max_length)
      !the starting position for the lines is zero
      ipos=0
      !there are no lines for the moment
      nlines_total=0

      !verify whether MPI has been initialized
      lmpinit=.false.
      call MPI_INITIALIZED(lmpinit,ierr)

      write(input_file, "(A)") trim(filename)
      i = index(input_file, ".", back = .true.)
      write(input_radical, "(A)") input_file(1:i - 1)
      write(input_type, "(A)") trim(input_file(i + 1:))

      !check if the file is present, only the proc 0 to avoid race conditions
      !when the inquired file is indeed generated by the proc 0 on free.
      exists = .false.
      if (iproc == 0) inquire(file=trim(input_file),exist=exists)
      if (lmpinit) call MPI_BCAST(exists,1,MPI_LOGICAL,0,bigdft_mpi%mpi_comm,ierr)
      if (ierr /=0) stop 'input_file BCAST (0) '
      if (exists) then
         !only the root processor parse the file
         nlines = 0
         if (iproc==0) then
            open(unit = 1, file = trim(filename), status = 'old')
            i=0
            parse_file: do 
               i=i+1
               lines(i)=repeat(' ',max_length) !initialize lines
               read(1, fmt = '(a)', iostat = ierror) lines(i)
               !eliminate leading blanks from the line
               !print *,'here',i,lines(i),ierror,trim(lines(i)),'len',len(trim(lines(i)))
               lines(i)=adjustl(lines(i))
               if (ierror /= 0) exit parse_file
            end do parse_file
            close(1)
            !check if the last line has 
            if(len(trim(lines(i))) > 0) then
               nlines=i
            else
               nlines=i-1
            end if
         end if
         !broadcast the number of lines
         !if (lmpinit) call MPI_BCAST(nlines,1,MPI_INTEGER,0,bigdft_mpi%mpi_comm,ierr)
         if (lmpinit) call fmpi_bcast(nlines,1,comm=bigdft_mpi%mpi_comm)
         if (ierr /=0) stop 'input_file BCAST (1) '
         nlines_total=nlines

         !broadcast all the lines
         if (lmpinit) call MPI_BCAST(lines,max_length*nlines,MPI_CHARACTER,0,bigdft_mpi%mpi_comm,ierr)
         !if (lmpinit) call fmpi_bcast(lines,max_length*nlines,comm=bigdft_mpi%mpi_comm)
         if (ierr /=0) stop 'input_file BCAST (2) '

         !!$    write(0,*) "Setup input file '", trim(filename), "' with ", i - 1, "lines."

!!$         allocate(inout_lines(0:nlines)) !the 0-th line is for the description of the file
!!$         do i=1,nlines
!!$            inout_lines(i)=lines(i)
!!$         end do
         !start parsing from the first line
         iline_parsed=1
      else
         !in this case the array constitute the output results
!!$         allocate(inout_lines(0:nmax_lines))
!!$         do i=1,nmax_lines
!!$            inout_lines(i)=repeat(' ',max_length) !initialize lines
!!$         end do
         nlines_total=nmax_lines
         nlines=0
      end if

      !lines which are parsed by the input
      allocate(inout_lines(0:nmax_lines))
      allocate(parsed_lines(nmax_lines))
      do i=1,nlines
         inout_lines(i)=lines(i)
         parsed_lines(i)=i
      end do
      do i=nlines+1,nmax_lines
         inout_lines(i)=repeat(' ',max_length) !initialize lines
         parsed_lines(i)=i
      end do


      !write the first line in the output
      if (exists) then
         write(inout_lines(iline_written),'(1x,5a)')&
            &   '#... (file:', trim(filename),')',&
            &    repeat('.',max_length-2-(len(trim(filename)//trim(comment_file_usage))+11)),&
            &   trim(comment_file_usage)
      else
         write(inout_lines(iline_written),'(1x,5a)')&
            &   '#... (file:',trim(filename),'.. not present)',&
            &     repeat('.',max_length-2-(len(trim(filename)//trim(comment_file_usage))+26)),&
            &   trim(comment_file_usage)
      end if
      iline_written=iline_written+1

      output = (iproc == 0) .and. dump
      !dump the 0-th line on the screen
      !if (iproc == 0 .and. dump) then
      !   write(stdout,'(a)') inout_lines(0)
      !end if
      !!$    output = (iproc == 0)
      !!$    ! Output
      !!$    if (iproc == 0) then
      !!$       write(stdout,*)
      !!$       if (exists) then
      !!$          write(stdout,'(1x,3a)') '--- (file: ', trim(filename), &
      !!$               & ') -----------------------------------------'//&
      !!$               trim(comment_file_usage)
      !!$       else
      !!$          write(stdout,'(1x,a)')&
      !!$               '--- (file:'//trim(filename)//'-- not present) --------------------------'//&
      !!$               trim(comment_file_usage)
      !!$       end if
      !!$    end if
   END SUBROUTINE input_set_file


   subroutine input_free(dump)
      use module_bigdft_mpi
      implicit none
      logical, intent(in), optional :: dump
      !Local variables
      integer, parameter :: iunit=11
      logical :: warn
      integer :: ierr,iline,jline

      if (present(dump)) then !case for compulsory variables
         !if (iline_written==1) iline_written=2
         if (dump) then
            !dump the file on the screen
            write(stdout,'(a)') inout_lines(0)
            do iline=1,iline_written-1
               !print *,'end',iline,parsed_lines(iline)
               write(stdout,fmt='(1x,a,a)') '#|',&
                    inout_lines(parsed_lines(iline))(1:max_length-2)
            end do
            if (iline_parsed==0) then !the file does not exist
               !add the writing of the file in the given unit
               open(unit=iunit,file='default.' // trim(input_type), status ='unknown')
               do iline=1,iline_written-1
                  write(iunit,'(a)')inout_lines(iline)
               end do
               close(unit=iunit)
            else if (nlines_total /= nmax_lines) then !case with existing file
               !search for lines which have not been processed
               warn=.false.
               line_done: do iline=1,iline_parsed
                  do jline=1,iline_parsed
                     if (parsed_lines(jline)==iline) cycle line_done
                  end do
                  if (len_trim(inout_lines(iline))==0) cycle line_done
                  if (.not. warn) then
                     write(stdout,*)&
                          '# ==== WARNING: the following lines have not been processed by the parser ===='
                     warn=.true.
                  end if
                  write(stdout,fmt='(1x,a,a)') '#|',inout_lines(iline)(1:max_length-2)
               end do line_done
               !put the rest of the lines
               do iline=iline_parsed,nlines_total
                  if (.not. warn) then
                     write(stdout,*)&
                          '# ==== WARNING: the following lines have not been processed by the parser ===='
                     warn=.true.
                  end if
                  write(stdout,fmt='(1x,a,a)') '#|',inout_lines(iline)(1:max_length-2)
               end do
            end if
         end if
      end if


      if (allocated(inout_lines)) deallocate(inout_lines)
      deallocate(parsed_lines)
      if (lmpinit) call MPI_BARRIER(bigdft_mpi%mpi_comm,ierr)
   END SUBROUTINE input_free


   subroutine leave()
      use module_bigdft_mpi
      implicit none
      !local variables
      integer :: ierr
      if (output) then
         write(stdout,'(1x,a,a,2(a,i3))')'Error while reading the file "', &
              &   trim(input_file), '", line=', iline_written,' argument=', iargument
         if (iline_written <= nlines_total) then
            write(stdout,*)inout_lines(iline_written),line_being_processed
         end if
         !to be called only if mpi is initialized
      end if
      if (lmpinit) call MPI_BARRIER(bigdft_mpi%mpi_comm,ierr)
      stop
   END SUBROUTINE leave


   subroutine check(ierror)
      implicit none
      !Arguments
      integer, intent(in) :: ierror

      if (ierror/=0) then
         call leave()
      end if
      !increment the argument at each passed check
      iargument=iargument+1

   END SUBROUTINE check


   !> Process the line needed with the default value in mind
   subroutine process_line(default,line_comment)
      implicit none
      character(len=*), intent(in), optional :: default,line_comment
      !Local variables
      integer :: i,iblank,istart,nchars

      if (iargument==1) then
         ipos=0
         line_being_processed=repeat(' ',max_length)
      end if

      if (present(default) .and. iline_parsed==0) then
         !case without file, write default and continue the line
         do i=1,len(default)
            inout_lines(iline_written)(i+ipos:i+ipos)=default(i:i)
         end do
         ipos=ipos+len(default)+1
         inout_lines(iline_written)(ipos:ipos)=' '
      end if
      if (present(line_comment) .and. iline_parsed==0) then
         !case without file, close the line. Start the comment at column 16 if possible
         istart=max(ipos+1,16)
         nchars=min(len(line_comment),max_length-istart)
         inout_lines(iline_written)(istart:istart+nchars)=line_comment(1:nchars)
         iline_written=iline_written+1
         iargument=0 !the argument should become 0 for the default case and one for the other one
      else if (.not. present(default) .and. .not. present(line_comment).and. iline_parsed/=0) then
         !traditional case, the argument should be parsed one after another
         !start with the entire line
         if (iargument==1) then 
            !print *,'prsed',iline_parsed,inout_lines(iline_parsed)
            line_being_processed=inout_lines(iline_parsed)
         else
            !search in the line the first blank
            iblank=scan(line_being_processed,' ')
            do i=1,max_length-iblank
               line_being_processed(i:i)=line_being_processed(i+iblank:i+iblank)
            end do
            do i=max_length-iblank+1,max_length
               line_being_processed(i:i)=' '
            end do
            ipos=ipos+iblank
         end if
         !adjust the line to eliminate further blanks
         line_being_processed=adjustl(line_being_processed)
      else if (.not. present(default) .and. present(line_comment) .and. iline_parsed/=0) then
         !traditional case, close the line and skip to the next one
         !case without file, close the line. Write also the predefined comment at the end
         !Start the comment at column 16 if possible
         iblank=scan(line_being_processed,' ')
         ipos=ipos+iblank
         !+2 in order to add a space before the comment 
         istart=max(ipos+2,16)
         do i=ipos+1,15
            inout_lines(iline_written)(i:i)=' '
         end do
         nchars=min(len(line_comment),max_length-istart)
         inout_lines(iline_written)(istart:istart+nchars)=line_comment(1:nchars)
         !clean the rest of the line
         do i=istart+nchars+1,max_length
            inout_lines(iline_written)(i:i)=' '
         end do
         iargument=1
         iline_parsed=iline_parsed+1
         iline_written=iline_written+1
      end if

   END SUBROUTINE process_line


   subroutine find(name, iline, ii)
     implicit none
     character(len = *), intent(in) :: name
     integer, intent(out) :: iline, ii
     !local variables
     logical :: line_found
      integer :: k
      line_found=.false.
      if (iline_parsed /= 0) then
         iline_parsed=iline_parsed+1
         search_line: do iline = 1, size(inout_lines)-1 !there is also the zero now
            k = 1
            do ii = 1, len(inout_lines(iline)), 1
               if (ichar(inout_lines(iline)(ii:ii)) == ichar(name(k:k)) .or. &
                  &   ichar(inout_lines(iline)(ii:ii)) == ichar(name(k:k)) + 32 .or. &
                  & ichar(inout_lines(iline)(ii:ii)) == ichar(name(k:k)) - 32) then
                  k = k + 1
               else
                  k = 1
                  cycle search_line
               end if
               if (k == len(name) + 1) then !the name has been found
                  line_found=.true.
                  exit search_line   !return
               end if
            end do
         end do search_line
      else
         iline_parsed=2
      end if
      if (.not. line_found) iline = 0
      !swap the lines found
      !print *,'iline',iline,iline_parsed,nlines_total+iline_parsed-1,line_found,name
      if (nlines_total /= nmax_lines ) then !the file exists
         if (line_found) then
            parsed_lines(iline_parsed-1)=iline
            !parsed_lines(iline)=iline_parsed-1
         else
            parsed_lines(iline_parsed-1)=nlines_total+iline_parsed-1
         end if
      end if

   END SUBROUTINE find

   !> Routines for compulsory file
   subroutine var_double_compulsory(var,default,dict,ranges,exclusive,comment,input_iostat)
      use dictionaries
      implicit none
      character(len=*), intent(in) :: default
      real(kind=8), intent(out) :: var
      type(dictionary), pointer, optional :: dict
      character(len=*), intent(in), optional :: comment
      integer, intent(out), optional :: input_iostat
      real(kind=8), dimension(2), intent(in), optional :: ranges
      real(kind=8), dimension(:), intent(in), optional :: exclusive
      !Local variables
      logical :: found
      integer :: ierror,ilist

      if (present(input_iostat)) then
         !first, check if the line is correct
         if (iline_written>nlines_total ) then
            input_iostat=-1
            return
         else
            input_iostat=0 !no error for the moment
         end if
      else
         if (iline_written>nlines_total) then
            call leave()
         end if
      end if

      !if the file has not been opened, use the default variable 
      !then write in the output lines the default
      if (iline_parsed==0) then
         !finalize the line if the comment is present
         if (present(comment)) then
            call process_line(default=default,line_comment=comment)
         else
            call process_line(default=default)
         end if
         call read_fraction_string(default,var,ierror)
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if
         !otherwise read the corresponding argument and check its validity
      else
         !read the argument
         call process_line()
         !print *,line_being_processed
         call read_fraction_string(line_being_processed,var,ierror)
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if

         !check the validity of the variable
         if (present(ranges)) then
            if (var < ranges(1) .or. var > ranges(2)) then
               if (output) then
                  write(stdout,'(1x,a,i0,a,i0)') &
                     &   'ERROR in parsing file '//trim(input_file)//', line=', iline_written,' argument=', iargument-1
                  write(stdout,*)'      values should be in range: [',ranges(1),'-',ranges(2),']'
               end if
               if (present(input_iostat)) then
                  input_iostat=1
                  return
               else
                  call leave()
               end if
            end if
         else if (present(exclusive)) then
            found=.false.
            found_loop: do ilist=1,size(exclusive)
               if (var == exclusive(ilist)) then 
                  found=.true.
                  exit found_loop
               end if
            end do found_loop
            if (.not. found) then
               if (output) then
                  write(stdout,'(1x,a,i0,a,i0)') &
                     &   'ERROR in parsing file '//trim(input_file)//', line=', iline_written,' argument=', iargument-1
                  write(stdout,*)'      values should be in list: ',exclusive(:)
               end if
               if (present(input_iostat)) then
                  input_iostat=1
                  return
               else
                  call leave()
               end if
            end if
         end if

         !increment the line if comment is present, do not touch the input file
         if (present(comment)) then
            call process_line(line_comment=comment)
         end if
      end if

      if (present(dict)) then
         if (associated(dict)) call set(dict,var) !for the moment the format is the default one
      end if

    END SUBROUTINE var_double_compulsory


   subroutine var_real_compulsory(var,default,dict,ranges,exclusive,comment,input_iostat)
      use dictionaries
      use module_precisions
      implicit none
      character(len=*), intent(in) :: default
      real(kind=4), intent(out) :: var
      type(dictionary), pointer, optional :: dict
      character(len=*), intent(in), optional :: comment
      integer, intent(out), optional :: input_iostat
      real(kind=4), dimension(2), intent(in), optional :: ranges
      real(kind=4), dimension(:), intent(in), optional :: exclusive
      !Local variables
      real(gp) :: double_var
      logical :: found
      integer :: ierror,ilist

      if (present(input_iostat)) then
         !first, check if the line is correct
         if (iline_written>nlines_total ) then
            input_iostat=-1
            return
         else
            input_iostat=0 !no error for the moment
         end if
      else
         if (iline_written>nlines_total) then
            call leave()
         end if
      end if

      !if the file has not been opened, use the default variable 
      !then write in the output lines the default
      if (iline_parsed==0) then
         !finalize the line if the comment is present
         if (present(comment)) then
            call process_line(default=default,line_comment=comment)
         else
            call process_line(default=default)
         end if
         call read_fraction_string(default,double_var,ierror)
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if
         var=real(double_var,kind=4)
         !otherwise read the corresponding argument and check its validity
      else
         !read the argument
         call process_line()
         call read_fraction_string(line_being_processed,double_var,ierror)
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if
         var=real(double_var,kind=4)

         !check the validity of the variable
         if (present(ranges)) then
            if (var < ranges(1) .or. var > ranges(2)) then
               if (output) then
                  write(stdout,'(1x,a,i0,a,i0)') &
                     &   'ERROR in parsing file '//trim(input_file)//', line=', iline_written,' argument=', iargument-1
                  write(stdout,*)'      values should be in range: [',ranges(1),'-',ranges(2),']'
               end if
               if (present(input_iostat)) then
                  input_iostat=1
                  return
               else
                  call leave()
               end if
            end if
         else if (present(exclusive)) then
            found=.false.
            found_loop: do ilist=1,size(exclusive)
               if (var == exclusive(ilist)) then 
                  found=.true.
                  exit found_loop
               end if
            end do found_loop
            if (.not. found) then
               if (output) then 
                  write(stdout,'(1x,a,i0,a,i0)') &
                     &   'ERROR in parsing file '//trim(input_file)//', line=', iline_written,' argument=', iargument-1
                  write(stdout,*)'      values should be in list: ',exclusive(:)
               end if
               if (present(input_iostat)) then
                  input_iostat=1
                  return
               else
                  call leave()
               end if
            end if
         end if 

         !increment the line if comment is present, do not touch the input file
         if (present(comment)) then
            call process_line(line_comment=comment)
         end if
      end if   

      if (present(dict)) then
         if (associated(dict)) call set(dict,var) !for the moment the format is the default one
      end if

   END SUBROUTINE var_real_compulsory


   subroutine var_int_compulsory(var,default,dict,ranges,exclusive,comment,input_iostat)
      use dictionaries
      implicit none
      character(len=*), intent(in) :: default
      integer, intent(out) :: var
      type(dictionary), pointer, optional :: dict
      character(len=*), intent(in), optional :: comment
      integer, intent(out), optional :: input_iostat
      integer, dimension(2), intent(in), optional :: ranges
      integer, dimension(:), intent(in), optional :: exclusive
      !Local variables
      logical :: found
      integer :: ierror,ilist

      if (present(input_iostat)) then
         !first, check if the line is correct
         if (iline_written>nlines_total) then
            input_iostat=-1
            return
         else
            input_iostat=0 !no error for the moment
         end if
      else
         if (iline_written>nlines_total) then
            call leave()
         end if
      end if

      !if the file has not been opened, use the default variable 
      !then write in the output lines the default
      if (iline_parsed==0) then
         !finalize the line if the comment is present
         if (present(comment)) then
            call process_line(default=default,line_comment=comment)
         else
            call process_line(default=default)
         end if
         read(default,*,iostat=ierror) var
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if
         !otherwise read the corresponding argument and check its validity
      else
         !read the argument
         call process_line()

         read(line_being_processed,fmt=*,iostat=ierror) var
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if

         !check the validity of the variable
         if (present(ranges)) then
            if (var < ranges(1) .or. var > ranges(2)) then
               if (output) then
                  write(stdout,'(1x,a,i0,a,i0)') &
                     &   'ERROR in parsing file '//trim(input_file)//', line=', iline_written,' argument=', iargument-1
                  write(stdout,*)'      values should be in range: [',ranges(1),'-',ranges(2),']'
               end if
               if (present(input_iostat)) then
                  input_iostat=1
                  return
               else
                  call leave()
               end if
            end if
         else if (present(exclusive)) then
            found=.false.
            found_loop: do ilist=1,size(exclusive)
               if (var == exclusive(ilist)) then
                  found=.true.
                  exit found_loop
               end if
            end do found_loop
            if (.not. found) then
               if (output) then
                  write(stdout,'(1x,a,i0,a,i0)') &
                     &   'ERROR in parsing file '//trim(input_file)//', line=', iline_written,' argument=', iargument-1
                  write(stdout,*)'      values should be in list: ',exclusive(:)
               end if
               if (present(input_iostat)) then
                  input_iostat=1
                  return
               else
                  call leave()
               end if
            end if
         end if 

         !increment the line if comment is present, do not touch the input file
         if (present(comment)) then
            call process_line(line_comment=comment)
         end if
      end if   

      if (present(dict)) then
         if (associated(dict)) call set(dict,var) !for the moment the format is the default one
      end if
      
   END SUBROUTINE var_int_compulsory


   subroutine var_char_compulsory(var,default,dict,exclusive,comment,input_iostat)
      use dictionaries
      implicit none
      character(len=*), intent(in) :: default
      character(len=*), intent(out) :: var
      type(dictionary), pointer, optional :: dict
      character(len=*), intent(in), optional :: comment
      integer, intent(out), optional :: input_iostat
      character(len=*), dimension(:), intent(in), optional :: exclusive
      !Local variables
      logical :: found
      integer :: ierror,ilist

      if (present(input_iostat)) then
         !first, check if the line is correct (or if it is an optional line)
         if (iline_written>nlines_total) then
            input_iostat=-1
            return
         else
            input_iostat=0 !no error for the moment
         end if
      else
         if (iline_written>nlines_total) then
            call leave()
         end if
      end if

      !if the file has not been opened, use the default variable 
      !then write in the output lines the default
      if (iline_parsed==0) then
         !finalize the line if the comment is present
         if (present(comment)) then
            call process_line(default=default,line_comment=comment)
         else
            call process_line(default=default)
         end if
         read(default,'(a)',iostat=ierror) var
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if
         !otherwise read the corresponding argument and check its validity
      else
         !read the argument
         call process_line()
         read(line_being_processed,fmt=*,iostat=ierror) var
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if
         if (present(exclusive)) then
            found=.false.
            found_loop: do ilist=1,size(exclusive)
               if (trim(var) .eqv. trim(exclusive(ilist))) then
                  found=.true.
                  exit found_loop
               end if
            end do found_loop
            if (.not. found) then
               if (output) then 
                  write(stdout,'(1x,a,i0,a,i0)') &
                     &   'ERROR in parsing file '//trim(input_file)//', line=', iline_written,' argument=', iargument-1
                  write(stdout,'(6x,a,30(1x,a))')&
                     &   'values should be in list: ',exclusive(:)
               end if
               if (present(input_iostat)) then
                  input_iostat=1
                  return
               else
                  call leave()
               end if
            end if
         end if 

         !increment the line if comment is present, do not touch the input file
         if (present(comment)) then
            call process_line(line_comment=comment)
         end if
      end if   

      if (present(dict)) then
         if (associated(dict)) call set(dict,var) !for the moment the format is the default one
      end if

   END SUBROUTINE var_char_compulsory


   subroutine var_logical_compulsory(var,default,dict,comment,input_iostat)
      use dictionaries
      implicit none
      character(len=*), intent(in) :: default
      logical, intent(out) :: var
      type(dictionary), pointer, optional :: dict
      character(len=*), intent(in), optional :: comment
      integer, intent(out), optional :: input_iostat
      !Local variables
      integer :: ierror

      !if the file has not been opened, use the default variable 
      !then write in the output lines the default
      if (iline_parsed==0) then
         !finalize the line if the comment is present
         if (present(comment)) then
            call process_line(default=default,line_comment=comment)
         else
            call process_line(default=default)
         end if
         read(default,*,iostat=ierror) var
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if
         !otherwise read the corresponding argument and check its validity
      else
         !read the argument
         call process_line()

         read(line_being_processed,fmt=*,iostat=ierror) var
         if (present(input_iostat) .and. ierror /= 0) then
            input_iostat=-2
            return
         else
            call check(ierror)
         end if

         !increment the line if comment is present, do not touch the input file
         if (present(comment)) then
            call process_line(line_comment=comment)
         end if
      end if   
      
      if (present(dict)) then
         if (associated(dict)) call set(dict,var) !for the moment the format is the default one
      end if

   END SUBROUTINE var_logical_compulsory


   !> Routines for non-compulsory file (input.perf)
   subroutine var_character(name, default, description, var)
      use yaml_strings, only : buffer_string
      use module_bigdft_mpi
      character(len = *), intent(in) :: name
      character(len = *), intent(in) :: default
      character(len = *), intent(in) :: description
      character(len = *), intent(out) :: var
      !local variables
      character(len=max_length) :: line
      character(len=2) :: frmt
      integer :: i, j, ierror, ierr,iblk,lgt,tab
      
      var=repeat(' ',len(var))

      write(var, "(A)") default

      call find(name, i, j)
      if (i > 0) then
         !read all the line
         write(frmt,'(i2)')max(max_length-j-1,0)
         read(inout_lines(i)(j + 2:), fmt = '(a'//trim(adjustl(frmt))//')', iostat = ierror) line
         if (ierror/=0) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
         !now line has potentially the slashes
         if (scan(line,'/') > 0) then
            line=adjustl(line)
            iblk=scan(line,' ')
            if (iblk >0 ) then
               var(1:iblk)=line(1:iblk)
            else
               read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) var
            end if
         else
            read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) var
         end if
         if (ierror/=0) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
                 &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
      end if
      if (output) then
         lgt=0
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,trim(name)//' ',lgt)
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,trim(var),lgt)
         tab=max(29-lgt,1)
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
              repeat(' ',tab),lgt)
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
              trim(description),lgt)

         !write(inout_lines(parsed_lines(iline_parsed-1)),"(a,1x,a,t30,a)") name, trim(var), description
         iline_written=iline_written+1
      end if
   END SUBROUTINE var_character


   subroutine var_logical(name, default, description, var)
      character(len = *), intent(in) :: name
      logical, intent(in) :: default
      character(len = *), intent(in) :: description
      logical, intent(out) :: var

      integer :: i, j, ierror

      var = default
      call find(name, i, j)
      if (i > 0) then
         read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) var
         if (ierror /= 0) then
            var = .true.
         end if
      end if
      if (output) then
         write(inout_lines(parsed_lines(iline_parsed-1)),"(a,1x,l1,t30,a)") name, var, description
         iline_written=iline_written+1
      end if
   END SUBROUTINE var_logical


   subroutine var_integer(name, default, description, var)
      use module_bigdft_mpi
      character(len = *), intent(in) :: name
      integer, intent(in) :: default
      character(len = *), intent(in) :: description
      integer, intent(out) :: var

      integer :: i, j, ierror, ierr,ilg
      var = default
      call find(name, i, j)

      if (i > 0) then
         read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) var
         if (ierror/=0) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
      end if
      if (output) then
         ilg=min(max(len(inout_lines)-31,0),len(trim(description)))
         write(inout_lines(parsed_lines(iline_parsed-1)),"(a,1x,I0,t30,a)") name, var,description(1:ilg)
         iline_written=iline_written+1
      end if
   END SUBROUTINE var_integer


   subroutine var_integer_array(name, default, description, var)
      use yaml_strings, only: buffer_string,yaml_toa
      use module_bigdft_mpi
      character(len = *), intent(in) :: name
      integer, intent(in) :: default(:)
      character(len = *), intent(in) :: description
      integer, intent(out) :: var(:)

      integer :: i, j, ierror, ierr,lgt,tab

      var = default
      call find(name, i, j)
      if (i > 0) then
         read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) var
         if (ierror/=0) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
      end if
      if (output) then
         lgt=0
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,trim(name),lgt)
         do j = 1, size(var), 1
            call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
                 trim(yaml_toa(var(j),fmt='(i0)')),lgt)
         end do
         tab=max(29-lgt,1)
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
              repeat(' ',tab),lgt)
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
              trim(description),lgt)
!!$         write(inout_lines(iline_written),"(1x,a,a,1x)", advance = "NO") "#|", name
!!$         do i = 1, size(var), 1
!!$            write(inout_lines(iline_written),"(1x,I0)", advance = "NO") var(i)
!!$         end do
!!$         write(inout_lines(iline_written),"(t10,a)")description
         iline_written=iline_written+1
      end if
   END SUBROUTINE var_integer_array


   subroutine var_double(name, default, description, var)
      use module_bigdft_mpi
      implicit none
      character(len = *), intent(in) :: name
      double precision, intent(in) :: default
      character(len = *), intent(in) :: description
      double precision, intent(out) :: var

      integer :: i, j, ierror, ierr

      var = default
      call find(name, i, j)
      if (i > 0) then
         read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) var
         if (ierror/=0) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
      end if
      if (output) then
         write(inout_lines(parsed_lines(iline_parsed-1)),"(a,1x,es9.2,t30,a)") name, var, description
         iline_written=iline_written+1
      end if
   END SUBROUTINE var_double


   subroutine var_keyword(name, length, default, list, description, var)
      use yaml_strings, only: buffer_string
      use module_bigdft_mpi
      implicit none
      character(len = *), intent(in) :: name
      integer, intent(in) :: length
      character(len = length), intent(in) :: default
      character(len = length), intent(in) :: list(:)
      character(len = *), intent(in) :: description
      integer, intent(out) :: var

      integer :: i, j, ierror, ierr,lgt,tab
      character(len = length) :: buf

      ! Set the default value to var.
      do i = 1, size(list), 1
         if (trim(default) == trim(list(i))) exit
      end do
      var = i - 1
      ! Find the keyword name in the file.
      call find(name, i, j)
      if (i > 0) then
         read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) buf
         if (ierror/=0) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
         ! Look for buf in list.
         do j = 1, size(list), 1
            if (trim(buf) == trim(list(j))) exit
         end do
         if (j > size(list)) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
         var = j - 1
      end if
      if (output) then
         lgt=0
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
              trim(name)//' '//trim(list(var+1)),lgt)
         tab=max(29-lgt,1)
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
              repeat(' ',tab),lgt)
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
              trim(description)//' ('//trim(list(1)),lgt)
         do j = 2, size(list)
            call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,&
                 ', '//trim(list(j)),lgt)
         end do
         call buffer_string(inout_lines(parsed_lines(iline_parsed-1)),max_length,') ',lgt)

!!$         write(inout_lines(iline_written),"(1x,a,a,1x,a,t30,2a)", advance = "NO") &
!!$            &   "#|", name, list(var + 1), description, " ("
!!$         write(inout_lines(iline_written),"(A)", advance = "NO") trim(list(1))
!!$         do i = 2, size(list), 1
!!$            write(inout_lines(iline_written),"(2A)", advance = "NO") ", ", trim(list(i))
!!$         end do
!!$         write(inout_lines(iline_written),"(A)") ")"
         iline_written=iline_written+1
      end if
   END SUBROUTINE var_keyword


   subroutine var_ids(name, default, list, description, var)
      use module_bigdft_mpi
      implicit none
      character(len = *), intent(in) :: name
      integer, intent(in) :: default
      integer, intent(in) :: list(:)
      character(len = *), intent(in) :: description
      integer, intent(out) :: var

      integer :: i, j, ierror, ierr

      var = default
      call find(name, i, j)
      if (i > 0) then
         read(inout_lines(i)(j + 2:), fmt = *, iostat = ierror) var
         if (ierror/=0) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
         do j = 1, size(list), 1
            if (var == list(j)) exit
         end do
         if (j > size(list)) then
            if (output) write(stdout,'(1x,a,a,a,i3)')  'Error while reading the file "', &
               &   trim(input_file), '", line=', i
            call MPI_ABORT(bigdft_mpi%mpi_comm,ierror,ierr)
         end if
      end if
      if (output) then
         write(inout_lines(parsed_lines(iline_parsed-1)),"(a,1x,I0,t30,a)") name, var, description
         iline_written=iline_written+1
      end if
   END SUBROUTINE var_ids

END MODULE module_input

subroutine old_input_stdout(unit)
  use module_input
  implicit none
  integer, intent(in) :: unit
  call input_set_stdout(unit=unit)
end subroutine old_input_stdout
