!-*- mode: F90 -*-!
!                                                            !
! This file is distributed under the terms of the GNU        !
! General Public License. See the file `LICENSE' in          !
! the root directory of the present distribution, or         !
! http://www.gnu.org/copyleft/gpl.txt .                      !
!                                                            !
!------------------------------------------------------------!
! Code to enable use of the POV-Ray ray-tracing software for !
! the rendering of Wannier function isosurfaces from .xsf    !
! files generated by Wannier90.                              !
!------------------------------------------------------------!
!                                                            !
! File parser from the Wannier90 package                     !
! Copyright (C) 2007 Jonathan Yates, Arash Mostofi,          !
! Young-Su Lee, Nicola Marzari, Ivo Souza, David Vanderbilt  !
!                                                            !
!------------------------------------------------------------!

module m_io
  use general
  implicit none

  ! Private data
  integer                            :: num_lines
  integer, parameter                 :: maxlen = 120
  character(len=maxlen), allocatable :: in_data(:)
  logical                            :: ltmp

  public

contains
  !=======================================!
  subroutine param_in_file
    !=======================================!
    ! Load the *.win file into a character  !
    ! array in_file, ignoring comments and  !
    ! blank lines and converting everything !
    ! to lowercase characters               !
    !=======================================!

    !use w90_utility,   only : utility_lowercase

    implicit none
    integer           ::  in_unit, tot_num_lines, ierr, line_counter, loop, in1, in2
    character(len=maxlen) :: dummy

    in_unit = 90
    open (in_unit, file='w90pov.inp', form='formatted', status='old', err=101)

    num_lines = 0; tot_num_lines = 0
    do
      read (in_unit, '(a)', iostat=ierr, err=101, end=210) dummy
      dummy = adjustl(dummy)
      tot_num_lines = tot_num_lines + 1
      if (.not. dummy(1:1) == '!' .and. .not. dummy(1:1) == '#') then
        if (len(trim(dummy)) > 0) num_lines = num_lines + 1
      endif

    end do

101 print *, 'error: can not read input file file w90pov.inp'
    stop
210 continue
    rewind (in_unit)

    allocate (in_data(num_lines), stat=ierr)
    if (ierr /= 0) then
      print *, 'Error allocating in_data in param_in_file'
      stop
    end if

    line_counter = 0
    do loop = 1, tot_num_lines
      read (in_unit, '(a)', iostat=ierr, err=101) dummy
      dummy = utility_lowercase(dummy)
      dummy = adjustl(dummy)
      if (dummy(1:1) == '!' .or. dummy(1:1) == '#') cycle
      if (len(trim(dummy)) == 0) cycle
      line_counter = line_counter + 1
      in1 = index(dummy, '!')
      in2 = index(dummy, '#')
      if (in1 == 0 .and. in2 == 0) in_data(line_counter) = dummy
      if (in1 == 0 .and. in2 > 0) in_data(line_counter) = dummy(:in2 - 1)
      if (in2 == 0 .and. in1 > 0) in_data(line_counter) = dummy(:in1 - 1)
      if (in2 > 0 .and. in1 > 0) in_data(line_counter) = dummy(:min(in1, in2) - 1)
    end do

    close (in_unit)

  end subroutine param_in_file

  !===========================================================================!
  subroutine param_get_keyword(keyword, found, c_value, l_value, i_value, r_value)
    !===========================================================================!
    !                                                                           !
    !             Finds the value of the required keyword.                      !
    !                                                                           !
    !===========================================================================!

!    use w90_io,        only : io_error

    implicit none

    character(*), intent(in)  :: keyword
    logical, intent(out) :: found
    character(*), optional, intent(inout) :: c_value
    logical, optional, intent(inout) :: l_value
    integer, optional, intent(inout) :: i_value
    real(kind=q), optional, intent(inout) :: r_value

    integer           :: kl, in, loop, itmp
    character(len=maxlen) :: dummy

    kl = len_trim(keyword)

    found = .false.

    do loop = 1, num_lines
      in = index(in_data(loop), trim(keyword))
      if (in == 0 .or. in > 1) cycle
      itmp = in + len(trim(keyword))
      if (in_data(loop) (itmp:itmp) /= '=' &
          .and. in_data(loop) (itmp:itmp) /= ':' &
          .and. in_data(loop) (itmp:itmp) /= ' ') cycle
      if (found) then
        print *, 'Error: Found keyword ', trim(keyword), ' more than once in input file'
        stop
      endif
      found = .true.
      dummy = in_data(loop) (kl + 1:)
      in_data(loop) (1:maxlen) = ' '
      dummy = adjustl(dummy)
      if (dummy(1:1) == '=' .or. dummy(1:1) == ':') then
        dummy = dummy(2:)
        dummy = adjustl(dummy)
      end if
    end do

    if (found) then
      if (present(c_value)) c_value = dummy
      if (present(l_value)) then
        if (index(dummy, 't') > 0) then
          l_value = .true.
        elseif (index(dummy, 'f') > 0) then
          l_value = .false.
        else
          print *, 'Error: Problem reading logical keyword ', trim(keyword)
          stop
        endif
      endif
      if (present(i_value)) read (dummy, *, err=220, end=220) i_value
      if (present(r_value)) read (dummy, *, err=220, end=220) r_value
    end if

    return

220 print *, 'Error: Problem reading keyword ', trim(keyword)
    stop

  end subroutine param_get_keyword

  !=========================================================================================!
  subroutine param_get_keyword_vector(keyword, found, length, c_value, l_value, i_value, r_value)
    !=========================================================================================!
    !                                                                                         !
    !                  Finds the values of the required keyword vector                        !
    !                                                                                         !
    !=========================================================================================!

!    use w90_io,        only : io_error

    implicit none

    character(*), intent(in)  :: keyword
    logical, intent(out) :: found
    integer, intent(in)  :: length
    character(*), optional, intent(inout) :: c_value(length)
    logical, optional, intent(inout) :: l_value(length)
    integer, optional, intent(inout) :: i_value(length)
    real(kind=q), optional, intent(inout) :: r_value(length)

    integer           :: kl, in, loop, i
    character(len=maxlen) :: dummy

    kl = len_trim(keyword)

    found = .false.

    do loop = 1, num_lines
      in = index(in_data(loop), trim(keyword))
      if (in == 0 .or. in > 1) cycle
      if (found) then
        print *, 'Error: Found keyword ', trim(keyword), ' more than once in input file'
        stop
      endif
      found = .true.
      dummy = in_data(loop) (kl + 1:)
      in_data(loop) (1:maxlen) = ' '
      dummy = adjustl(dummy)
      if (dummy(1:1) == '=' .or. dummy(1:1) == ':') then
        dummy = dummy(2:)
        dummy = adjustl(dummy)
      end if
    end do

    if (found) then
      if (present(c_value)) read (dummy, *, err=230, end=230) (c_value(i), i=1, length)
      if (present(l_value)) then
        ! I don't think we need this. Maybe read into a dummy charater
        ! array and convert each element to logical
      endif
      if (present(i_value)) read (dummy, *, err=230, end=230) (i_value(i), i=1, length)
      if (present(r_value)) read (dummy, *, err=230, end=230) (r_value(i), i=1, length)
    end if

    return

230 print *, 'Error: Problem reading keyword ', trim(keyword), ' in param_get_keyword_vector'
    stop

  end subroutine param_get_keyword_vector

  !=================================!
  function utility_lowercase(string)!
    !=================================!
    !                                 !
    ! Takes a string and converts to  !
    !      lowercase characters       !
    !                                 !
    !=================================!

    implicit none

    character(len=*), intent(in) :: string
    character(len=maxlen) :: utility_lowercase

    integer :: iA, iZ, idiff, ipos, ilett

    iA = ichar('A')
    iZ = ichar('Z')
    idiff = iZ - ichar('z')

    utility_lowercase = string

    do ipos = 1, len(string)
      ilett = ichar(string(ipos:ipos))
      if ((ilett .ge. iA) .and. (ilett .le. iZ)) &
        utility_lowercase(ipos:ipos) = char(ilett - idiff)
    enddo

    utility_lowercase = trim(adjustl(utility_lowercase))

    return

  end function utility_lowercase

end module m_io
