!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubemain_sort
  use cubetools_parameters
  use cubetools_structure
  use cube_types
  use cubeadm_cubeid_types
  use cubeadm_cubeprod_types
  use cubemain_messaging
  use cubetopology_sperange_types
  !
  public :: sort 
  private
  !
  type :: sort_comm_t
     type(option_t),     pointer :: comm
     type(cubeid_arg_t), pointer :: cube
     type(sperange_opt_t)        :: range
     type(cube_prod_t),  pointer :: sorted
   contains
     procedure, public  :: register => cubemain_sort_register
     procedure, private :: parse    => cubemain_sort_parse
     procedure, private :: main     => cubemain_sort_main
  end type sort_comm_t
  type(sort_comm_t) :: sort
  !
  type sort_user_t 
     type(cubeid_user_t)   :: cubeids
     type(sperange_user_t) :: range
   contains
     procedure, private :: toprog => cubemain_sort_user_toprog
  end type sort_user_t
  type sort_prog_t
     type(sperange_prog_t) :: range  ! Range to be sorted
     type(cube_t), pointer :: cube   ! Input cube
     type(cube_t), pointer :: sorted ! Sorted cube
     integer(kind=chan_k)  :: fc,lc  ! First and last channels
   contains
     procedure, private :: header   => cubemain_sort_prog_header
     procedure, private :: data     => cubemain_sort_prog_data
     procedure, private :: loop     => cubemain_sort_prog_loop
     procedure, private :: spectrum => cubemain_sort_prog_spectrum
  end type sort_prog_t
  !
contains
  !
  subroutine cubemain_sort_command(line,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    character(len=*), intent(in)    :: line
    logical,          intent(inout) :: error
    !
    type(sort_user_t) :: user
    character(len=*), parameter :: rname='SORT>COMMAND'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    call sort%parse(line,user,error)
    if (error) return
    call sort%main(user,error)
    if (error) return
  end subroutine cubemain_sort_command
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_sort_register(sort,error)
    use cubedag_allflags
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(sort_comm_t), intent(inout) :: sort
    logical,            intent(inout) :: error
    !
    type(cubeid_arg_t) :: cubearg
    type(cube_prod_t) :: oucube
    character(len=*), parameter :: rname='SORT>REGISTER'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubetools_register_command(&
         'SORT','[cubeid]',&
         'Sort the intensities of each spectrum of a cube',&
         'SORT reorders channels in each spectrum by decreasing&
         & brightness around the peak channel.'//strg_bl//&
         'Behavior:'//strg_cr//&
         '- The command identifies the maximum-brightness channel in each spectrum.'//strg_cr//&
         '- It then sorts contiguous channels around that peak by decreasing brightness.'//strg_bl//&
         'Examples:'//strg_cr//&
         'CUBE> SORT mycube'//strg_cr//&
         'CUBE> SORT mycube /RANGE -20 20 km/s',&
         cubemain_sort_command,&
         sort%comm,error)
    if (error) return
    call cubearg%register(&
         'CUBEID',&
         'Signal cube identifier',&
         'If omitted, use the last matching cube (cube flag) in&
         & the default index (DAG by default; see SET\INDEX).',&
         code_arg_optional,&
         [flag_cube],&
         code_read,&
         code_access_speset,&
         sort%cube,&
         error)
    if (error) return
    call sort%range%register(&
         'RANGE',&
         'Define the velocity range over which to sort',&
         error)
    if (error) return
    !
    ! Product
    call oucube%register(&
         'SORTED',&
         'Sorted cube',&
         strg_emp,&
         [flag_sort],&
         sort%sorted,&
         error)
    if (error) return
  end subroutine cubemain_sort_register
  !
  subroutine cubemain_sort_parse(sort,line,user,error)
    !----------------------------------------------------------------------
    ! SORT [cubeid]
    ! /RANGE first last [unit]
    !----------------------------------------------------------------------
    class(sort_comm_t), intent(in)    :: sort
    character(len=*),   intent(in)    :: line
    type(sort_user_t),  intent(out)   :: user
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname='SORT>PARSE'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_parse(line,sort%comm,user%cubeids,error)
    if (error) return
    call sort%range%parse(line,user%range,error)
    if (error) return
  end subroutine cubemain_sort_parse
  !
  subroutine cubemain_sort_main(sort,user,error)
    use cubeadm_timing
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(sort_comm_t), intent(in)    :: sort
    type(sort_user_t),  intent(in)    :: user
    logical,            intent(inout) :: error
    !
    type(sort_prog_t) :: prog
    character(len=*), parameter :: rname='SORT>MAIN'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call user%toprog(prog,error)
    if (error) return
    call prog%header(sort,error)
    if (error) return
    call cubeadm_timing_prepro2process()
    call prog%data(error)
    if (error) return
    call cubeadm_timing_process2postpro()
  end subroutine cubemain_sort_main
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_sort_user_toprog(user,prog,error)
    use cubeadm_get
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(sort_user_t), intent(in)    :: user
    type(sort_prog_t),  intent(out)   :: prog
    logical,            intent(inout) :: error
    !
    integer(chan_k) :: stride
    character(len=*), parameter :: rname='SORT>MAIN'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_get_header(sort%cube,user%cubeids,prog%cube,error)
    if (error) return
    call user%range%toprog(prog%cube,code_sperange_truncated,prog%range,error)
    if (error) return
    call prog%range%to_chan_k(prog%fc,prog%lc,stride,error)
    if (error) return
  end subroutine cubemain_sort_user_toprog
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_sort_prog_header(prog,comm,error)
    use cubeadm_clone
    use cubetools_axis_types
    use cubetools_header_methods
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(sort_prog_t), intent(inout) :: prog
    type(sort_comm_t),  intent(in)    :: comm
    logical,            intent(inout) :: error
    !
    type(axis_t) :: axis
    character(len=*), parameter :: rname='SORT>PROG>HEADER'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_clone_header(comm%sorted,prog%cube,prog%sorted,error)
    if (error) return
    call cubetools_header_get_axis_head_c(prog%sorted%head,axis,error)
    if (error) return
    axis%name = 'Sorted '//trim(axis%name)
    axis%n = prog%lc-prog%fc+1
    axis%ref = 1d0
    axis%val = 0d0
    call cubetools_header_update_axset_c(axis,prog%sorted%head,error)
    if (error) return
    !
  end subroutine cubemain_sort_prog_header
  !
  subroutine cubemain_sort_prog_data(prog,error)
    use cubeadm_opened
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(sort_prog_t), target, intent(inout) :: prog
    logical,                    intent(inout) :: error
    !
    type(cubeadm_iterator_t) :: iter
    character(len=*), parameter :: rname='SORT>PROG>DATA'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_datainit_all(iter,error)
    if (error) return
    !$OMP PARALLEL DEFAULT(none) SHARED(prog,error) FIRSTPRIVATE(iter)
    !$OMP SINGLE
    do while (cubeadm_dataiterate_all(iter,error))
       if (error) exit
       !$OMP TASK SHARED(prog,error) FIRSTPRIVATE(iter)
       if (.not.error) &
            call prog%loop(iter,error)
       !$OMP END TASK
    enddo ! ie
    !$OMP END SINGLE
    !$OMP END PARALLEL
    !
  end subroutine cubemain_sort_prog_data
  !
  subroutine cubemain_sort_prog_loop(prog,iter,error)
    use cubeadm_taskloop
    use cubemain_spectrum_real
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(sort_prog_t), target, intent(inout) :: prog
    type(cubeadm_iterator_t),   intent(inout) :: iter
    logical,                    intent(inout) :: error
    !
    integer(kind=chan_k) :: nchan
    type(spectrum_t) :: spec,good,sorted
    character(len=*), parameter :: rname='SORT>PROG>LOOP'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    nchan = prog%lc-prog%fc+1
    call good%reallocate('good',nchan,iter,error)
    if (error) return
    call sorted%reallocate('sorted',nchan,iter,error)
    if (error) return
    call spec%reassociate_and_init(prog%cube,iter,error)
    if (error) return
    !
    do while (iter%iterate_entry(error))
       call prog%spectrum(iter%ie,spec,good,sorted,error)
       if (error) return
    enddo
  end subroutine cubemain_sort_prog_loop
  !
  subroutine cubemain_sort_prog_spectrum(prog,ie,spec,good,sorted,error)
    use cubemain_spectrum_real
    use cubemain_spectrum_blanking
    use cubemain_spectrum_moment_tool
    use cubemain_spectrum_operations
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(sort_prog_t), target, intent(inout) :: prog
    integer(kind=entr_k),       intent(in)    :: ie
    type(spectrum_t),           intent(inout) :: spec
    type(spectrum_t),           intent(inout) :: good
    type(spectrum_t),           intent(inout) :: sorted
    logical,                    intent(inout) :: error
    !
    type(spectrum_tpeak_t) :: tpeak
    type(spectrum_t) :: line
    character(len=*), parameter :: rname='SORT>PROG>SPECTRUM'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call spec%get(prog%cube,ie,error)
    if (error) return
    call line%point_to(spec,prog%fc,prog%lc,1.0,error)
    if (error) return
    call cubemain_spectrum_unblank(line,good,error)
    if (error) return
    if (good%n.gt.0) then
       call tpeak%compute(good,error)
       if (error) return
       call cubemain_spectrum_sort(tpeak%ic,good,sorted,error)
       if (error) return
       call sorted%put(prog%sorted,ie,error)
       if (error) return
    endif
  end subroutine cubemain_sort_prog_spectrum
end module cubemain_sort
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
