!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubeadm_visi_types
  use cubetools_parameters
  use cubetools_array_types
  use cube_types
  use cubeadm_messaging
  use cubeadm_taskloop
  !
  public :: visi_t
  private
  !
  type, extends(cplx_2d_t) :: visi_t
     logical :: uvkind = .true.              ! uv or lm plane?
     type(cube_t),             private, pointer :: cube => null() ! Associated cube
     type(cubeadm_iterator_t), private, pointer :: task => null() ! Associated task iteration
   contains
     generic,   public  :: allocate       => allocate_iter,allocate_noiter
     generic,   public  :: associate      => associate_iter,associate_noiter
     procedure, public  :: get_pixel_area => visi_get_pixel_area
     procedure, public  :: get            => visi_get
     procedure, public  :: put            => visi_put
     procedure, public  :: put_in         => visi_put_in
     procedure, private :: tasknum        => visi_task_num
     !
     procedure, private :: allocate_iter    => visi_allocate_iter
     procedure, private :: allocate_noiter  => visi_allocate_noiter
     procedure, private :: associate_iter   => visi_associate_iter
     procedure, private :: associate_noiter => visi_associate_noiter
  end type visi_t
  !
contains
  !
  subroutine visi_allocate_iter(visi,name,cube,iterator,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(visi_t),                    intent(out)   :: visi
    character(len=*),                 intent(in)    :: name
    type(cube_t),             target, intent(in)    :: cube
    type(cubeadm_iterator_t), target, intent(in)    :: iterator
    logical,                          intent(inout) :: error
    !
    call visi_allocate_noiter(visi,name,cube,error)
    if (error)  return
    visi%task => iterator
  end subroutine visi_allocate_iter
  !
  subroutine visi_allocate_noiter(visi,name,cube,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(visi_t),        intent(out)   :: visi
    character(len=*),     intent(in)    :: name
    type(cube_t), target, intent(in)    :: cube
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='VISI>ALLOCATE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    if (.not.cube%iscplx()) then
       call cubeadm_message(seve%e,rname,  &
            'Invalid attempt to get a C*4 image from a R*4 cube')
       error = .true.
       return
    endif
    !
    call visi%reallocate(name,&
         cube%tuple%current%desc%nx,&
         cube%tuple%current%desc%ny,&
         error)
    if (error) return
    visi%cube => cube
    visi%task => null()
  end subroutine visi_allocate_noiter
  !
  subroutine visi_associate_iter(visi,name,cube,iterator,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(visi_t),                    intent(out)   :: visi
    character(len=*),                 intent(in)    :: name
    type(cube_t),             target, intent(in)    :: cube
    type(cubeadm_iterator_t), target, intent(in)    :: iterator
    logical,                          intent(inout) :: error
    !
    call visi_associate_noiter(visi,name,cube,error)
    if (error)  return
    visi%task => iterator
  end subroutine visi_associate_iter
  !
  subroutine visi_associate_noiter(visi,name,cube,error)
    !-------------------------------------------------------------------
    !-------------------------------------------------------------------
    class(visi_t),        intent(out)   :: visi
    character(len=*),     intent(in)    :: name
    type(cube_t), target, intent(in)    :: cube
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='VISI>ASSOCIATE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    if (.not.cube%iscplx()) then
       call cubeadm_message(seve%e,rname,  &
            'Invalid attempt to get a C*4 image from a R*4 cube')
       error = .true.
       return
    endif
    !
    call visi%prepare_association(name,&
         cube%tuple%current%desc%nx,&
         cube%tuple%current%desc%ny,&
         error)
    if (error) return
    visi%cube => cube
    visi%task => null()
  end subroutine visi_associate_noiter
  !
  !------------------------------------------------------------------------
  !
  subroutine visi_get_pixel_area(visi,area,error)
    use cubetools_axis_types
    use cubetools_header_methods
    !---------------------------------------------------------------------
    ! 
    !---------------------------------------------------------------------
    class(visi_t),     intent(in)    :: visi
    real(kind=coor_k), intent(out)   :: area
    logical,           intent(inout) :: error
    !
    type(axis_t) :: axis
    real(kind=coor_k) :: xinc,yinc
    character(len=*), parameter :: rname='GET>PIXEL>AREA'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call cubetools_header_get_axis_head_l(visi%cube%head,axis,error)
    if (error) return
    xinc = axis%inc
    call cubetools_header_get_axis_head_m(visi%cube%head,axis,error)
    if (error) return
    yinc = axis%inc
    area = xinc*yinc
  end subroutine visi_get_pixel_area
  !
  !------------------------------------------------------------------------
  !
  subroutine visi_get(visi,ient,error)
    use cubeio_chan
    use cubetuple_entry
    !---------------------------------------------------------------------
    ! Get the ient image from the cube.
    ! When visi%val is an allocated pointer, we make a copy.
    ! In all other cases (associated or null), we make it point to the data.
    !---------------------------------------------------------------------
    class(visi_t),        intent(inout) :: visi
    integer(kind=entr_k), intent(in)    :: ient
    logical,              intent(inout) :: error
    ! 
    type(cube_chan_t) :: entry
    character(len=*), parameter :: rname='GET>VISI'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call cubetuple_get_chan(visi%cube%user,visi%cube%prog,visi%cube,ient,entry,error)
    if (error) return
    !
    if (visi%pointeris.eq.code_pointer_allocated) then
       visi%val(:,:) = entry%c4(:,:)
    else
       visi%val => entry%c4
       visi%pointeris = code_pointer_associated
    endif
    visi%nx = entry%nx
    visi%ny = entry%ny
    visi%uvkind = .true.
    !
    call cubeio_free_chan(entry,error)
    if (error) return
  end subroutine visi_get
  !
  subroutine visi_put(visi,ient,error)
    !---------------------------------------------------------------------
    ! Put the ient image to the cube
    ! Only use pointers => Nothing to free
    !---------------------------------------------------------------------
    class(visi_t),        intent(in)    :: visi
    integer(kind=entr_k), intent(in)    :: ient
    logical,              intent(inout) :: error
    !
    call visi_put_in(visi,visi%cube,ient,error)
    if (error)  return
  end subroutine visi_put
  !
  subroutine visi_put_in(visi,cube,ient,error)
    use cubeio_chan
    use cubetuple_entry
    !---------------------------------------------------------------------
    ! Put the ient visi to the cube.
    ! Only use pointers => Nothing to free.
    !
    ! This flavor, which explicitely states the output cube, should be used
    ! when the input visi needs to be written in another cube without
    ! copy. See, eg, the copy tool. This should an exotic use compare to
    ! visi_put.
    ! ---------------------------------------------------------------------
    class(visi_t),        intent(in)    :: visi
    type(cube_t),         intent(inout) :: cube
    integer(kind=entr_k), intent(in)    :: ient
    logical,              intent(inout) :: error
    !
    type(cube_chan_t) :: entry
    character(len=*), parameter :: rname='VISI>PUT>IN'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    entry%allocated = code_pointer_associated
    entry%nx = visi%nx
    entry%ny = visi%ny
    entry%c4 => visi%val
    entry%iscplx = .true.
    !
    call cubetuple_put_chan(cube%user,       &
                            cube%prog,       &
                            cube,            &
                            visi%tasknum(),  &
                            ient,            &
                            entry,           &
                            error)
    if (error) return
  end subroutine visi_put_in
  !
  function visi_task_num(visi)
    !-------------------------------------------------------------------
    ! Return the task number this visi_t is running with
    !-------------------------------------------------------------------
    integer(kind=entr_k) :: visi_task_num
    class(visi_t), intent(in) :: visi
    !
    if (associated(visi%task)) then
      visi_task_num = visi%task%num
    else
      ! Assume single thread
      visi_task_num = 1
    endif
  end function visi_task_num
end module cubeadm_visi_types
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
