!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubemain_baseline_wavelet
  use cubetools_structure
  use cubemain_messaging
  use cubemain_baseline_cubes_types
  !
  public :: baseline_wavelet_comm_t,baseline_wavelet_user_t
  private
  !
  type baseline_wavelet_comm_t
     type(option_t),              pointer :: key
     type(baseline_cubes_comm_t), pointer :: cubes => null()
   contains
     procedure, public :: register     => cubemain_baseline_wavelet_comm_register
     procedure, public :: parse_key    => cubemain_baseline_wavelet_comm_parse_key
     procedure, public :: parse_others => cubemain_baseline_wavelet_comm_parse_others
     procedure, public :: main         => cubemain_baseline_wavelet_comm_main
  end type baseline_wavelet_comm_t
  !
  type baseline_wavelet_user_t
     logical               :: present = .false.
     character(len=argu_l) :: order = strg_star
     type(baseline_cubes_user_t) :: cubes
   contains
     procedure, public :: toprog => cubemain_baseline_wavelet_user_toprog
     procedure, public :: list   => cubemain_baseline_wavelet_user_list
  end type baseline_wavelet_user_t
  !
  type baseline_wavelet_prog_t
     integer(kind=inte_k)        :: order = 0 ! [---] Wavelet order
     type(baseline_cubes_prog_t) :: cubes     ! Input and output cubes

   contains
     procedure :: list   => cubemain_baseline_wavelet_prog_list
     procedure :: header => cubemain_baseline_wavelet_prog_header
     procedure :: data   => cubemain_baseline_wavelet_prog_data
     procedure :: loop   => cubemain_baseline_wavelet_prog_loop
     procedure :: act    => cubemain_baseline_wavelet_prog_act
  end type baseline_wavelet_prog_t
  !
contains
  !
  subroutine cubemain_baseline_wavelet_comm_register(comm,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_wavelet_comm_t), intent(inout) :: comm
    logical,                        intent(inout) :: error
    !
    type(standard_arg_t) :: stdarg
    character(len=*), parameter :: rname='BASELINE>WAVELET>REGISTER'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubetools_register_option(&
         'WAVELET','order',&
         'Use a wavelet filter to define the baseline',&
         strg_id,&
         comm%key,error)
    if (error) return
    call stdarg%register(&
         'ORDER',&
         'Wavelet order',&
         strg_id,&
         code_arg_mandatory,&
         error)
    if (error) return
  end subroutine cubemain_baseline_wavelet_comm_register
  !
  subroutine cubemain_baseline_wavelet_comm_parse_key(comm,line,user,error)
    use cubetools_structure
    !----------------------------------------------------------------------
    ! /WAVELET order
    !----------------------------------------------------------------------
    class(baseline_wavelet_comm_t), intent(in)    :: comm
    character(len=*),               intent(in)    :: line
    type(baseline_wavelet_user_t),  intent(out)   :: user
    logical,                        intent(inout) :: error
    !
    integer(kind=argu_k), parameter :: iorder=1
    character(len=*), parameter :: rname='BASELINE>WAVELET>COMM>PARSE>KEY'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call comm%key%present(line,user%present,error)
    if (error) return
    if (user%present) then
       call cubetools_getarg(line,comm%key,iorder,user%order,mandatory,error)
       if (error) return
    endif
  end subroutine cubemain_baseline_wavelet_comm_parse_key
  !
  subroutine cubemain_baseline_wavelet_comm_parse_others(comm,cubes,line,user,error)
    !----------------------------------------------------------------------
    ! Parse the others useful keys of the commands in this case
    !----------------------------------------------------------------------
    class(baseline_wavelet_comm_t),       intent(inout) :: comm
    type(baseline_cubes_comm_t),  target, intent(inout) :: cubes
    character(len=*),                     intent(in)    :: line
    type(baseline_wavelet_user_t),        intent(inout) :: user
    logical,                              intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>WAVELET>COMM>PARSE>OTHERS'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    comm%cubes => cubes
    call comm%cubes%parse(line,user%cubes,error)
    if (error) return
  end subroutine cubemain_baseline_wavelet_comm_parse_others
  !
  subroutine cubemain_baseline_wavelet_comm_main(comm,user,error) 
    use cubeadm_timing
    !----------------------------------------------------------------------
    ! Parsing call is done in command_baseline.f90. Reminder calls are done
    ! here!
    !----------------------------------------------------------------------
    class(baseline_wavelet_comm_t), intent(in)    :: comm
    type(baseline_wavelet_user_t),  intent(inout) :: user
    logical,                        intent(inout) :: error
    !
    type(baseline_wavelet_prog_t) :: prog
    character(len=*), parameter :: rname='BASELINE>WAVELET>COMM>MAIN'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call user%toprog(comm,prog,error)
    if (error) return
    call prog%list(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_baseline_wavelet_comm_main
  !
  !------------------------------------------------------------------------
  !
  subroutine cubemain_baseline_wavelet_user_toprog(user,comm,prog,error)
    use cubetools_user2prog
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(baseline_wavelet_user_t), intent(inout) :: user
    type(baseline_wavelet_comm_t),  intent(in)    :: comm
    type(baseline_wavelet_prog_t),  intent(inout) :: prog
    logical,                        intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>WAVELET>USER>TOPROG'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    ! Get input and output cubes
    call user%cubes%toprog(comm%cubes,prog%cubes,error)
    if (error) return
    !
    ! Get wavelet order
    call cubetools_user2prog_resolve_unk(user%order,prog%order,error)
    if (error) return
    if (prog%order.lt.0) then
       ! ***JP: Unclear to me that wavelet will work with order.eq.0...
       call cubemain_message(seve%e,rname,'Order must be positive')
       error = .true.
       return
    endif
  end subroutine cubemain_baseline_wavelet_user_toprog
  !
  subroutine cubemain_baseline_wavelet_user_list(user,error)
    !----------------------------------------------------------------------
    ! Mostly for debugging purpose
    !----------------------------------------------------------------------
    class(baseline_wavelet_user_t), intent(in)    :: user 
    logical,                       intent(inout) :: error
    !
    character(len=mess_l) :: mess
    character(len=*), parameter :: rname='BASELINE>WAVELET>USER>LIST'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    ! *** JP: To be written
    call cubemain_message(seve%r,rname,mess)
  end subroutine cubemain_baseline_wavelet_user_list
  !
  !------------------------------------------------------------------------
  !
  subroutine cubemain_baseline_wavelet_prog_list(prog,error)
    use cubetools_format
    !-------------------------------------------------------------------
    ! List the baseline_wavelet information in a user friendly way
    !-------------------------------------------------------------------
    class(baseline_wavelet_prog_t), intent(in)    :: prog
    logical,                        intent(inout) :: error
    !
    character(len=mess_l) :: mess
    character(len=*), parameter :: rname='BASELINE>WAVELET>PROG>LIST'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call cubemain_message(seve%r,rname,'')
    mess = cubetools_format_stdkey_boldval('Baselining method','Wavelet Filtering',40)
    call cubemain_message(seve%r,rname,mess)
    write(mess,'(a,i0,a)') 'Subtracting wavelet orders from 0 to ',prog%order,' from the spectra'
    call cubemain_message(seve%r,rname,mess)
  end subroutine cubemain_baseline_wavelet_prog_list
  !
  subroutine cubemain_baseline_wavelet_prog_header(prog,comm,error)
    use cube_types
    use cubetools_axis_types
    use cubetools_header_methods
    !-------------------------------------------------------------------
    ! Update cube headers
    !-------------------------------------------------------------------
    class(baseline_wavelet_prog_t), intent(inout) :: prog
    type(baseline_wavelet_comm_t),  intent(in)    :: comm
    logical,                        intent(inout) :: error
    !
    character(len=*), parameter :: rname='BASELINE>WAVELET>PROG>HEADER'
    !
    call cubemain_message(mainseve%trace,rname,'Welcome')
    !
    call prog%cubes%header(comm%cubes,error)
    if (error) return
  end subroutine cubemain_baseline_wavelet_prog_header
  !
  subroutine cubemain_baseline_wavelet_prog_data(prog,error)
    use cubeadm_opened
    !----------------------------------------------------------------------
    ! 
    !----------------------------------------------------------------------
    class(baseline_wavelet_prog_t), intent(inout) :: prog
    logical,                        intent(inout) :: error
    !
    type(cubeadm_iterator_t) :: iter
    character(len=*), parameter :: rname='BASELINE>WAVELET>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 ! iter
    !$OMP END SINGLE
    !$OMP END PARALLEL
  end subroutine cubemain_baseline_wavelet_prog_data
  !
  subroutine cubemain_baseline_wavelet_prog_loop(prog,iter,error)
    use cubeadm_taskloop
    use cubeadm_spectrum_types
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_wavelet_prog_t), intent(inout) :: prog
    type(cubeadm_iterator_t),       intent(inout) :: iter
    logical,                        intent(inout) :: error
    !
    type(spectrum_t) :: input,line,base
    character(len=*), parameter :: rname='BASELINE>WAVELET>PROG>LOOP'
    !
    call input%associate('input',prog%cubes%cube,iter,error)
    if (error) return
    call line%allocate('line',prog%cubes%line,iter,error)
    if (error) return
    call base%allocate('base',prog%cubes%base,iter,error)
    if (error) return
    !
    do while (iter%iterate_entry(error))
      call prog%act(iter%ie,input,base,line,error)
      if (error) return
    enddo
  end subroutine cubemain_baseline_wavelet_prog_loop
  !
  subroutine cubemain_baseline_wavelet_prog_act(prog,ie,input,base,line,error)
    use gkernel_interfaces
    use cubetools_nan
    use cubeadm_spectrum_types
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(baseline_wavelet_prog_t), intent(inout) :: prog
    integer(kind=entr_k),           intent(in)    :: ie
    type(spectrum_t),               intent(inout) :: input
    type(spectrum_t),               intent(inout) :: base
    type(spectrum_t),               intent(inout) :: line
    logical,                        intent(inout) :: error
    !
    real(kind=sign_k), allocatable :: wavelets(:,:)
    character(len=*), parameter :: rname='BASELINE>WAVELET>PROG>ACT'
    !
    call input%get(ie,error)
    if (error) return
    !
    if (input%y%isblanked()) then
       ! Nothing to do
       base%y%val(:) = gr4nan
       line%y%val(:) = gr4nan
    else if (input%y%hasblank()) then ! Expansive call when the input array is completely valid!
       ! Problematic case
       call cubemain_message(seve%e,rname,'Some NaN intensities in input spectrum')
       call cubemain_message(seve%e,rname,'Try replacing them with, eg, zeros before baselining')
       error = .true.
       return
    else
       line%y%val = input%y%val
       call gwavelet_gaps(line%y%val,wavelets,error)
       if (error) return
       call gwavelet_subtract(prog%order,wavelets,line%y%val,error)
       if (error) return
       base%y%val = input%y%val-line%y%val
    endif
    !
    call base%put(ie,error)
    if (error) return
    call line%put(ie,error)
    if (error) return
  end subroutine cubemain_baseline_wavelet_prog_act
end module cubemain_baseline_wavelet
! 
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
