module catalog_data
  use gkernel_types
  integer, save :: mmol=-1
  integer, save :: nmol, nsel
  real(8), allocatable :: molfreq(:)
  real(8), allocatable :: selfreq(:)
  integer, allocatable :: ip(:)
  character(len=32), allocatable :: mollong(:)
  character(len=32), allocatable :: molplot(:)
  character(len=16), allocatable :: molshort(:)
  character(len=32), allocatable :: sellong(:)
  character(len=32), allocatable :: selplot(:)
  character(len=16), allocatable :: selshort(:)
  character(len=filename_length) :: mofile
  logical, save :: isastro
  real, save :: eup_max=300
  integer, save :: nlinedb
end module catalog_data
!
subroutine catalog_init(imol,error)
  use gkernel_interfaces
  use catalog_data
  !
  integer, intent(inout) :: imol
  logical, intent(out) :: error
  !
  ! Print *,'MMOL ',imol    ! GCC 10 optimizer bug: it prints 0 if imol == -1 ...
  error = .false.
  if (imol.eq.-1) then  
    call sic_def_real('LINEDB%ENERGY',eup_max,0,0,.false.,error)
    if (error) return
    call sic_def_logi('LINEDB%ASTRO',isastro,.true.,error)
    if (error) return
    imol = 0
  endif
  !
  ! Delete ASTRO catalog if any
  call sic_delvariable('CATALOG',.false.,error)
end subroutine catalog_init
! 
subroutine catalog_comm(line,quiet,error)
  use gkernel_types
  use gkernel_interfaces
  use gbl_message
  use imager_interfaces, only : map_message, catalog_readastro
  use catalog_data
  !---------------------------------------------------------------------
  ! @ private
  ! IMAGER
  !
  !   List or define Spectral Line Data Base(s)
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line
  logical, intent(out) :: quiet
  logical, intent(inout) :: error
  !
  character(len=*), parameter :: rname='CATALOG'
  character(len=1), parameter :: question_mark='?'
  !
  character(len=filename_length) :: line_comm = ' '
  character(len=filename_length) :: chain, file
  real(8) :: r
  logical :: found
  character*16 :: check
  integer :: lt,i,j,narg,nc, iform
  integer(kind=index_length) :: dim(4)
  ! This duplicates the list in LINEDB Python code, but
  ! is unavoidable to check proper Data Base existence
  integer, parameter :: monline=4
  character*12 :: onlines(monline)
  data onlines /'voparis', 'cdms', 'jpl', 'splatalogue'/ !! cam-vamdc, cdms-vamdc
  !
  call catalog_init(mmol, error)
  nmol = 0
  error = .false.
  quiet = .true.
  !
  ! Normal code
  narg = sic_narg(0)
  chain = ' '
  call sic_ch(line,0,1,chain,nc,.false.,error)
  if (chain(1:1).eq.question_mark) then
    if (narg.gt.1) then
      call sic_ch(line,0,2,file,nc,.false.,error)
      call exec_program('@ catalog ? '//file)
    else
      call exec_program('@ catalog ? ')
    endif
    return
  endif
  !
  call sic_delvariable('LINES',.false.,error)
  error = .false.
  call sic_delvariable('LINEDB%DBIN',.false.,error)
  error = .false.
  nlinedb = 0
  !
  ! Note the LowerCase for the "in" argument
  line_comm = "LINEDB\USE in "
  lt = 15
  !
  do i=1,narg
    call sic_ch(line,0,i,chain,nc,.false.,error)
    !
    check = chain
    call sic_lower(check)
    found = .false.
    do j=1,monline
      if (check.eq.onlines(j)) then
        found = .true.
        exit
      endif
    enddo
    !
    if (found) then
      line_comm(lt:) = check
      lt = lt+len_trim(check)+2
    else
      ! If it is not one of the on-line databases, it can be a local
      ! file. Locate it
      found = sic_findfile(chain,file,' ','.linedb')
      !
      if (found) then
        !
        ! Verify if it can be an Astro catalog
        open(unit=1,file=file,status='OLD')               
        read(1,'(A)') check
        !
        if (check(1:7).eq.'SQLite ') then
          isastro = .false.
          iform = 1
        elseif (check(1:1).eq.'!') then
          isastro = .true.
          iform = 2
        else
          chain = 'partfunc.cat'
          inquire(file='parfunc.cat',exist=isastro)
          if (.not.isastro) inquire(file='partfunc.json',exist=isastro)
          if (isastro) then
            ! There is a partfunc file, so it could be Gildas JPL file format
            ! Gildas JPL file start by a Tag identifier followed by molecule NAME
            rewind(1)
            read(1,*) r
            if (nint(r).eq.r) then
              isastro = .false. ! Tag must be an integer
              iform = -1
            else
              ! Astro files would have frequencies in GHz instead, never integers,
              ! so they remain our best guess here...
              iform = 0
            endif
          else
            ! JPL data files format require "partfunc" file, so cannot be JPL
            isastro = .true.
            iform = 0
          endif
        endif
        close(unit=1)
        !
        if (isastro) then
          if (narg.eq.1) then
            call message_colour(-1)
            ! Read the ASTRO catalog
            call catalog_readastro (file,error)
            if (error) then
              call map_message(seve%e,rname,'File '//trim(file)//' is not a Line catalog',1)              
              return
            endif
            if (iform.ne.0) call map_message(seve%w,rname,   &
              & 'Astro catalog should start with a comment line (! sometext...)',1)
            call map_message(seve%i,rname,'File '//trim(file)//' is an Astro catalog',3)
            mofile = file
            dim(1) = 1
            call sic_def_charn('LINEDB%DBIN',mofile,1,1,.true.,error)
            isastro = .true.
            if (sic_varexist("all%catalog")) then
              line_comm = "LET all%catalog 'linedb%dbin'"
              call exec_command(line_comm,error)
            endif
            nlinedb = narg
            return
          else
            call map_message(seve%e,rname,'Astro catalogs are exclusive of any other',1)
            error = .true.
            return
          endif
        else if (iform.eq.1) then
          call map_message(seve%i,rname,'File '//trim(file)//' is a DBMS catalog')              
        else 
          call map_message(seve%w,rname,'File '//trim(file)//' may be a JPL data file',3)                      
        endif
        !
        chain  = '"'//trim(file)//'"'   ! Python "LINEDB\USE" does not parse anything else
        line_comm(lt:) = chain
        lt = lt+len_trim(chain)+2
      else
        call map_message(seve%w,rname,'No such line catalog '//trim(chain),3)
      endif
    endif
  enddo
  nlinedb = narg
  !
  ! Execute the "LINEDB\USE in" command if needed
  if (lt.gt.15) then
    call exec_command(line_comm,error)
    isastro = .false.
    quiet = .false.
    if (error) return
    !
    line_comm = "LET all%catalog 'linedb%dbin'"
  else
    line_comm = 'LET all%catalog "( none defined )"'
    call map_message(seve%w,rname,'No line catalog',3)
  endif
  ! Reset the all%catalog if needed
  if (sic_varexist("all%catalog")) call exec_command(line_comm,error)
  !
end subroutine catalog_comm    
!
subroutine catalog_find(line,error)
  use gildas_def
  use gkernel_interfaces
  use catalog_data
  use gbl_message
  use imager_interfaces, only : map_message
  !---------------------------------------------------------------------
  ! @ private
  !
  ! IMAGER
  !   Support for command 
  !     FIND Fmin Fmax [/SPECIES Name]
  ! Returns in the LINES% structure the frequencies and names of lines
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line        ! Command line
  logical, intent(inout) :: error
  !
  character(len=128) :: cfmin, cfmax  ! Frequency ranges, formatted
  integer :: ni, nm
  character(len=512) :: line_comm
  real(8) :: fmin,fmax
  integer :: k, i
  integer(kind=index_length) :: dim(4)
  !
  integer, parameter :: o_species=1
  integer :: i_spec, l
  character(len=32) :: c_spec
  character(len=8) :: c_enrg
  !
  call sic_delvariable('LINES',.false.,error)
  error = .false.
  if (nlinedb.eq.0) then
    call map_message(seve%w,'FIND','No line catalog(s)',3)
    return
  endif
  !
  i_spec = sic_narg(o_species)
  if (i_spec.eq.1) then
    call sic_ch(line,1,o_species,c_spec,l,.true.,error)
    if (error) return
  endif
  !
  if (nmol.eq.0) then
    ! LINEDB version
    !
    call sic_ch(line,0,1,cfmin,ni,.true.,error)
    if (error) return
    call sic_ch(line,0,2,cfmax,nm,.true.,error)
    if (error) return
    !
    write(c_enrg,'(f6.0)') eup_max
    if (i_spec.eq.1) then
      line_comm = "LINEDB\SELECT /FREQ "//cfmin(1:ni)//' '//cfmax(1:nm)// &
      & " /ENERGY "//c_enrg//" /SORT aij /SPECIES "//'"'//trim(c_spec)//'"'
    else
      line_comm = "LINEDB\SELECT /FREQ "//cfmin(1:ni)//' '//cfmax(1:nm)// &
        & " /ENERGY "//c_enrg//" /SORT aij"
    endif
    !!Print *,trim(line_comm)
    call exec_program(line_comm)
    call exec_program('@ catalog_lines')
  else
    !
    ! ASTRO version - Catalog frequencies are in GHz
    call sic_r8(line,0,1,fmin,.true.,error)
    if (error) return
    call sic_r8(line,0,2,fmax,.true.,error)
    if (error) return
    fmin = fmin*1d-3
    fmax = fmax*1d-3
    !
    ! LINES%N will contain the number of lines
    k = 0
    do i=1,nmol
      if (molfreq(i).ge.fmin .and. molfreq(i).le.fmax) then
        k = k+1
        ip(k) = i
      endif
    enddo
    nsel = k
    l = 0
    do k=1,nsel
      i = ip(k)
      if (i_spec.eq.1) then
        if (molshort(i).ne.c_spec) cycle 
      endif
      l=l+1
      selfreq(l) = molfreq(i)*1d3
      sellong(l) = mollong(i)
      selplot(l) = molplot(i)
      selshort(l) = molshort(i)
    enddo
    nsel = l
    !
    call sic_defstructure('LINES',.true.,error)
    if (nsel.ne.0) then
      dim(1) = nsel
      call sic_def_dble('LINES%FREQUENCY',selfreq,1,dim,.true.,error)
      call sic_def_charn('LINES%LINES',sellong,1,dim,.true.,error)
      call sic_def_charn('LINES%PLOT',selplot,1,dim,.true.,error)
      call sic_def_charn('LINES%SPECIES',selshort,1,dim,.true.,error)
      nsel = -nsel
    endif
    call sic_def_inte('LINES%N',nsel,0,0,.true.,error)
  endif 
end subroutine catalog_find
!
subroutine catalog_readastro (molfile,error)
  use gildas_def
  use gkernel_interfaces
  use gbl_message
  use imager_interfaces, only : map_message
  use catalog_data
  !---------------------------------------------------------------------
  ! @ private
  !    IMAGER
  !  Read an Astro catalog
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: molfile        ! Catalog name
  logical, intent(inout) :: error
  ! Local
  character(len=*), parameter :: rname='CATALOG'
  character(len=256) :: chain
  character(len=32) :: aname,bname,aline
  real(kind=8) :: afreq
  integer(kind=4) :: i,k,lun,nv,ier,lt,ls
  integer(kind=4) :: icsv, iend, step
  logical :: skip, worry
  character(len=16) :: varname
  !
  error = .false.
  !
  varname = 'CATALOG'
  !
  ! Open file
  nmol = 0
  ier = sic_getlun(lun)
  if (ier.ne.1) then
    call map_message(seve%e,rname,'Cannot allocate LUN')
    error = .true.
    return
  endif
  open(unit=lun,file=molfile(1:len_trim(molfile)), status='OLD',iostat=ier)
  if (ier.ne.0) then
    call map_message(seve%e,rname,'Cannot open file: '//trim(molfile))
    call sic_frelun(lun)
    error = .true.
    return
  endif
  !
  ! Read file - Two pass to properly dimension the Number of molecules
  !
  ! Verify if strictly ASTRO conformant or not...
  read(lun,'(A)') aname
  worry = (aname(1:1).ne.'!') 
  if (worry) worry = (index(aname,';').eq.0)
  rewind(lun)
  !
  nmol = 0
  do step = 1,2
    do 
      read(lun,'(A)',iostat=ier) chain
      if (ier.lt.0) exit
      !
      if (ier.ne.0 .or. lenc(chain).eq.0) cycle
      ! Comment line
      if (chain(1:1).eq.'!') cycle
      !
      icsv = index(chain,';')
      if (icsv.gt.1) then
        ! Comma separated value
        read(chain(1:icsv-1),*,iostat=ier) afreq
        if (ier.ne.0) cycle
        aname = chain(icsv+1:)
        iend = index(aname,';')
        aline = aname(iend+1:)
        aname(iend:) = ' '
        iend = index(aline,';')
        if (iend.ne.0) aline(iend:) = ' '
      else if (icsv.eq.1) then
        ! CSV comment line
        cycle
      else
        ! Historical ASTRO format
        ! Note that since Fortran-90, quotes are no longer required to delimit strings 
        ! in List-Directed format. They are only required when spaces should be in the
        ! strings.
        read(chain,*,iostat=ier) afreq, aname, aline
        if (ier.ne.0) then
          aline = ' '
          read(chain,*,iostat=ier) afreq, aname
          if (ier.ne.0) cycle
        endif
        ! If not strictly conformant, ignore values that are integers, as
        ! the likelihood to have spectral line EXACTLY a number of GHz is tiny...
        if (worry) then
          if (nint(afreq).eq.afreq) cycle
        endif
        if (len_trim(aname).ne.0) then
          bname = aname
          iend = 0
        else
          aname = bname ! Inherit last molecule name if not specified
          iend = 1
        endif 
      endif
      nmol = nmol + 1
      ! First step: just count
      if (step.eq.1) cycle
      !
      ! Second Step: fill array
      molfreq(nmol) = afreq
      !
      lt = len_trim(aname)
      ls = index(aname(1:lt),' ')
      if (ls.ne.0) then
        aline = aname(ls+1:lt)//aline
        lt = ls
      else
        lt = lt+1
      endif
      molplot(nmol) = aname(1:lt)//aline
      !
      k = 0
      skip = .false.
      do i=1,lt
        if (skip) then
          if (aname(i:i).ne."\") skip = .false.
        else
          if (aname(i:i).eq."\") then
            skip = .true.
          else
            k = k+1
            aname(k:k) = aname(i:i)
          endif
        endif
      enddo
      molshort(nmol) = aname(1:k)
      mollong(nmol) = aname(1:k)//aline
      if (nmol.ge.mmol) exit
    enddo
    !
    if (step.eq.1) then
      if (nmol.gt.mmol) then
        write(chain,'(A,I0,A)') 'Allocating space for ',nmol,' spectral lines'
        call map_message(seve%i,rname,chain)
        if (mmol.ne.0) deallocate(molfreq,selfreq,mollong,molplot,molshort,sellong,selplot,selshort,ip)
        mmol = nmol
        allocate(molfreq(mmol),selfreq(mmol),mollong(mmol),molplot(mmol),molshort(mmol), &
        & sellong(mmol),selplot(mmol),selshort(mmol),ip(mmol))
      endif  
      rewind(lun)
      nmol = 0
    endif
  enddo
  !
  close(unit=lun)
  call sic_frelun(lun)
  !
  if (nmol.eq.0) then
    error = .true.
    return
  endif
  !
  ! Create the SIC substructure 
  !
  call sic_defstructure(varname,.true.,error)
  if (error) return
  nv = len_trim(varname)
  call sic_def_inte(varname(1:nv)//'%N',nmol,0,0,.true.,error)
  call sic_def_dble(varname(1:nv)//'%FREQ',molfreq,1,nmol,.true.,error)
  call sic_def_charn(varname(1:nv)//'%NAME',mollong,1,nmol,.true.,error)
  call sic_def_charn(varname(1:nv)//'%SHORT',molshort,1,nmol,.true.,error)
  call sic_def_charn(varname(1:nv)//'%PLOT',molplot,1,nmol,.true.,error)
  if (error) return
  !
end subroutine catalog_readastro
