!# This source file is distributed with code Pégase.3.0.1 (2019-02-21).
!# Copyright: Michel Fioc (Michel.Fioc@iap.fr), Sorbonne université, 
!# Institut d'astrophysique de Paris/CNRS, France.
!# 
!# Pégase.3.0.1 is governed by the CeCILL license under French law and abides 
!# by the rules of distribution of free software. You can use, modify and/or 
!# redistribute this software under the terms of the CeCILL license as circulated 
!# by CEA, CNRS and INRIA at "http://www.cecill.info". The text of this license
!# is also available in French and in English in directory "doc_dir/" of this
!# code.
!# 
!# As a counterpart to the access to the source code and to the rights to copy,
!# modify and redistribute it granted by the license, users are provided only
!# with a limited warranty, and the software's author, the holder of the
!# economic rights, and the successive licensors have only limited
!# liability. 
!# 
!# The fact that you are presently reading this means that you have had
!# knowledge of the CeCILL license and that you accept its terms.
!#====================================================================== 

module mod_select_file

  use mod_types, only : long_string
  use mod_read_file_list, only : read_file_list

  implicit none
  private
  integer, parameter :: n_max_text_lines = 15

  public :: select_single_file, select_several_files

contains

!#======================================================================
!# Select one single file from a list.

  subroutine select_single_file(dir, list, selected_file, object, objects, back, &
       exist_warning, print_first_line)

    use mod_file_access, only : open_file, close_file, path_file
    use mod_strings, only : HT, quote_string, unquote_string

    implicit none
    character(len=*), intent(in) :: dir
    character(len=*), intent(in) :: list
    character(len=*), intent(out) :: selected_file
    character(len=*), intent(in), optional :: object, objects
    logical, intent(in), optional :: back, exist_warning, print_first_line
!#......................................................................
    logical :: exist_list, exist_file, void, back_eff, exist_warning_eff, print_first_line_eff
    integer :: error, n_files, i_file, choice
    character(len=long_string) :: string, object_eff, objects_eff, &
         current_file, first_line
    character(len=long_string), dimension(:), pointer :: file => null()
    logical :: provide_directly
    integer :: unit
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    if (present(object)) then
       object_eff = object
    else
       object_eff = "file"
    endif
    if (present(objects)) then
       objects_eff = objects
    else
       objects_eff = "files"
    endif
    if (present(back)) then
       back_eff = back
    else
       back_eff = .false.
    endif
    if (present(exist_warning)) then
       exist_warning_eff = exist_warning
    else
       exist_warning_eff = .false.
    endif
    if (present(print_first_line)) then
       print_first_line_eff = print_first_line
    else
       print_first_line_eff = .false.
    endif

    inquire(file =  path_file(dir, list), exist = exist_list)
    if (exist_list) then
       call read_file_list(dir, list, file, n_files, exist_warning_eff, &
            back_eff)
       if (n_files == 0) then
          write(*, "(a)") "Warning: list """ // trim(list) // """ is void."
          void = .true.
       else
          void = .false.
       endif
    else
       write(*, "(a)") "Warning: list """ // trim(list) // """ does not exist."
    endif

    if (exist_list .and. .not.void) then
       if (back_eff) then
          current_file = file(n_files)
       else
          current_file = file(1)
       endif

       if (print_first_line_eff) then
          call open_file(unit, path_file(dir, current_file))
          read(unit,*) first_line
          call close_file(unit)
          first_line = adjustl(first_line)
       endif

       if (print_first_line_eff .and. first_line /= "") then
          write(*, "(a)") "Default " // trim(object_eff) // ": " // &
               trim(first_line) // " (file """ // trim(current_file) // """)."
       else
          write(*, "(a)") "Default " // trim(object_eff) // ": """ // &
               trim(current_file) // """."
       endif

       do
          write(*, "(/a)") "Either"
          write(*, "(a)") "-- press the <RETURN>/<ENTER> key to select the default " // &
               trim(object_eff) // ","
          write(*, "(a)") "-- press <TAB>, then <RETURN> to show a list of possible " // &
               trim(objects_eff) // ", or"
          write(*, "(a/a/a)") "-- enter the name of the " // trim(object_eff) // &
               " you want to select (even if not in the list).", &
               "   If the file is in a subdirectory of """ // trim(dir) // &
               """ or in another directory, ", &
               "   the path (relative to """ // trim(dir) // """ or absolute) must be present."
          provide_directly = .false.
          read(*, "(a)") string
          string = adjustl(string)

          if (string == "") then !# Default.
             if (back_eff) then
                selected_file = file(n_files)
             else
                selected_file = file(1)
             endif

          else if (string == HT) then !# Select from a list.
             file_loop : do i_file = 1, n_files
                if (back_eff) then
                   current_file = file(n_files+1-i_file)
                else
                   current_file = file(i_file)
                endif
                if (print_first_line_eff) then
                   call open_file(unit, path_file(dir, current_file))
                   read(unit,*) first_line
                   call close_file(unit)
                   first_line = adjustl(first_line)
                endif

                if (print_first_line_eff .and. first_line /= "") then
                   write(*, "(tr3,i0,a)") i_file, ": " // trim(first_line) // &
                        " (file """ // trim(current_file) // """)."
                else
                   write(*, "(tr3,i0,a)") i_file, ": """ // trim(current_file) // &
                        """."
                endif

                if (mod(i_file, n_max_text_lines) == 0) then
                   do
                      write(*, "(/a)") "Either"
                      write(*, "(a)") "-- enter the number of the " // trim(object_eff) // &
                           " you want to select,"
                      write(*, "(a)") "-- press <TAB>, then <RETURN> to show the next " // &
                           trim(objects_eff) // " in the list,"
                      write(*, "(a)") "-- press <RETURN> to stop, or"
                      write(*, "(a)") "-- enter ""0"" to return to previous menu."

                      read(*, "(a)") string
                      string = adjustl(string)

                      if (string == "") stop

                      if (string == HT) exit
                      read(string,*,iostat = error) choice
                      if (error == 0) then
                         if (choice >= 1 .and. choice <= n_files) then
                            if (back_eff) then
                               selected_file = file(n_files+1-choice)
                            else
                               selected_file = file(choice)
                            endif
                            exit file_loop
                         else if (choice == 0) then
                            selected_file = ""
                            exit file_loop
                         else
                            write(*, "(a,i0,a)") "The number should be between &
                                 &1 and ", n_files, "!"
                            cycle
                         endif
                      else
                         write(*, "(a)") "??"
                      endif
                   enddo
                else if (i_file == n_files) then
                   do
                      write(*, "(/a)") "Either"
                      write(*, "(a)") "-- enter the number of the " // trim(object_eff) // " you want to select,"
                      write(*, "(a)") "-- press <RETURN> to stop, or"
                      write(*, "(a)") "-- enter ""0"" to return to previous menu."

                      read(*, "(a)") string
                      string = adjustl(string)

                      if (string == "") stop

                      read(string,*,iostat = error) choice

                      if (error == 0) then
                         if (choice >= 1 .and. choice <= n_files) then
                            if (back_eff) then
                               selected_file = file(n_files+1-choice)
                            else
                               selected_file = file(choice)
                            endif
                            exit file_loop
                         else if (choice == 0) then
                            selected_file = ""
                            exit file_loop
                         else
                            write(*, "(a,i0,a)") "The number should be between &
                                 &1 and ", n_files, "!"
                            cycle
                         endif
                      else
                         write(*, "(a)") "??"
                      endif
                   enddo
                endif
             enddo file_loop

          else !# Provide name of the file directly.
             provide_directly = .true.
             string = unquote_string(string)
             selected_file = adjustl(string)
          endif
          if (selected_file == "") cycle

          inquire(file = path_file(dir, selected_file), exist = exist_file)
          if (exist_file) then
             if (provide_directly) then !# Update the list of files.
                call open_file(unit, path_file(dir, list), position="append", &
                     action="write")
                write(unit, "(a)") quote_string(selected_file)
                call close_file(unit)
             endif
             exit
          else
             write(*, "(a)") trim(object_eff) // " """ // trim(selected_file) // &
                  """ does not exist."
          endif
       enddo

    else !# Non-existent or void list.
       do
          write(*, "(/a)") "Either"
          write(*, "(a)") "-- enter the name of the " // trim(object_eff) // " you want to select, or"
          write(*, "(a)") "-- press <RETURN> to stop."

          read(*, "(a)") string

          if (string == "") then
             stop

          else
             string = unquote_string(string)
             selected_file = adjustl(string)
             inquire(file = path_file(dir, selected_file), exist = exist_file)
             if (exist_file) then
                if (exist_list) then !# The list of files exists but is void. Update it!
                   call open_file(unit, path_file(dir, list), &
                        position="append", action="write")
                   write(unit, "(a)") quote_string(selected_file)
                   call close_file(unit)
                endif
                exit
             else
                write(*, "(a)") trim(object_eff) // " """ // trim(selected_file) // &
                     """ does not exist."
             endif
          endif
       enddo
    endif

  end subroutine select_single_file

!#======================================================================
!# Select one or more files from a list.

  subroutine select_several_files(dir, list, selected_file, n_selected_files, object, &
       objects, back, exist_warning)

    use mod_file_access, only : open_file, close_file, path_file
    use mod_strings, only : quote_string, unquote_string, HT
    use mod_types
    use mod_linked_list

    implicit none
    character(len=*), intent(in) :: dir
    character(len=*), intent(in) :: list
    character(len=*), dimension(:), pointer :: selected_file
    integer, intent(out) :: n_selected_files
    character(len=*), intent(in), optional :: object, objects
    logical, intent(in), optional :: back, exist_warning
!#......................................................................
    logical :: exist_list, exist_file, void, back_eff, exist_warning_eff
    integer :: error, n_files, i_file, choice
    character(len=long_string) :: string, object_eff, objects_eff, current_file
    character(len=long_string), dimension(:), pointer :: file => null()
    logical :: provide_directly
    integer :: unit
    type(lk_lst_long_string) :: head_node
    type(lk_lst_long_string), pointer :: current_node
    character(len=len(selected_file)) :: potential_file
!#::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    if (present(object)) then
       object_eff = object
    else
       object_eff = "file"
    endif
    if (present(objects)) then
       objects_eff = objects
    else
       objects_eff = "files"
    endif
    if (present(back)) then
       back_eff = back
    else
       back_eff = .false.
    endif
    if (present(exist_warning)) then
       exist_warning_eff = exist_warning
    else
       exist_warning_eff = .false.
    endif

    inquire(file =  path_file(dir, list), exist = exist_list)
    if (exist_list) then
       call read_file_list(dir, list, file, n_files, exist_warning_eff, &
            back_eff)
       if (n_files == 0) then
          write(*, "(a)") "Warning: list """ // trim(list) // """ is void."
          void = .true.
       else
          void = .false.
       endif
    else
       write(*, "(a)") "Warning: list """ // trim(list) // """ does not exist."
    endif

    n_selected_files = 0
    call lk_lst_initialize(head_node)
    if (exist_list .and. .not.void) then
       file_loop : do i_file = 1, n_files
          if (back_eff) then
             current_file = file(n_files+1-i_file)
          else
             current_file = file(i_file)
          endif
          write(*, "(tr3,i0,a)") i_file, ": """ // trim(current_file) // """."

          if  (mod(i_file, n_max_text_lines) == 0) then
             write(*, "(/a)") "-- Enter the integers associated to the " // trim(objects_eff) // &
                  " you want to select (press the <RETURN>/<ENTER> key after each)."
             write(*,"(a/a/a)") "   You may enter their names instead (even if not in the list), " // &
                  "but if the file is in a subdirectory ", &
                  "   of """ // trim(dir) // """ or in another directory, " // &
                  "the path (relative to """ // trim(dir) // """ or absolute) ", &
                  "   must be present;"
             write(*, "(a)") "-- press <TAB>, then <RETURN> to show the next " // trim(objects_eff) // &
                  " in the list;"
             write(*, "(a)") "-- press <RETURN> to stop selecting additional " // trim(objects_eff) // "."

             do
                provide_directly = .false.
                read(*, "(a)") string
                string = adjustl(string)

                if (string == "") exit file_loop !# Stop reading files.

                if (string == HT) exit

                if (verify(trim(string), "0123456789") == 0) then !# Integer input.
                   read(string,*,iostat = error) choice
                   if (error == 0) then
                      if (choice >= 1 .and. choice <= n_files) then
                         if (back_eff) then
                            potential_file = file(n_files+1-choice)
                         else
                            potential_file = file(choice)
                         endif
                      else
                         write(*, "(a,i0,a)") "The number should be between 1 and ", &
                              n_files, "!"
                         cycle
                      endif
                   else
                      write(*, "(a)") "??"
                   endif

                else !# Provide name of the file directly.
                   provide_directly = .true.
                   string = unquote_string(string)
                   potential_file = adjustl(string)
                endif

                inquire(file = path_file(dir, potential_file), exist = exist_file)
                if (exist_file) then
                   n_selected_files = n_selected_files+1
                   call lk_lst_new_node(head_node, current_node)
                   current_node % val = potential_file
                   if (provide_directly) then !# Update the list of files.
                      call open_file(unit, path_file(dir, list), &
                           position="append", action="write")
                      write(unit, "(a)") quote_string(potential_file)
                      call close_file(unit)
                   endif
                else
                   write(*, "(a)") trim(object_eff) // " """ // &
                        trim(potential_file) // """ does not exist."
                endif
             enddo

          else if (i_file == n_files) then
             write(*, "(/a)") "-- Enter the integers associated to the " // trim(objects_eff) // &
                  " you want to select (press the <RETURN>/<ENTER> key after each)."
             write(*,"(a/a/a)") "   You may enter their names instead (even if not in the list), " // &
                  "but if the file is in a subdirectory ", &
                  "   of """ // trim(dir) // """ or in another directory, " // &
                  "the path (relative to """ // trim(dir) // """ or absolute) ", &
                  "   must be present;"
             write(*, "(a)") "-- press <RETURN> to stop selecting additional " // trim(objects_eff) // "."

!# Note: typing <TAB> in the last screen prints ''file "  " does not exist.''.
!# Should it restart the loop on the list of files???

             do
                provide_directly = .false.
                read(*, "(a)") string
                string = adjustl(string)

                if (string == "") exit file_loop !# Stop reading files.

                if (verify(trim(string), "0123456789") == 0) then !# Integer input.
                   read(string,*,iostat = error) choice
                   if (error == 0) then
                      if (choice >= 1 .and. choice <= n_files) then
                         if (back_eff) then
                            potential_file = file(n_files+1-choice)
                         else
                            potential_file = file(choice)
                         endif
                      else
                         write(*, "(a,i0,a)") "The number should be between 1 and ", &
                              n_files, "!"
                         cycle
                      endif
                   else
                      write(*, "(a)") "??"
                   endif

                else !# Provide name of the file directly.
                   provide_directly = .true.
                   string = unquote_string(string)
                   potential_file = adjustl(string)
                endif

                inquire(file = path_file(dir, potential_file), exist = exist_file)
                if (exist_file) then
                   n_selected_files = n_selected_files+1
                   call lk_lst_new_node(head_node, current_node)
                   current_node % val = potential_file
                   if (provide_directly) then !# Update the list of files.
                      call open_file(unit, path_file(dir, list), &
                           position="append", action="write")
                      write(unit, "(a)") quote_string(potential_file)
                      call close_file(unit)
                   endif
                else
                   write(*, "(a)") trim(object_eff) // " """ // &
                        trim(potential_file) // """ does not exist."
                endif
             enddo
          endif
       enddo file_loop

    else !# Non-existent or void list.
       write(*, "(/a)") "-- Enter the name of the " // trim(objects_eff) // " you want to select &
            &(press the <RETURN>/<ENTER> key after each);"
       write(*, "(a)") "-- press <RETURN> to stop selecting additional " // trim(objects_eff) // "."

       do
          read(*, "(a)") string
          if (string == "") then
             exit

          else
             string = unquote_string(string)
             potential_file = adjustl(string)
             inquire(file = path_file(dir, potential_file), &
                  exist = exist_file)
             if (exist_file) then
                n_selected_files = n_selected_files+1
                call lk_lst_new_node(head_node, current_node)
                current_node % val = potential_file
                if (exist_list) then !# The list of files exists but is void: \
!# update it! Do not create it if it does not already exist.
                   call open_file(unit, path_file(dir, list), &
                        position="append", action="write")
                   write(unit, "(a)") quote_string(potential_file)
                   call close_file(unit)
                endif
             else
                write(*, "(a)") trim(object_eff) // " """ // trim(potential_file) // &
                     """ does not exist."
             endif
          endif
       enddo
    endif

    call lk_lst_to_array(head_node, selected_file, n_selected_files)

  end subroutine select_several_files

end module mod_select_file
