!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubetools_spatial_types
  use cubetools_parameters
  use cubetools_messaging
  use cubetools_beam_types
  use cubetools_spaelt_types
  use cubetools_spafra_types
  use cubetools_spapro_types
  use cubetools_axis_types
  use cubetools_consistency_types
  use cubetools_structure
  !
  public :: spatial_t,spatial_cons_t
  public :: cubetools_spatial_init,cubetools_spatial_put_and_derive
  public :: cubetools_spatial_final,cubetools_spatial_get
  public :: cubetools_spatial_list
  public :: cubetools_spatial_sicdef,cubetools_spatial_copy
  public :: cubetools_spatial_consistency_init,cubetools_spatial_consistency_final
  public :: cubetools_spatial_consistency_check,cubetools_spatial_consistency_print
  public :: cubetools_spatial_consistency_set_tol
  !
  ! Source parse, user2prog and register
  public :: spatial_source_user_t,spatial_source_opt_t
  public :: cubetools_spatial_source2uservar
  ! Spatial frame 
  public :: spafra_opt_t,spafra_user_t,spafra_t
  ! Projection
  public :: spapro_t
  public :: spapro_type_opt_t,spapro_type_user_t,spapro_center_opt_t,spapro_center_user_t
  public :: spapro_angle_opt_t,spapro_angle_user_t
  ! Beam parse, user2prog and register
  public :: beam_opt_t,beam_user_t,beam_t
  !
  private
  !
  type spatial_t
     character(len=argu_l) :: source = strg_unk ! [---] Source name
     type(spafra_t)        :: fra               ! Frame of reference
     type(spaelt_t)        :: ref               ! Value     at reference pixel
     type(spaelt_t)        :: inc               ! Increment at reference pixel
     type(spapro_t)        :: pro               ! Projection
     type(beam_t)          :: bea               ! Beam
     type(axis_t), pointer :: l => null()       ! Pointer to l axis
     type(axis_t), pointer :: m => null()       ! Pointer to m axis
  end type spatial_t
  !
  type spatial_source_opt_t
     type(option_t),      pointer :: opt
   contains
     procedure :: register  => cubetools_spatial_source_register
     procedure :: parse     => cubetools_spatial_source_parse
     procedure :: user2prog => cubetools_spatial_source_user2prog
  end type spatial_source_opt_t
  !
  type spatial_source_user_t
     character(len=argu_l) :: name = strg_unk   ! Source name
     logical               :: do   = .false.    ! Option was present
  end type spatial_source_user_t
  !
  type spatial_cons_t
     logical                   :: check=.true.  ! Check the section
     logical                   :: prob =.false. ! Is there a problem
     logical                   :: mess =.true.  ! Output message for this section?
     type(consistency_desc_t)  :: sou           ! Source Consistency
     type(spafra_cons_t)       :: fra           ! Frame Consistency
     type(spapro_cons_t)       :: pro           ! Projection Consistency
     type(beam_cons_t)         :: bea           ! Beam consistency
     type(axis_cons_t)         :: l             ! L axis consistency
     type(axis_cons_t)         :: m             ! M axis consistency
  end type spatial_cons_t
contains
  !
  subroutine cubetools_spatial_init(spa,error)
    !-------------------------------------------------------------------
    ! Initialize the type
    !-------------------------------------------------------------------
    type(spatial_t), intent(inout) :: spa
    logical,         intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>INIT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    spa%source = strg_unk
    call cubetools_spafra_init(spa%fra,error)
    if(error) return
    call cubetools_spaelt_init(spa%ref,error)
    if (error) return
    call cubetools_spaelt_init(spa%inc,error)
    if (error) return
    call cubetools_spapro_init(spa%pro,error)
    if(error) return
    call cubetools_beam_init(spa%bea,error)
    if(error) return
  end subroutine cubetools_spatial_init
  !
  subroutine cubetools_spatial_put_and_derive(&
       source,&
       frame,equinox,&
       proj,l0,m0,pang,&
       major,minor,bang,&
       laxis,maxis,&
       spa,error)
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    character(len=*),     intent(in)    :: source
    integer(kind=code_k), intent(in)    :: frame
    real(kind=equi_k),    intent(in)    :: equinox
    integer(kind=code_k), intent(in)    :: proj
    real(kind=coor_k),    intent(in)    :: l0
    real(kind=coor_k),    intent(in)    :: m0
    real(kind=coor_k),    intent(in)    :: pang
    real(kind=beam_k),    intent(in)    :: major
    real(kind=beam_k),    intent(in)    :: minor
    real(kind=beam_k),    intent(in)    :: bang
    type(axis_t), target, intent(in)    :: laxis
    type(axis_t), target, intent(in)    :: maxis
    type(spatial_t),      intent(inout) :: spa
    logical,              intent(inout) :: error
    !
    real(kind=coor_k) :: il,im,linc,minc
    character(len=*), parameter :: rname='SPATIAL>PUT>AND>DERIVE'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    spa%source = source
    call cubetools_spafra_put_and_derive(frame,equinox,spa%fra,error)
    if (error) return
    call cubetools_spaelt_put_and_derive(il,im,l0,m0,spa%ref,error)
    if (error) return
    call cubetools_spaelt_put_and_derive(1d0,1d0,linc,minc,spa%inc,error)
    if (error) return
    call cubetools_spapro_put_and_derive(proj,l0,m0,pang,spa%pro,error)
    if(error) return
    call cubetools_beam_put_and_derive(major,minor,bang,spa%bea,error)
    if(error) return
    spa%l => laxis
    spa%m => maxis
  end subroutine cubetools_spatial_put_and_derive
  !
  subroutine cubetools_spatial_get(spa,&
       source,&
       frame,equinox,&
       proj,l0,m0,pang,&
       major,minor,bang,&
       error)
    !-------------------------------------------------------------------
    ! 
    !-------------------------------------------------------------------
    type(spatial_t),      intent(in)    :: spa
    character(len=*),     intent(out)   :: source
    integer(kind=code_k), intent(out)   :: frame
    real(kind=equi_k),    intent(out)   :: equinox
    integer(kind=code_k), intent(out)   :: proj
    real(kind=coor_k),    intent(out)   :: l0
    real(kind=coor_k),    intent(out)   :: m0
    real(kind=coor_k),    intent(out)   :: pang
    real(kind=beam_k),    intent(out)   :: major
    real(kind=beam_k),    intent(out)   :: minor
    real(kind=beam_k),    intent(out)   :: bang
    logical,              intent(inout) :: error
    !
    real(kind=coor_k) :: idummy,il,im,linc,minc
    character(len=*), parameter :: rname='SPATIAL>GET'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    source = spa%source
    call cubetools_spafra_get(spa%fra,frame,equinox,error)
    if (error) return
    call cubetools_spaelt_get(spa%ref,il,im,l0,m0,error)
    if (error) return
    call cubetools_spaelt_get(spa%inc,idummy,idummy,linc,minc,error)
    if (error) return
    call cubetools_spapro_get(spa%pro,proj,l0,m0,pang,error)
    if(error) return
    call cubetools_beam_get(spa%bea,major,minor,bang,error)
    if(error) return
  end subroutine cubetools_spatial_get
  !
  subroutine cubetools_spatial_final(spa,error)
    !-------------------------------------------------------------------
    ! Reinitialize the type
    !-------------------------------------------------------------------
    type(spatial_t), intent(inout) :: spa
    logical,         intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>FINAL'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    spa%source = strg_unk
    call cubetools_spafra_final(spa%fra,error)
    if (error) return    
    call cubetools_spaelt_final(spa%ref,error)
    if (error) return
    call cubetools_spaelt_final(spa%inc,error)
    if (error) return
    call cubetools_spapro_final(spa%pro,error)
    if(error) return
    call cubetools_beam_final(spa%bea,error)
    if (error) return
  end subroutine cubetools_spatial_final
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_spatial_list(spa,error)
    use gbl_constant
    use phys_const
    use cubetools_format
    !-------------------------------------------------------------------
    ! spa%ref and spa%inc are not listed (ie, commented out) on purpose
    ! here as they are listed in the axset section
    ! -------------------------------------------------------------------
    type(spatial_t), intent(in)    :: spa
    logical,         intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>LIST'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    ! call cubetools_spaelt_list(spa%ref,error)
    ! if (error) return
    ! call cubetools_spaelt_list(spa%inc,error)
    ! if (error) return
    call cubetools_spapro_list(spa%source,spa%fra,spa%pro,error)
    if(error) return
    call cubetools_beam_list(spa%bea,error)
    if(error) return
    call cubetools_message(seve%r,rname,' ')
  end subroutine cubetools_spatial_list
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_spatial_source_register(source,abstract,error)
    !----------------------------------------------------------------------
    ! Register a /SOURCE option under a given name and abstract
    !----------------------------------------------------------------------
    class(spatial_source_opt_t), intent(out)   :: source
    character(len=*),            intent(in)    :: abstract
    logical,                     intent(inout) :: error
    !
    type(standard_arg_t) :: stdarg
    !
    character(len=*), parameter :: rname='SPATIAL>SOURCE>REGISTER'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_register_option(&
         'SOURCE','name',&
         abstract,&
         strg_id,&
         source%opt,error)
    if (error) return
    call stdarg%register( &
         'name',  &
         'Source name', &
         '"*" or "=" mean previous value is kept',&
         code_arg_mandatory, error)
    if (error) return
  end subroutine cubetools_spatial_source_register
  !
  subroutine cubetools_spatial_source_parse(source,line,user,error)
    !----------------------------------------------------------------------
    ! Parse source name 
    ! /SOURCE name
    ! ----------------------------------------------------------------------
    class(spatial_source_opt_t), intent(in)    :: source
    character(len=*),            intent(in)    :: line
    type(spatial_source_user_t), intent(out)   :: user
    logical,                     intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>SOURCE>PARSE'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call source%opt%present(line,user%do,error)
    if (error) return
    if (user%do) then
       call cubetools_getarg(line,source%opt,1,user%name,mandatory,error)
       if (error)  return
    endif
  end subroutine cubetools_spatial_source_parse
  !
  subroutine cubetools_spatial_source_user2prog(source,user,prog,error)
    use cubetools_user2prog
    !----------------------------------------------------------------------
    ! Resolves source name based on a previous one according to user
    ! inputs
    ! --------------------------------------------------------------------
    class(spatial_source_opt_t), intent(in)    :: source
    type(spatial_source_user_t), intent(in)    :: user
    character(len=sour_l),       intent(inout) :: prog
    logical,                     intent(inout) :: error
    !
    character(len=sour_l) :: def, prev
    character(len=*), parameter :: rname='SPATIAL>SOURCE>USER2PROG'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (user%do) then
       if (len_trim(user%name).gt.sour_l) call cubetools_message(seve%w,rname,&
            'Source name will be truncated at 12 characters')
       def = prog
       prev = prog
       call cubetools_user2prog_resolve_all(user%name,def,prev,prog,error)
       if (error) return
    endif
  end subroutine cubetools_spatial_source_user2prog
  !
  subroutine cubetools_spatial_source2uservar(prog,uservar,error)
    use cubetools_userspace
    use cubetools_uservar
    !------------------------------------------------------------------------
    ! Loads spatial source onto a user space variable
    !------------------------------------------------------------------------
    type(spatial_t),    intent(in)    :: prog
    class(userspace_t), intent(inout) :: uservar
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname = 'SPATIAL>SOURCE2USERVAR'
    !
    call cubetools_message(toolseve%trace,rname,'welcome')
    !
    select type(uservar)
    type is (uservar_t)
       call uservar%set(prog%source,error)
       if (error) return
    class default
       call cubetools_message(seve%e,rname,'Internal error: object has wrong type')
       error = .true.
       return
    end select
  end subroutine cubetools_spatial_source2uservar
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_spatial_sicdef(name,spa,readonly,error)
    use gkernel_interfaces
    !-------------------------------------------------------------------
    ! Point a SIC structure onto an instance of the spatial_t type
    !-------------------------------------------------------------------
    character(len=*), intent(in)    :: name
    type(spatial_t),  intent(in)    :: spa
    logical,          intent(in)    :: readonly
    logical,          intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>SICDEF'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call sic_defstructure(name,global,error)
    if (error) return
    call sic_def_charn(trim(name)//'%source',spa%source,0,0,readonly,error)
    if (error)  return
    call cubetools_spafra_sicdef(trim(name)//'%fra',spa%fra,readonly,error)
    if (error) return
    call cubetools_spaelt_sicdef(trim(name)//'%ref',spa%ref,readonly,error)
    if (error) return
    call cubetools_spaelt_sicdef(trim(name)//'%inc',spa%inc,readonly,error)
    if (error) return
    call cubetools_spapro_sicdef(trim(name)//'%pro',spa%pro,readonly,error)
    if (error) return
    call cubetools_beam_sicdef(trim(name)//'%bea',spa%bea,readonly,error)
    if (error) return
!!$    call cubetools_axis_sicdef(trim(name)//'%L',spa%l,readonly,error)
!!$    if (error) return
!!$    call cubetools_axis_sicdef(trim(name)//'%M',spa%m,readonly,error)
!!$    if (error) return
  end subroutine cubetools_spatial_sicdef
  !
  subroutine cubetools_spatial_copy(laxis,maxis,in,ou,error)
    !-------------------------------------------------------------------
    ! Copy the spatial_t type content from one instance to another one
    ! -------------------------------------------------------------------
    type(axis_t), target, intent(in)    :: laxis
    type(axis_t), target, intent(in)    :: maxis
    type(spatial_t),      intent(in)    :: in
    type(spatial_t),      intent(out)   :: ou
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>COPY'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    ou%source = in%source
    call cubetools_spafra_copy(in%fra,ou%fra,error)
    if (error) return
    call cubetools_spaelt_copy(in%ref,ou%ref,error)
    if (error) return
    call cubetools_spaelt_copy(in%inc,ou%inc,error)
    if (error) return
    call cubetools_spapro_copy(in%pro,ou%pro,error)
    if(error) return
    call cubetools_beam_copy(in%bea,ou%bea,error)
    if (error) return
    ou%l => laxis
    ou%m => maxis
  end subroutine cubetools_spatial_copy
  !
  !---------------------------------------------------------------------
  !
  subroutine cubetools_spatial_consistency_init(cons,error)
    !-------------------------------------------------------------------
    ! Init the consistency between two spatial sections
    !-------------------------------------------------------------------
    type(spatial_cons_t), intent(out)   :: cons
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>CONSISTENCY>INIT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_consistency_init(notol,check,mess,cons%sou,error)
    if (error) return
    call cubetools_spafra_consistency_init(cons%fra,error)
    if (error) return
    call cubetools_spapro_consistency_init(cons%pro,error)
    if (error) return
    call cubetools_beam_consistency_init(cons%bea,error)
    if (error) return
    call cubetools_axis_consistency_init(cons%l,error)
    if (error) return
    call cubetools_axis_consistency_init(cons%m,error)
    if (error) return
    !   
  end subroutine cubetools_spatial_consistency_init
  !
  subroutine cubetools_spatial_consistency_set_tol(spatol,beatol,cons,error)
    !-------------------------------------------------------------------
    ! Set the tolerance for spatial consistency check
    !-------------------------------------------------------------------
    real(kind=tole_k),    intent(in)    :: spatol
    real(kind=tole_k),    intent(in)    :: beatol
    type(spatial_cons_t), intent(inout) :: cons
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>CONSISTENCY>SET>TOL'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_spapro_consistency_set_tol(spatol,cons%pro,error)
    if (error) return
    call cubetools_axis_consistency_set_tol(spatol,cons%l,error)
    if (error) return
    call cubetools_axis_consistency_set_tol(spatol,cons%m,error)
    if (error) return    
    call cubetools_beam_consistency_set_tol(beatol,cons%bea,error)
    if (error) return
  end subroutine cubetools_spatial_consistency_set_tol
  !
  subroutine cubetools_spatial_consistency_check(cons,spa1,spa2,error)
    !-------------------------------------------------------------------
    ! Check the consistency between two spatial sections
    !-------------------------------------------------------------------
    type(spatial_cons_t), intent(inout) :: cons
    type(spatial_t),      intent(in)    :: spa1
    type(spatial_t),      intent(in)    :: spa2
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>CONSISTENCY>CHECK'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (.not.cons%check) return
    !
    call cubetools_consistency_string_check(cons%sou,spa1%source,spa2%source,error)
    if (error) return
    call cubetools_spafra_consistency_check(cons%fra,spa1%fra,spa2%fra,error)
    if (error) return
    call cubetools_spapro_consistency_check(cons%pro,spa1%fra,spa1%l%inc,spa1%m%inc,spa1%pro,&
         spa2%fra,spa2%l%inc,spa2%m%inc,spa2%pro,error)
    if (error) return
    call cubetools_beam_consistency_check(cons%bea,spa1%bea,spa2%bea,error)
    if (error) return
    call cubetools_axis_consistency_check(cons%l,spa1%l,spa2%l,error)
    if (error) return
    call cubetools_axis_consistency_check(cons%m,spa1%m,spa2%m,error)
    if (error) return
    !
    cons%prob = cons%sou%prob.or.cons%fra%prob.or.cons%pro%prob.or.&
         cons%bea%prob.or.cons%l%prob.or.cons%m%prob
    !   
  end subroutine cubetools_spatial_consistency_check
  !
  subroutine cubetools_spatial_consistency_print(cons,spa1,spa2,error)
    use cubetools_format
    !-------------------------------------------------------------------
    ! Print the consistency between two spatial sections
    !-------------------------------------------------------------------
    type(spatial_cons_t), intent(in)    :: cons
    type(spatial_t),      intent(in)    :: spa1
    type(spatial_t),      intent(in)    :: spa2
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>CONSISTENCY>PRINT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (.not.cons%mess) return
    !
    call cubetools_consistency_title('spatial sections',2,cons%check,cons%prob,error)
    if(error) return
    if (cons%check.and.cons%prob) then
       call cubetools_spatial_source_consistency_print(cons%sou,spa1%source,spa2%source,error)
       if (error) return
       call cubetools_spafra_consistency_print(cons%fra,spa1%fra,spa2%fra,error)
       if (error) return
       call cubetools_spapro_consistency_print(cons%pro,spa1%fra,spa1%pro,&
            spa2%fra,spa2%pro,error)
       if (error) return
       call cubetools_beam_consistency_print(cons%bea,spa1%bea,spa2%bea,error)
       if (error) return
       call cubetools_axis_consistency_print('L',cons%l,spa1%l,spa2%l,error)
       if (error) return
       call cubetools_axis_consistency_print('M',cons%m,spa1%m,spa2%m,error)
       if (error) return
    endif
    call cubetools_message(seve%r,rname,'')
    !
  end subroutine cubetools_spatial_consistency_print
  !
  subroutine cubetools_spatial_source_consistency_print(cons,source1,source2,error)
    !-------------------------------------------------------------------
    ! Print the consistency between two sources
    !-------------------------------------------------------------------
    type(consistency_desc_t), intent(in)    :: cons
    character(len=*),         intent(in)    :: source1
    character(len=*),         intent(in)    :: source2
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>SOURCE>CONSISTENCY>PRINT'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    if (.not.cons%mess) return
    !
    call cubetools_consistency_title('sources',3,cons%check,cons%prob,error)
    if(error) return
    if (cons%check.and.cons%prob) then
       call cubetools_consistency_string_print('Source names',cons,source1,source2,error)
       if (error) return
    endif
    call cubetools_message(seve%r,rname,'')
  end subroutine cubetools_spatial_source_consistency_print
  !
  subroutine cubetools_spatial_consistency_final(cons,error)
    !-------------------------------------------------------------------
    ! Final the consistency between two spatial sections
    !-------------------------------------------------------------------
    type(spatial_cons_t), intent(out)   :: cons
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='SPATIAL>CONSISTENCY>FINAL'
    !
    call cubetools_message(toolseve%trace,rname,'Welcome')
    !
    call cubetools_consistency_final(cons%sou,error)
    if (error) return
    call cubetools_spafra_consistency_final(cons%fra,error)
    if (error) return
    call cubetools_spapro_consistency_final(cons%pro,error)
    if (error) return
    call cubetools_beam_consistency_final(cons%bea,error)
    if (error) return
    call cubetools_axis_consistency_final(cons%l,error)
    if (error) return
    call cubetools_axis_consistency_final(cons%m,error)
    if (error) return
    !   
  end subroutine cubetools_spatial_consistency_final
end module cubetools_spatial_types
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
