subroutine mrtindex_find_comm(line,ix,cx,error)
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_find_comm
  use mrtindex_parse_types
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ public
  !  Support routine for command:
  !   MFIND
  ! Read the index file
  !---------------------------------------------------------------------
  character(len=*),          intent(in)    :: line   ! Input command line
  type(mrtindex_optimize_t), intent(in)    :: ix
  type(mrtindex_optimize_t), intent(inout) :: cx
  logical,                   intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='FIND'
  character(len=message_length) :: mess
  type(user_calib_t) :: fuser
  integer(kind=entry_length) :: i,j
  character(len=11) :: date
  !
  if (ix%next.eq.1)  call mrtindex_message(seve%w,rname,'Input index is empty')
  !
  ! --- Fill CX from selection criteria --------------------------------
  call mrtindex_find_parse(line,fuser,error)
  if (error)  return
  !
  call mrtindex_find(fuser,ix,cx,error)
  if (error)  return
  !
  ! Check if there is no identical versions of the same entry. This
  ! works since we ensured that CX is sorted regarding those criterions
  do i=2,cx%next-1
    j = i-1
    if (cx%dobs(i)   .eq.cx%dobs(j)    .and.  &
        cx%scan(i)   .eq.cx%scan(j)    .and.  &
        cx%backend(i).eq.cx%backend(j) .and.  &
        cx%version(i).eq.cx%version(j)) then
      call gag_todate(cx%dobs(i),date,error)
      write(mess,'(A,I0,A,I0,A,A,A,I0,A,A,A,I0)')           &
        'Entry numbers ',j,' and ',i,                       &
        ' are identical: Date=',trim(date),                 &
        ', Scan=',cx%scan(i),                               &
        ', Backend=',trim(backends_mrtcal(cx%backend(i))),  &
        ', Version=',cx%version(i)
      call mrtindex_message(seve%e,rname,mess)
      error = .true.
      return
    endif
  enddo
  !
  ! *** JP Is it the right place?
  call mrtindex_cx2sic(cx,error)
  if (error) return
  !
  write(mess,'(I0,A)')  cx%next-1,' entries in Current indeX'
  call mrtindex_message(seve%i,rname,mess)
  !
end subroutine mrtindex_find_comm
!
subroutine mrtindex_find_parse(line,fuser,error)
  use imbfits_parameters
  use mrtindex_dependencies_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_find_parse
  use mrtindex_parse_types
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Parse the fuser input for selection criteria
  !---------------------------------------------------------------------
  character(len=*),   intent(in)    :: line   !
  type(user_calib_t), intent(out)   :: fuser   !
  logical,            intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='FIND>PARSE'
  integer(kind=4), parameter :: optdate=1  ! Match the order in 'mrtcal_load'
  integer(kind=4), parameter :: optscan=2
  integer(kind=4), parameter :: optback=3
  integer(kind=4), parameter :: optobst=4
  integer(kind=4), parameter :: optsour=5
  integer(kind=4), parameter :: optproj=6
  integer(kind=4), parameter :: optcomp=7
  integer(kind=4), parameter :: optcali=8
  integer(kind=4), parameter :: optsolv=9
  integer(kind=4), parameter :: optswit=10
  integer(kind=4), parameter :: optfron=11
  integer(kind=4), parameter :: optpola=12
  integer(kind=4) :: nchar,i1,iarg
  !
  ! /DATE [date|*]
  fuser%cdate = '*'
  if (sic_narg(optdate).ge.1) then
    ! Get the option arguments, and convert them to gag_dates on-the-fly
    ! (this conversion is much easier here because we still have the
    ! argument limits made by Sic).
    call sic_ke(line,optdate,1,fuser%cdate,nchar,.true.,error)
    if (error)  return
    if (fuser%cdate.ne.'*') then
      ! ZZZ There should be a Sic API which extracts the correct portion
      ! of the command line! (same in astro/lib/astro_uv.f90)
      call datelist_decode(line,optdate,fuser%cdate,error)
      if (error)  return
    endif
  endif
  !
  ! /SCAN [iscan|*]
  fuser%cscan = '*'
  i1 = 1
  do iarg=1,sic_narg(optscan)
    ! Translate the arguments (as string, because of "TO" in "12 TO 34") and
    ! save them in a string
    call sic_ke(line,optscan,iarg,fuser%cscan(i1:),nchar,.true.,error)
    if (error)  return
    i1 = i1+nchar+1
  enddo
  !
  ! /BACKEND [backname|*]
  fuser%cback = '*'
  i1 = 1
  do iarg=1,sic_narg(optback)
    ! Translate the arguments and save them in a string
    call sic_ke(line,optback,iarg,fuser%cback(i1:),nchar,.true.,error)
    if (error)  return
    i1 = i1+nchar+1
  enddo
  !
  ! /OBSTYPE [List|*]
  fuser%cobst = '*'
  i1 = 1
  do iarg=1,sic_narg(optobst)
    ! Translate the arguments and save them in a string
    call sic_ke(line,optobst,iarg,fuser%cobst(i1:),nchar,.true.,error)
    if (error)  return
    i1 = i1+nchar+1
  enddo
  !
  ! /SWITCHMODE [List|*]
  fuser%cswit = '*'
  i1 = 1
  do iarg=1,sic_narg(optswit)
    ! Translate the arguments and save them in a string
    call sic_ke(line,optswit,iarg,fuser%cswit(i1:),nchar,.true.,error)
    if (error)  return
    i1 = i1+nchar+1
  enddo
  !
  ! /POLARIMETRY [YES|NO|*]
  fuser%cpola = '*'
  call sic_ke(line,optpola,1,fuser%cpola,nchar,.false.,error)
  if (error)  return
  !
  ! /SOURCE [sourcename|*]
  fuser%csour = '*'
  call sic_ke(line,optsour,1,fuser%csour,nchar,.false.,error)
  if (error)  return
  !
  ! /PROJID [projectid|*]
  fuser%cproj = '*'
  call sic_ch(line,optproj,1,fuser%cproj,nchar,.false.,error)
  if (error)  return
  !
  ! /FRONTEND [List|* *&*]
  fuser%cfron = user_find_allfront
  i1 = 1
  do iarg=1,sic_narg(optfron)
    ! Translate the arguments and save them in a string
    call sic_ch(line,optfron,iarg,fuser%cfron(i1:),nchar,.true.,error)
    if (error)  return
    i1 = i1+nchar+1
  enddo
  call sic_upper(fuser%cfron)
  !
  ! /COMPLETE [UNREADABLE|READABLE|NO|YES|*]
  fuser%ccomp = '*'
  call sic_ke(line,optcomp,1,fuser%ccomp,nchar,.false.,error)
  if (error)  return
  !
  ! /CALIBRATED [List|*]
  fuser%ccali = '*'
  i1 = 1
  do iarg=1,sic_narg(optcali)
    ! Translate the arguments and save them in a string
    call sic_ke(line,optcali,iarg,fuser%ccali(i1:),nchar,.true.,error)
    if (error)  return
    i1 = i1+nchar+1
  enddo
  !
  ! /SOLVED [List|*]
  fuser%csolv = '*'
  i1 = 1
  do iarg=1,sic_narg(optsolv)
    ! Translate the arguments and save them in a string
    call sic_ke(line,optsolv,iarg,fuser%csolv(i1:),nchar,.true.,error)
    if (error)  return
    i1 = i1+nchar+1
  enddo
  !
end subroutine mrtindex_find_parse
!
subroutine mrtindex_find_criter(user,criter,error)
  use gbl_message
  use gkernel_interfaces
  use imbfits_parameters
  use mrtindex_interfaces, except_this=>mrtindex_find_criter
  use mrtindex_parse_types
  use mrtindex_types
  !---------------------------------------------------------------------
  ! @ private
  !  Convert the user inputs into selection criteria
  !---------------------------------------------------------------------
  type(user_calib_t),  intent(in)    :: user    !
  type(mrtindex_find_t), intent(out)   :: criter  !
  logical,             intent(inout) :: error   !
  ! Local
  character(len=*), parameter :: rname='FIND>CRITER'
  character(len=24) :: key
  character(len=obstype_length) :: myobst
  character(len=calstatus_length) :: mycali
  character(len=solstatus_length) :: mysolv
  character(len=backname_length) :: myback
  character(len=16) :: myfron
  character(len=3) :: myswit
  integer(kind=4) :: next,ipar,nc,ikey
  integer(kind=4), parameter :: mlist=10
  character(len=10) :: completeness(4)
  ! Data
  data completeness / 'UNREADABLE','READABLE','NO','YES' /
  !
  ! VERSION: not customizable
  criter%llast = .true.  ! Select last version only
  !
  ! /DATE [date|*]
  criter%ldate = user%cdate.ne.'*'
  if (criter%ldate) then
    call sic_parse_listi4(rname,user%cdate,criter%idate,mlist,error)
    if (error)  return
    if (any(criter%idate%i3(1:criter%idate%nlist).ne.1)) then
      call mrtindex_message(seve%e,rname,  &
        'Step other than 1 in /DATE list(s) not implemented')
      error = .true.
      return
    endif
  endif
  !
  ! /SCAN [iscan|*]
  criter%lscan = user%cscan.ne.'*'
  if (criter%lscan) then
    call sic_parse_listi4(rname,user%cscan,criter%iscan,mlist,error)
    if (error)  return
    if (any(criter%iscan%i3(1:criter%iscan%nlist).ne.1)) then
      call mrtindex_message(seve%e,rname,  &
        'Step other than 1 in /SCAN list(s) not implemented')
      error = .true.
      return
    endif
    ! Think twice if we support step lower or greater than 0
  endif
  !
  ! /BACKEND [backname|*]
  criter%lback = user%cback.ne.'*'
  if (criter%lback) then
    criter%iback(:) = -1  ! Means not set
    ipar = 0
    next = 1
    do
      call sic_next(user%cback(next:),myback,nc,next)
      if (nc.eq.0)  exit
      ipar = ipar+1
      call sic_ambigs(rname,myback,key,criter%iback(ipar),backends_mrtcal,  &
        nbackends_mrtcal+1,error)
      if (error)  return
      criter%iback(ipar) = criter%iback(ipar)-1  ! Because 'backends_mrtcal'
                                                 ! numbering starts at 0
      if (ipar.eq.nbackends_mrtcal)  exit
    enddo
  endif
  !
  ! /OBSTYPE [List|*]
  criter%lobst = user%cobst.ne.'*'
  if (criter%lobst) then
    criter%iobst(:) = -1  ! Means not set
    ipar = 0
    next = 1
    do
      call sic_next(user%cobst(next:),myobst,nc,next)
      if (nc.eq.0)  exit
      ipar = ipar+1
      call sic_ambigs(rname,myobst,key,criter%iobst(ipar),obstypes_mrtcal,  &
        nobstypes_mrtcal+1,error)
      if (error)  return
      criter%iobst(ipar) = criter%iobst(ipar)-1  ! Because 'obstypes_mrtcal'
                                                 ! numbering starts at 0
      if (ipar.eq.nobstypes_mrtcal)  exit
    enddo
  endif
  !
  ! /SWITCHMODE [List|*]
  criter%lswit = user%cswit.ne.'*'
  if (criter%lswit) then
    criter%iswit(:) = -1  ! Means not set
    ipar = 0
    next = 1
    do
      call sic_next(user%cswit(next:),myswit,nc,next)
      if (nc.eq.0)  exit
      ipar = ipar+1
      call sic_ambigs(rname,myswit,key,criter%iswit(ipar),switchmodes_mrtcal,  &
        nswitchmodes+1,error)
      if (error)  return
      criter%iswit(ipar) = criter%iswit(ipar)-1  ! Because 'switchmodes_mrtcal'
                                                 ! numbering starts at 0
      if (ipar.eq.nswitchmodes)  exit
    enddo
  endif
  !
  ! /POLARIMETRY [YES|NO|*]
  criter%lpola = user%cpola.ne.'*'
  if (criter%lpola) then
    call sic_ambigs(rname,user%cpola,key,ikey,polstatus,npolstatus,error)
    if (error)  return
    criter%ipola = ikey-1  ! Because 'polstatus' numbering starts at 0
  endif
  !
  ! /SOURCE [sourcename|*]
  criter%lsour = user%csour.ne.'*'
  if (criter%lsour) then
    criter%csour = user%csour
  endif
  !
  ! /PROJID [pojectid|*]
  criter%lproj = user%cproj.ne.'*'
  if (criter%lproj) then
    criter%cproj = user%cproj
  endif
  !
  ! /FRONTEND [recname|*]
  criter%lfron = user%cfron.ne.user_find_allfront
  if (criter%lfron) then
    criter%cfron(:) = ''  ! Means not set
    ipar = 0
    next = 1
    do
      call sic_next(user%cfron(next:),myfron,nc,next)
      if (nc.eq.0)  exit
      ipar = ipar+1
      criter%cfron(ipar) = myfron
      if (ipar.eq.mrtcal_indx_mfrontend)  exit
    enddo
  endif
  !
  ! /COMPLETE [UNREADABLE|READABLE|NO|YES|*]
  criter%lcomp = user%ccomp.ne.'*'
  if (criter%lcomp) then
    call sic_ambigs(rname,user%ccomp,key,ikey,completeness,4,error)
    if (error)  return
    !  UNREADABLE: complete_unreadable + complete_empty
    !  READABLE:   complete_partial + complete_full
    !  NO:         complete_unreadable + complete_empty + complete_partial
    !  YES:        complete_full
    ! Let's encode this as bits
    criter%icomp = 0  ! Fill with zeroes
    if (ikey.eq.1) then
      criter%icomp = ibset(criter%icomp,complete_unreadable)
      criter%icomp = ibset(criter%icomp,complete_empty)
    elseif (ikey.eq.2) then
      criter%icomp = ibset(criter%icomp,complete_partial)
      criter%icomp = ibset(criter%icomp,complete_full)
    elseif (ikey.eq.3) then
      criter%icomp = ibset(criter%icomp,complete_unreadable)
      criter%icomp = ibset(criter%icomp,complete_empty)
      criter%icomp = ibset(criter%icomp,complete_partial)
    elseif (ikey.eq.4) then
      criter%icomp = ibset(criter%icomp,complete_full)
    endif
  endif
  !
  ! /CALIBRATED [List|*]
  criter%lcali = user%ccali.ne.'*'
  if (criter%lcali) then
    criter%icali(:) = -1  ! Means not set
    ipar = 0
    next = 1
    do
      call sic_next(user%ccali(next:),mycali,nc,next)
      if (nc.eq.0)  exit
      ipar = ipar+1
      call sic_ambigs(rname,mycali,key,criter%icali(ipar),calstatus,  &
        ncalstatus,error)
      if (error)  return
      if (ipar.eq.ncalstatus)  exit
    enddo
  endif
  !
  ! /SOLVED [List|*]
  criter%lsolv = user%csolv.ne.'*'
  if (criter%lsolv) then
    criter%isolv(:) = -1  ! Means not set
    ipar = 0
    next = 1
    do
      call sic_next(user%csolv(next:),mysolv,nc,next)
      if (nc.eq.0)  exit
      ipar = ipar+1
      call sic_ambigs(rname,mysolv,key,criter%isolv(ipar),solstatus,  &
        nsolstatus,error)
      if (error)  return
      if (ipar.eq.nsolstatus)  exit
    enddo
  endif
  !
end subroutine mrtindex_find_criter
!
subroutine mrtindex_find(fuser,in,out,error)
  use gbl_message
  use gkernel_interfaces
  use mrtindex_interfaces, except_this=>mrtindex_find
  use mrtindex_types
  use mrtindex_parse_types
  !---------------------------------------------------------------------
  ! @ public
  !  Fill OUT index from IN index according to the user inputs
  !---------------------------------------------------------------------
  type(user_calib_t),      intent(in)    :: fuser   !
  type(mrtindex_optimize_t), intent(in)    :: in     !
  type(mrtindex_optimize_t), intent(inout) :: out    !
  logical,                 intent(inout) :: error  !
  ! Local
  character(len=*), parameter :: rname='FIND'
  integer(kind=entry_length) :: ient,jent,nfound,ielem
  type(mrtindex_find_t) :: criter
  integer(kind=4) :: list(in%next-1) ! Automatic array (assume in%next>1)
  integer(kind=4) :: is
  logical :: found
  !
  call mrtindex_find_criter(fuser,criter,error)
  if (error)  return
  !
  nfound = 0
  do jent=1,in%next-1
    ient = in%sort(jent)
    if (criter%llast) then
      if (.not.in%islast(ient))  cycle
    endif
    if (criter%lsour) then
      if (.not.match_string(in%source(ient),criter%csour)) cycle
    endif
    if (criter%lproj) then
      if (.not.match_string(in%projid(ient),criter%cproj)) cycle
    endif
    if (criter%lcomp) then
      if (.not.btest(criter%icomp,in%filstatus(ient)))  cycle
    endif
    if (criter%lpola) then
      if (in%polstatus(ient).ne.criter%ipola)  cycle
    endif
    ! /BACKEND: more complicated, comes after
    if (criter%lback) then
      found = .false.
      do is=1,nbackends_mrtcal
        if (criter%iback(is).lt.0)  exit  ! No more backends set
        if (in%backend(ient).eq.criter%iback(is)) then
          found = .true.
          exit
        endif
      enddo
      if (.not.found)  cycle
    endif
    ! /FRONTEND: less efficient, comes after
    if (criter%lfron) then
      if (.not.mrtindex_find_byfrontend(in%frontend(:,ient),criter%cfron))  cycle
      if (error)  return
    endif
    ! /OBSTYPE: more complicated, comes after
    if (criter%lobst) then
      found = .false.
      do is=1,nobstypes_mrtcal
        if (criter%iobst(is).lt.0)  exit  ! No more obstypes set
        if (in%obstype(ient).eq.criter%iobst(is)) then
          found = .true.
          exit
        endif
      enddo
      if (.not.found)  cycle
    endif
    ! /CALIBRATED: same
    if (criter%lcali) then
      found = .false.
      do is=1,ncalstatus
        if (criter%icali(is).lt.0)  exit  ! No more calibration statuses set
        if (in%calstatus(ient).eq.criter%icali(is)) then
          found = .true.
          exit
        endif
      enddo
      if (.not.found)  cycle
    endif
    ! /SOLVED: same
    if (criter%lsolv) then
      found = .false.
      do is=1,nsolstatus
        if (criter%isolv(is).lt.0)  exit  ! No more calibration statuses set
        if (in%solstatus(ient).eq.criter%isolv(is)) then
          found = .true.
          exit
        endif
      enddo
      if (.not.found)  cycle
    endif
    ! /SWITCHMODE: same
    if (criter%lswit) then
      found = .false.
      do is=1,nswitchmodes
        if (criter%iswit(is).lt.0)  exit  ! No more switchmodes set
        if (in%switchmode(ient).eq.criter%iswit(is)) then
          found = .true.
          exit
        endif
      enddo
      if (.not.found)  cycle
    endif
    ! /DATE: more complicated, comes last
    if (criter%ldate) then
      found = .false.
      do is=1,criter%idate%nlist
        if (in%dobs(ient).ge.criter%idate%i1(is) .and.  &
            in%dobs(ient).le.criter%idate%i2(is)) then
          found = .true.
          exit
        endif
      enddo
      if (.not.found)  cycle
    endif
    ! /SCAN: more complicated, comes last
    if (criter%lscan) then
      found = .false.
      do is=1,criter%iscan%nlist
        if (in%scan(ient).ge.criter%iscan%i1(is) .and.  &
            in%scan(ient).le.criter%iscan%i2(is)) then
          found = .true.
          exit
        endif
      enddo
      if (.not.found)  cycle
    endif
    !
    nfound = nfound+1
    list(nfound) = ient
  enddo
  !
  call reallocate_mrtoptimize(out,nfound,.false.,error)
  if (error)  return
  !
  out%next = 1
  do ient=1,nfound
    ielem = list(ient)
    call mrtindex_optimize_to_optimize(in,ielem,out,error)
    if (error)  exit
  enddo
  !
contains
  !
  function mrtindex_find_byfrontend(frontend,pattern) result(found)
    !-------------------------------------------------------------------
    ! Return true if the frontend list matches the list of patterns.
    ! See HELP MFIND /FRONTEND for details
    !-------------------------------------------------------------------
    logical :: found
    character(len=*), intent(in) :: frontend(:)
    character(len=*), intent(in) :: pattern(:)
    !
    integer(kind=4) :: ipattern,iampe,ipipe
    !
    found = .false.
    do ipattern=1,size(pattern)
      ! 3 main cases:
      ! - single band e.g. E090
      ! - dual band   e.g. E090&E230
      ! - dual band   e.g. E090|E230
      iampe = index(pattern(ipattern),'&')
      ipipe = index(pattern(ipattern),'|')
      if (iampe.gt.0) then
        found = mrtindex_find_byfrontend_dual(frontend,  &
                                              pattern(ipattern)(1:iampe-1),  &
                                              pattern(ipattern)(iampe+1:),  &
                                              .true.)
      elseif (ipipe.gt.0) then
        found = mrtindex_find_byfrontend_dual(frontend,  &
                                              pattern(ipattern)(1:ipipe-1),  &
                                              pattern(ipattern)(ipipe+1:),  &
                                              .false.)
      else
        found = mrtindex_find_byfrontend_single(frontend,  &
                                                pattern(ipattern))
      endif
      if (error)  return
      if (found)  return
    enddo
  end function mrtindex_find_byfrontend
  !
  function mrtindex_find_byfrontend_dual(frontend,pattern1,pattern2,  &
    land) result(found)
    !-------------------------------------------------------------------
    ! Return true if the frontend list is dual band and matches the 2
    ! pattern.
    !-------------------------------------------------------------------
    logical :: found
    character(len=*), intent(in) :: frontend(:)
    character(len=*), intent(in) :: pattern1
    character(len=*), intent(in) :: pattern2
    logical,          intent(in) :: land        ! Logical and, or logical or?
    !
    integer(kind=4) :: ifront,nfront
    logical :: found1,found2
    !
    nfront = 0
    found = .false.
    found1 = .false.
    found2 = .false.
    do ifront=1,size(frontend)
      if (frontend(ifront).eq.'')  exit
      nfront = nfront+1
      if (match_string(frontend(ifront),pattern1))  found1 = .true.
      if (match_string(frontend(ifront),pattern2))  found2 = .true.
    enddo
    if (nfront.ne.2) then
      found = .false.
    elseif (land) then  ! Logical and
      found = found1.and.found2
    else  ! Logical or
      found = found1.or.found2
    endif
  end function mrtindex_find_byfrontend_dual
  !
  function mrtindex_find_byfrontend_single(frontend,pattern) result(found)
    !-------------------------------------------------------------------
    ! Return true if the frontend list is single band and matches the
    ! pattern.
    !-------------------------------------------------------------------
    logical :: found
    character(len=*), intent(in) :: frontend(:)
    character(len=*), intent(in) :: pattern
    !
    integer(kind=4) :: ifront,nfront
    !
    nfront = 0
    found = .false.
    do ifront=1,size(frontend)
      if (frontend(ifront).eq.'')  exit
      nfront = nfront+1
      if (match_string(frontend(ifront),pattern))  found = .true.
    enddo
    if (nfront.ne.1)  found = .false.
  end function mrtindex_find_byfrontend_single
  !
end subroutine mrtindex_find
