!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubemain_snr
  use cubetools_parameters
  use cube_types
  use cubetools_structure
  use cubeadm_cubeid_types
  use cubeadm_cubeprod_types
  use cubemain_messaging
  !
  public :: snr
  private
  !
  type :: snr_comm_t
     type(option_t), pointer :: comm
     type(cubeid_arg_t), pointer :: sig
     type(cubeid_arg_t), pointer :: noi
     type(cube_prod_t),  pointer :: snr
   contains
     procedure, public  :: register => cubemain_snr_comm_register
     procedure, private :: parse    => cubemain_snr_comm_parse
     procedure, private :: main     => cubemain_snr_comm_main
  end type snr_comm_t
  type(snr_comm_t) :: snr
  !
  type snr_user_t
     type(cubeid_user_t) :: cubeids
   contains
     procedure, private :: toprog => cubemain_snr_user_toprog
  end type snr_user_t
  !
  type snr_prog_t
     type(cube_t), pointer :: sig
     type(cube_t), pointer :: noi
     type(cube_t), pointer :: snr
     procedure(cubemain_snr_prog_singlenoise_loop), pointer :: loop => null()
   contains
     procedure, private :: header          => cubemain_snr_prog_header
     procedure, private :: data            => cubemain_snr_prog_data
     procedure, private :: singlenoise_act => cubemain_snr_prog_singlenoise_act
     procedure, private :: multinoise_act  => cubemain_snr_prog_multinoise_act
  end type snr_prog_t
  !
contains
  !
  subroutine cubemain_snr_command(line,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    character(len=*), intent(in)    :: line
    logical,          intent(inout) :: error
    !
    type(snr_user_t) :: user
    character(len=*), parameter :: rname='SNR>COMMAND'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call snr%parse(line,user,error)
    if (error) return
    call snr%main(user,error)
    if (error) continue
  end subroutine cubemain_snr_command
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_snr_comm_register(comm,error)
    use cubedag_allflags
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(snr_comm_t), intent(inout) :: comm
    logical,           intent(inout) :: error
    !
    type(cubeid_arg_t) :: incube
    type(cube_prod_t) :: oucube
    character(len=*), parameter :: comm_abstract='Compute the signal to noise ratio'
    character(len=*), parameter :: comm_help=strg_id
    character(len=*), parameter :: rname='SNR>COMM>REGISTER'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    ! Syntax
    call cubetools_register_command(&
         'SNR','[signal [noise]]',&
         comm_abstract,&
         comm_help,&
         cubemain_snr_command,&
         comm%comm,&
         error)
    if (error) return
    call incube%register( &
         'SIGNAL', &
         'Signal cubeid',  &
         strg_id,&
         code_arg_optional,  &
         [flag_cube], & 
         code_read,&
         code_access_speset,&
         comm%sig,&
        error)
    if (error) return
    call incube%register( &
         'NOISE', &
         'Noise cubeid', &
         strg_id,&
         code_arg_optional, &
         [flag_noise], &
         code_read,&
         code_access_speset,&
         comm%noi,&
         error)
    if (error) return
    !
    ! Product
    call oucube%register(&
         'SNR',&
         'Cube of Signal-to-Noise Ratios',&
         strg_id,&
         [flag_snr],&
         comm%snr,&
         error)
    if (error) return
  end subroutine cubemain_snr_comm_register
  !
  subroutine cubemain_snr_comm_parse(comm,line,user,error)
    !----------------------------------------------------------------------
    ! SNR signalid noiseid
    !----------------------------------------------------------------------
    class(snr_comm_t), intent(in)    :: comm
    character(len=*),  intent(in)    :: line
    type(snr_user_t),  intent(out)   :: user
    logical,           intent(inout) :: error
    !
    character(len=*), parameter :: rname='SNR>COMM>PARSE'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_parse(line,comm%comm,user%cubeids,error)
    if (error) return
  end subroutine cubemain_snr_comm_parse
  !
  subroutine cubemain_snr_comm_main(comm,user,error)
    use cubeadm_timing
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(snr_comm_t), intent(in)    :: comm
    type(snr_user_t),  intent(in)    :: user
    logical,           intent(inout) :: error
    !
    type(snr_prog_t) :: prog
    character(len=*), parameter :: rname='SNR>COMM>MAIN'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call user%toprog(comm,prog,error)
    if (error) return
    call prog%header(comm,error)
    if (error) return
    call cubeadm_timing_prepro2process()
    call prog%data(error)
    if (error) return
    call cubeadm_timing_process2postpro()
  end subroutine cubemain_snr_comm_main
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_snr_user_toprog(user,comm,prog,error)
    use cubetools_consistency_methods
    use cubetools_header_methods
    use cubeadm_get
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(snr_user_t), intent(in)    :: user
    type(snr_comm_t),  intent(in)    :: comm
    type(snr_prog_t),  intent(out)   :: prog
    logical,           intent(inout) :: error
    !
    logical :: conspb
    integer(kind=chan_k) :: nnoi
    character(len=*), parameter :: rname='SNR>USER>TOPROG'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !    
    call cubeadm_get_header(comm%sig,user%cubeids,prog%sig,error)
    if (error) return
    call cubeadm_get_header(comm%noi,user%cubeids,prog%noi,error)
    if (error) return
    !
    conspb = .false.
    call cubetools_consistency_signal_noise('Input cube',prog%sig%head,'Noise',prog%noi%head,conspb,error)
    if (error) return
    if (cubetools_consistency_failed(rname,conspb,error)) return
    !
    call cubetools_header_get_nchan(prog%noi%head,nnoi,error)
    if (error) return
    if (nnoi.eq.1) then
       prog%loop => cubemain_snr_prog_singlenoise_loop
    else
       prog%loop => cubemain_snr_prog_multinoise_loop
    endif
  end subroutine cubemain_snr_user_toprog
  !
  !----------------------------------------------------------------------
  !
  subroutine cubemain_snr_prog_header(prog,comm,error)
    use cubetools_header_methods
    use cubeadm_clone
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(snr_prog_t), intent(inout) :: prog
    class(snr_comm_t), intent(in)    :: comm
    logical,           intent(inout) :: error
    !
    character(len=*), parameter :: rname='SNR>PROG>HEADER'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubeadm_clone_header(comm%snr,prog%sig,prog%snr,error)
    if (error) return
    call cubetools_header_put_array_unit('---',prog%snr%head,error)
    if (error) return
  end subroutine cubemain_snr_prog_header
  !
  subroutine cubemain_snr_prog_data(prog,error)
    use cubeadm_opened
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(snr_prog_t), intent(inout) :: prog
    logical,           intent(inout) :: error
    !
    character(len=*), parameter :: rname='SNR>PROG>DATA'
    type(cubeadm_iterator_t) :: iter
    !
    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) then
          call prog%loop(iter,error)
       endif
       !$OMP END TASK
    enddo ! iter
    !$OMP END SINGLE
    !$OMP END PARALLEL
  end subroutine cubemain_snr_prog_data
  !
  subroutine cubemain_snr_prog_singlenoise_loop(prog,iter,error)
    use cubeadm_taskloop
    use cubeadm_spectrum_types
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(snr_prog_t),        intent(inout) :: prog
    type(cubeadm_iterator_t), intent(inout) :: iter
    logical,                  intent(inout) :: error
    !
    type(spectrum_t) :: sig,noi,snr
    character(len=*), parameter :: rname='SNR>PROG>SINGLENOISE>LOOP'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    ! 
    call sig%associate('sig',prog%sig,iter,error)
    if (error) return
    call noi%associate('noi',prog%noi,iter,error)
    if (error) return
    call snr%allocate('snr',prog%snr,iter,error)
    if (error) return
    !
    do while (iter%iterate_entry(error))
      call prog%singlenoise_act(iter%ie,sig,noi,snr,error)
      if (error) return
    enddo ! iter
  end subroutine cubemain_snr_prog_singlenoise_loop
  !
  subroutine cubemain_snr_prog_singlenoise_act(prog,ie,sig,noi,snr,error)
    use cubetools_nan
    use cubeadm_spectrum_types
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(snr_prog_t),    intent(inout) :: prog
    integer(kind=entr_k), intent(in)    :: ie
    type(spectrum_t),     intent(inout) :: sig
    type(spectrum_t),     intent(inout) :: noi
    type(spectrum_t),     intent(inout) :: snr
    logical,              intent(inout) :: error
    !
    integer(kind=chan_k) :: ic
    real(kind=sign_k), pointer :: noise
    character(len=*), parameter :: rname='SNR>PROG>SINGLENOISE>ACT'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call sig%get(ie,error)
    if (error) return
    call noi%get(ie,error)
    if (error) return
    noise => noi%y%val(1)
    if ((noise.gt.0).and.(.not.ieee_is_nan(noise))) then
       do ic=1,snr%n
          if (.not.ieee_is_nan(sig%y%val(ic))) then
             snr%y%val(ic) = sig%y%val(ic)/noise
          endif
       enddo ! ic
    else
       do ic=1,snr%n
          snr%y%val(ic) = gr4nan
       enddo ! ic             
    endif
    call snr%put(ie,error)
    if (error) return
  end subroutine cubemain_snr_prog_singlenoise_act
  !
  subroutine cubemain_snr_prog_multinoise_loop(prog,iter,error)
    use cubeadm_taskloop
    use cubeadm_spectrum_types
    use cubemain_interpolate_spectrum_tool
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(snr_prog_t),        intent(inout) :: prog
    type(cubeadm_iterator_t), intent(inout) :: iter
    logical,                  intent(inout) :: error
    !
    type(spectrum_t) :: sig,rawnoi,intnoi,snr
    type(interpolate_spectrum_prog_t) :: interp
    character(len=*), parameter :: rname='SNR>PROG>MULTINOISE>LOOP'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    ! 
    call sig%associate('sig',prog%sig,iter,error)
    if (error) return
    call intnoi%allocate('intnoi',prog%sig,iter,error)
    if (error) return
    call rawnoi%associate('rawnoi',prog%noi,iter,error)
    if (error) return
    call snr%allocate('snr',prog%snr,iter,error)
    if (error) return
    call interp%init(rawnoi,intnoi,error)
    if (error) return
    !
    do while (iter%iterate_entry(error))
      call prog%multinoise_act(iter%ie,sig,rawnoi,intnoi,snr,interp,error)
      if (error) return
    enddo ! iter
  end subroutine cubemain_snr_prog_multinoise_loop
  !
  subroutine cubemain_snr_prog_multinoise_act(prog,ie,sig,rawnoi,intnoi,snr,interp,error)
    use cubetools_nan
    use cubeadm_spectrum_types
    use cubemain_interpolate_spectrum_tool
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(snr_prog_t),                 intent(inout) :: prog
    integer(kind=entr_k),              intent(in)    :: ie
    type(spectrum_t),                  intent(inout) :: sig
    type(spectrum_t),                  intent(inout) :: rawnoi
    type(spectrum_t),                  intent(inout) :: intnoi
    type(spectrum_t),                  intent(inout) :: snr
    type(interpolate_spectrum_prog_t), intent(in)    :: interp
    logical,                           intent(inout) :: error
    !
    integer(kind=chan_k) :: ic
    real(kind=sign_k), pointer :: noise
    character(len=*), parameter :: rname='SNR>PROG>MULTINOISE>ACT'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call sig%get(ie,error)
    if (error) return
    call rawnoi%get(ie,error)
    if (error) return
    if (rawnoi%y%isblanked()) then
       ! Nothing to do
       snr%y%val(:) = gr4nan
    else
       call interp%spectrum(rawnoi,intnoi,error)
       if (error) return
       do ic=1,snr%n
          noise => intnoi%y%val(ic) 
          if ((noise.gt.0).and.(.not.ieee_is_nan(noise))) then
             if (.not.ieee_is_nan(sig%y%val(ic))) then
                ! *** JP: The fact that only positive or both
                ! *** JP: positive and negative values should be considered
                ! *** JP: signal should be a user-domain decision
                snr%y%val(ic) = sig%y%val(ic)/noise
             endif
          endif
       enddo ! ic
    endif
    call snr%put(ie,error)
    if (error) return
  end subroutine cubemain_snr_prog_multinoise_act
end module cubemain_snr
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
