module catalog_data
  use gkernel_types
  character(len=filename_length) :: mofile  !! Catalog file name
  logical, save :: isastro          !! Type of catalog
  real, save :: eup_max=300         !! Energy threshold
  integer, save :: nlinedb          !! Number of LINEDB data bases
  ! For catalog lines
  integer, save :: mmol=-1          !! Size of arrays
  integer, save :: nmol             !! Number of lines in catalog
  real(8), allocatable :: molfreq(:)    !! Frequencies in MHz
  character(len=32), allocatable :: mollong(:)  !! Long names
  character(len=32), allocatable :: molplot(:)  !! Displayed names
  character(len=16), allocatable :: molshort(:) !! Short names
  ! As above, for selected lines
  integer, save :: nsel                 !! Number of selected lines
  integer, allocatable :: ip(:)         !! Pointers to catalog lines
  real(8), allocatable :: selfreq(:)    !! Selected line frequencies
  character(len=32), allocatable :: sellong(:)
  character(len=32), allocatable :: selplot(:)
  character(len=16), allocatable :: selshort(:)
end module catalog_data
!
subroutine catalog_init(imol,error)
  use gkernel_interfaces
  use catalog_data
  !
  !! IMAGER --  Initialize or Update catalog variables
  integer, intent(inout) :: imol  !! Number of molecules
  logical, intent(out) :: error   !! Logical error flag
  !
  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)
  nmol = 0
  ! Delete all other catalogs
  call sic_delvariable('LINES',.false.,error)
  error = .false.
  call sic_delvariable('LINEDB%DBIN',.false.,error)
  error = .false.
  nlinedb = 0
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 -- Support for command CATALOG
  !
  !   List or define Spectral Line Data Base(s)
  !!
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line  !! Command line
  logical, intent(out) :: quiet         !! Quiet flag (no message)
  logical, intent(inout) :: error       !! Logical error flag
  !
  ! Constants
  character(len=*), parameter :: rname='CATALOG'
  character(len=1), parameter :: question_mark='?'
  ! 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
  !
  ! Local ---
  character(len=filename_length) :: line_comm = ' '
  character(len=filename_length) :: chain, file
  real(8) :: r
  logical :: found
  character(len=16) :: check
  integer :: lt,i,j,narg,nc, iform
  integer(kind=index_length) :: dim(4)
  !
  ! Code ----
  if (sic_present(1,0)) then
    call catalog_filter(line,quiet,error)
    return
  endif
  !
  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
  !
  ! Ensure things are properly initialized
  call catalog_init(mmol, error)
  !
  ! Reset if asked for.
  check = chain
  call sic_upper(check)
  if (check.eq."NONE") return
  !
  ! 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
            ! 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
            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 [/CHECK]  [/SPECIES Name]
  !
  ! Returns in the LINES% structure the frequencies and names of lines
  !!
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line    !! Command line
  logical, intent(inout) :: error         !! Logical Error Flag
  !
  integer, parameter :: o_check=1       ! Verbose mode...
  integer, parameter :: o_species=2
  ! Local ---
  character(len=128) :: cfmin, cfmax  ! Frequency ranges, formatted
  character(len=32) :: start_comm
  integer :: ni, nm, ns
  character(len=512) :: line_comm
  real(8) :: fmin,fmax
  integer :: k, i
  integer(kind=index_length) :: dim(4)
  integer :: i_spec, l
  character(len=32) :: c_spec
  character(len=8) :: c_enrg
  !
  ! Code ----
  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,o_species,1,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
    !
    if (sic_present(o_check,0)) then
      start_comm = "LINEDB\SELECT /FREQ "
    else
      start_comm = "LINEDB\SELECT /QUIET /FREQ "
    endif
    ns = len_trim(start_comm)+1
    !
    write(c_enrg,'(f6.0)') eup_max
    if (i_spec.eq.1) then
      line_comm = start_comm(1:ns)//cfmin(1:ni)//' '//cfmax(1:nm)// &
        & " /ENERGY "//c_enrg//" /SORT aij /SPECIES "//'"'//trim(c_spec)//'"'
    else
      line_comm = start_comm(1:ns)//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 - In memory Catalog frequencies are in MHz
    call sic_r8(line,0,1,fmin,.true.,error)
    if (error) return
    call sic_r8(line,0,2,fmax,.true.,error)
    if (error) return
    !
    ! 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)
      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 -- Support for command CATALOG
  !
  !  Read an Astro catalog, or one of its CSV variants
  !  The Frequency unit can be specified in the first Line
  !  if it is a comment line.
  !    (recognized values are GHz, MHz, kHz and Hz only)
  !!
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: molfile   !! Catalog name
  logical, intent(inout) :: error           !! Logical Error Flag
  !
  character(len=*), parameter :: rname='CATALOG'
  !
  ! Local ---
  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, inext, ilast
  logical :: skip, worry
  character(len=16) :: varname
  real(8) :: fscale
  integer :: icase
  !
  ! Code ----
  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,
  ! and check the Frequency unit if specified
  read(lun,'(A)') chain
  worry = (chain(1:1).eq.'!').or.(chain(1:1).eq.';')
  if (worry) then
    if (index(chain,'Hz').ne.0) then
      icase = -6
      if (index(chain,'MHz').ne.0) icase = 0
      if (index(chain,'GHz').ne.0) icase = 3
      if (index(chain,'kHz').ne.0) icase = -3      
      fscale = 10.D0**icase
      select case (icase)
      case (-6)      
        call map_message(seve%i,rname,'Converting frequencies from Hz to MHz',3)
      case (-3)
        call map_message(seve%i,rname,'Converting frequencies from kHz to MHz',3)
      case (3)
        call map_message(seve%i,rname,'Converting frequencies from GHz to MHz',3)
      case (0)
        call map_message(seve%i,rname,'Frequencies are already specified in MHz')
      end select
      worry = .false.
    else
      fscale = 1D3        ! Default frequency unit is GHz
      call map_message(seve%w,rname,'Assuming Frequencies are in GHz')
    endif
  else
    fscale = 1D3          ! Default frequency unit is GHz
    worry = (index(chain,';').eq.0)
    call map_message(seve%w,rname,'Assuming Frequencies are in GHz')
  endif
  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. len_trim(chain).eq.0) cycle
      ! Comment line
      if (chain(1:1).eq.'!') cycle
      !
      icsv = index(chain,';')
      if (icsv.gt.1) then
        !Print *,"CHAIN ",chain
        ! Comma separated value
        read(chain(1:icsv-1),*,iostat=ier) afreq
        if (ier.ne.0) cycle
        inext = index(chain(icsv+1:),';')
        if (inext.ne.0) then
          aname = adjustl(chain(icsv+1:icsv+inext-1))
          !Print *,'ANAME ['//trim(aname)//']'
          ilast = index(chain(icsv+inext+2:),';')
          if (ilast.ne.0) then
            ilast = icsv+inext+ilast
          else
            ilast = len_trim(chain)
          endif
          aline = adjustl(chain(icsv+inext+2:ilast))
          !Print *,'ALINE ['//trim(aline)//']'
        else
          aname = adjustl(chain(icsv+1:))
          aline = ' '
        endif
      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*fscale
      !
      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
  if (worry) call map_message(seve%w,rname,   &
    & 'Astro catalog should start with a comment line with Frequency unit, e.g. "! MHz"',1)
  !
  ! 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
!
subroutine catalog_filter(line,quiet,error)
  use gkernel_types
  use gkernel_interfaces
  use gbl_message
  use imager_interfaces, only : map_message, catalog_readastro
  !---------------------------------------------------------------------
  ! @ private
  !* 
  ! IMAGER -- Support for command  
  !   CATALOG InFile /SORT OutFile [precis]
  !
  !     Sort and order an Astro Catalog, only considering
  !   new Lines if they are more than PRECIS MHz apart 
  !!
  !---------------------------------------------------------------------
  character(len=*), intent(in) :: line  !! Command line
  logical, intent(out) :: quiet         !! Quiet flag (no message)
  logical, intent(inout) :: error       !! Logical error flag
  !
  character(len=*), parameter :: rname='CATALOG /SORT'
  !
  ! Local ---
  character(len=filename_length) :: fich, file, ofile
  real(8) :: r
  logical :: found, isastro
  character*16 :: check
  integer :: nc, iform, imol
  character(len=256) :: chain
  character(len=32) :: aname,bname,aline
  character(len=32), allocatable :: molname(:), molline(:)
  real(kind=8), allocatable :: molfreq(:)
  real(kind=8) :: afreq, dfreq
  integer, allocatable :: indx(:)
  integer :: nmol, mmol
  integer(kind=4) :: lun,ier
  integer(kind=4) :: icsv, iend, step
  logical :: worry
  character(len=16) :: varname
  real(8) :: fscale
  !
  ! Code ----
  nmol = 0
  error = .false.
  quiet = .true.
  !
  if (sic_narg(0).ne.1) then
    call map_message(seve%e,rname,'One and only one filename as argument required')
    error = .true.
    return
  endif
  !
  call sic_ch(line,1,1,fich,nc,.true.,error)
  if (error) return
  call sic_parsef(fich,ofile,' ','.lin')
  dfreq = 0.d0
  call sic_r8(line,1,2,dfreq,.false.,error)
  if (error) return
  !
  call sic_ch(line,0,1,fich,nc,.false.,error)
  ! If it is not one of the on-line databases, 
  ! it can be a local file. Locate it
  found = sic_findfile(fich,file,' ','.lin')
  if (.not.found) then
    call map_message(seve%e,rname,'No such catalog '//trim(file))
    error = .true.
    return
  endif
  ier = sic_getlun(lun)
  if (ier.ne.1) then
    call map_message(seve%e,rname,'Cannot allocate LUN')
    error = .true.
    return
  endif
  !
  ! Verify if it is an Astro catalog
  open(unit=lun,file=file,status='OLD')               
  read(lun,'(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(lun)
      read(lun,*) 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
  !
  if (.not.isastro) then
    close(unit=lun)
    call map_message(seve%e,rname,'LINEDB catalogs or JPL files not supported in CATALOG /SORT')
    call sic_frelun(lun)
    error = .true.
    return
  endif
  !
  ! Now read the Catalog
  varname = 'CATALOG'
  fscale = 1D3          ! Default frequency unit is GHz
  !
  ! Open file and Check the Frequency units
  nmol = 0
  rewind(unit=lun)
  !
  ! Read file - Two pass to properly dimension the Number of molecules
  !
  ! Verify if strictly ASTRO conformant or not,
  ! and check the Frequency unit if specified
  read(lun,'(A)') aname
  worry = (aname(1:1).ne.'!') 
  if ((.not.worry).or.(aname(1:1).eq.';')) then
    if (index(aname,'Hz').ne.0) then
      fscale = 1D-6
      if (index(aname,'MHz').ne.0) fscale=1D0
      if (index(aname,'GHz').ne.0) fscale=1D3
      if (index(aname,'kHz').ne.0) fscale=1D-3
    endif
  else
    worry = (index(aname,';').eq.0)
  endif
  rewind(unit=lun)
  !
  nmol = 0
  do step = 1,2
    do 
      read(lun,'(A)',iostat=ier) chain
      if (ier.lt.0) exit
      !
      if (ier.ne.0 .or. len_trim(chain).eq.0) cycle
      ! Comment line
      if (chain(1:1).eq.'!') cycle
      !
      icsv = index(chain,';')
      if (icsv.gt.1) then
        !Print *,"CHAIN ",chain
        ! Comma separated value
        read(chain(1:icsv-1),*,iostat=ier) afreq
        if (ier.ne.0) cycle
        aname = adjustl(chain(icsv+1:))
        !Print *,"ANAME ",aname(1:30)
        iend = index(aname,';')
        aline = adjustl(aname(iend+1:))
        !Print *,"ALINE ",aline(1:30)
        aname(iend:) = ' '
        !Print *,"ANAME ",aname(1:30)
        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
      ! Store the Frequencies and Lines
      molfreq(nmol) = afreq*fscale
      molname(nmol) = aname
      molline(nmol) = aline
    enddo
    !
    if (step.eq.1) then
      if (nmol.eq.0) exit
      !
      write(chain,'(A,I0,A)') 'Allocating space for ',nmol,' spectral lines'
      call map_message(seve%i,rname,chain)
      mmol = nmol
      allocate(molfreq(mmol),molline(mmol),molname(nmol),indx(nmol),stat=ier)
      rewind(lun)
      nmol = 0
    endif
  enddo
  !
  close(unit=lun)
  !
  if (nmol.eq.0) then
    call map_message(seve%w,rname,'No molecules in catalog')
    call sic_frelun(lun)
    return
  endif
  !
  ! Sort by Frequencies
  call gr8_trie(molfreq,indx,nmol,error)
  if (error) then
    call sic_frelun(lun)
    return
  endif
  !
  ! Write Sorted result
  ier = sic_open(lun,ofile,'NEW',.false.)
  if (ier.ne.0) then
    call map_message(seve%r,rname,'Cannot create '//trim(ofile))
    call sic_frelun(lun)
    error = .true.
    return
  endif
  write(lun,'(A)') '! Astro CSV catalog format, unit MHz'
  !
  nmol = 0
  afreq = 0
  aname = ' '
  do imol=1,mmol
    if (abs(molfreq(imol)-afreq).gt.dfreq) then
      nmol = nmol+1
      write(lun,'(F14.6,A,A,A,A,A)') molfreq(imol),' ; ',molname(indx(imol)),' ; ',molline(indx(imol)),' ;'
      afreq = molfreq(imol)
      aname = molname(indx(imol))
    else if (molname(indx(imol)).ne.aname) then
      nmol = nmol+1
      write(lun,'(F14.6,A,A,A,A,A)') molfreq(imol),' ; ',molname(indx(imol)),' ; ',molline(indx(imol)),' ;'
      afreq = molfreq(imol)
      aname = molname(indx(imol))
    endif
  enddo
  close(unit=lun)
  write(chain,'(A,I0,A,I0)') 'Selected and Sorted ',nmol,' lines out of ',MMOL
  call map_message(seve%i,rname,chain,2)
  call sic_frelun(lun)
  error = .false.
end subroutine catalog_filter
