!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! This template reads a UV table and writes a LM (2D) cube from it, with
! CUBE capabilities as of 19-MAR-2025.
!
! - CUBE is thought to iterate entries (image or spectrum) mapping
!   leading dimensions (LM or C resp.), and iterating the trailing
!   dimension(s).
! - The real common dimension is the visibility axis. A possibility
!   could be to map the input table dimensions as:
!      C (channel axis) = 1
!      X (vis. axis) = 2
!      Y (fake) = 3
!   and the output image as
!      C (pseudo L) = 1,
!      X (pseudo M) = 2,
!      Y (fake) = 3
!   Then we should be able to use "spectrum" iteration, iterating the
!   trailing X*Y dimensions.
! - However, in practice, the current internal engines actually iterate
!   the Y dimension (i.e. buffer C*X planes) which is degenerate...
! - Because of the above remark, the command is currently coded with
!   full data access, using full-cube types. Two drawbacks:
!     1/ this means we access the 2D data with a 3D type
!     2/ full access means the data must fit in memory all at once.
!   For ease of use, the dimensions must be mapped as:
!     X = 1
!     Y = 2
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubetemplate_table2cube
  use cubetools_parameters
  use cubetools_structure
  use cubetuple_format_class
  use cube_types
  use cubeadm_cubeid_types
  use cubeadm_cubeprod_types
  use cubetemplate_factor_types
  use cubetopology_cuberegion_types
  use cubetemplate_messaging
  !
  public :: table2cube
  private
  !
  type :: table2cube_comm_t
     type(option_t),     pointer :: comm
     type(factor_comm_t)         :: factor
    !type(cuberegion_comm_t)     :: region
     type(cubeid_arg_t), pointer :: intable
     type(cube_prod_t),  pointer :: oucube
   contains
     procedure, public  :: register => cubetemplate_table2cube_comm_register
     procedure, private :: parse    => cubetemplate_table2cube_comm_parse
     procedure, private :: main     => cubetemplate_table2cube_comm_main
  end type table2cube_comm_t
  type(table2cube_comm_t) :: table2cube
  !
  type table2cube_user_t
     type(cubeid_user_t)     :: cubeids
     type(factor_user_t)     :: factor
    !type(cuberegion_user_t) :: region
   contains
     procedure, private :: toprog => cubetemplate_table2cube_user_toprog
  end type table2cube_user_t
  !
  type table2cube_prog_t
    !type(cuberegion_prog_t)  :: region
     type(factor_prog_t)      :: factor
     class(format_t), pointer :: intable
     type(cube_t),    pointer :: oucube
   contains
     procedure, private :: header => cubetemplate_table2cube_prog_header
     procedure, private :: data   => cubetemplate_table2cube_prog_data
     procedure, private :: loop   => cubetemplate_table2cube_prog_loop
     procedure, private :: act    => cubetemplate_table2cube_prog_act
  end type table2cube_prog_t
  !
contains
  !
  subroutine cubetemplate_table2cube_command(line,error)
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    character(len=*), intent(in)    :: line
    logical,          intent(inout) :: error
    !
    type(table2cube_user_t) :: user
    character(len=*), parameter :: rname='TABLE2CUBE>COMMAND'
    !
    call cubetemplate_message(templateseve%trace,rname,'Welcome')
    !
    call table2cube%parse(line,user,error)
    if (error) return
    call table2cube%main(user,error)
    if (error) continue
  end subroutine cubetemplate_table2cube_command
  !
  !----------------------------------------------------------------------
  !
  subroutine cubetemplate_table2cube_comm_register(comm,error)
    use cubedag_allflags
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(table2cube_comm_t), intent(inout) :: comm
    logical,                  intent(inout) :: error
    !
    type(cubeid_arg_t) :: intable
    type(cube_prod_t) :: oucube
    character(len=*), parameter :: rname='TABLE2CUBE>COMM>REGISTER'
    !
    call cubetemplate_message(templateseve%trace,rname,'Welcome')
    !
    ! Syntax
    call cubetools_register_command(&
         'TABLE2CUBE','[cubeid]',&
         'Template command for UV-table input and 2D image-cube output access',&
         'Input file must be real and output file will be real.',&
         cubetemplate_table2cube_command,&
         comm%comm,&
         error)
    if (error) return
    call intable%register(&
         'INPUT',&
         'Input UV table',&
         strg_emp,&
         code_arg_optional,&
         [flag_any],&
         code_read,&
         code_access_fullset,&
         comm%intable,&
         error)
    if (error) return
    call comm%factor%register(error)
    if (error) return
   !call comm%region%register(error)
   !if (error) return
    !
    ! Products
    call oucube%register(&
         'OUTPUT',&
         'Output cube',&
         strg_emp,&
         [flag_template],&
         comm%oucube,&
         error)
    if (error)  return
  end subroutine cubetemplate_table2cube_comm_register
  !
  subroutine cubetemplate_table2cube_comm_parse(comm,line,user,error)
    !----------------------------------------------------------------------
    ! table2cube cubname
    !----------------------------------------------------------------------
    class(table2cube_comm_t), intent(in)    :: comm
    character(len=*),         intent(in)    :: line
    type(table2cube_user_t),  intent(out)   :: user
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='TABLE2CUBE>COMM>PARSE'
    !
    call cubetemplate_message(templateseve%trace,rname,'Welcome')
    !
    call cubeadm_cubeid_parse(line,comm%comm,user%cubeids,error)
    if (error) return
   !call comm%region%parse(line,user%region,error)
   !if (error) return
    call comm%factor%parse(line,user%factor,error)
    if (error) return
  end subroutine cubetemplate_table2cube_comm_parse
  !
  subroutine cubetemplate_table2cube_comm_main(comm,user,error)
    use cubeadm_timing
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(table2cube_comm_t), intent(in)    :: comm
    type(table2cube_user_t),  intent(inout) :: user
    logical,                  intent(inout) :: error
    !
    type(table2cube_prog_t) :: prog
    character(len=*), parameter :: rname='TABLE2CUBE>MAIN'
    !
    call cubetemplate_message(templateseve%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 cubetemplate_table2cube_comm_main
  !
  !----------------------------------------------------------------------
  !
  subroutine cubetemplate_table2cube_user_toprog(user,comm,prog,error)
    use cubeadm_get
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(table2cube_user_t), intent(in)    :: user
    type(table2cube_comm_t),  intent(in)    :: comm
    type(table2cube_prog_t),  intent(out)   :: prog
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='TABLE2CUBE>USER>TOPROG'
    !
    call cubetemplate_message(templateseve%trace,rname,'Welcome')
    !
    call cubeadm_get_fheader(comm%intable,user%cubeids,prog%intable,error)
    if (error) return
    call user%factor%toprog(prog%factor,error)
    if (error) return
   !call user%region%toprog(prog%intable,prog%region,error)
   !if (error) return
    ! User feedback about the interpretation of his command line
    call prog%factor%list(error)
    if (error) return
   !call prog%region%list(error)
   !if (error) return
  end subroutine cubetemplate_table2cube_user_toprog
  !
  !----------------------------------------------------------------------
  !
  subroutine cubetemplate_table2cube_prog_header(prog,comm,error)
    use cubetools_axis_types
    use cubetools_unit_setup
    use cubeadm_clone
    use cubetools_header_methods
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(table2cube_prog_t), intent(inout) :: prog
    type(table2cube_comm_t),  intent(in)    :: comm
    logical,                  intent(inout) :: error
    !
    type(axis_t) :: axis
    character(len=*), parameter :: rname='TABLE2CUBE>PROG>HEADER'
    !
    call cubetemplate_message(templateseve%trace,rname,'Welcome')
    !
    if (prog%intable%head%set%il.ne.1 .or.  &
        prog%intable%head%set%im.ne.2) then
      call cubetemplate_message(seve%e,rname,  &
        'The table dimensions must have been remapped as IX=1 and IY=2')
      error = .true.
      return
    endif
    !
    call cubeadm_clone_header(comm%oucube,prog%intable,prog%oucube,error)
    if (error) return
    !
    ! 1st axis: from the UV table frequency axis
    call cubetools_header_get_axis_head_l(prog%oucube%head,axis,error)
    if (error) return
    axis%name = 'Channels'
    axis%unit = ''
    axis%kind = code_unit_fov  ! ZZZ
    axis%genuine = .true.
    axis%regular = .true.
    ! Assume 7 leading DAPS and 3 values per atom (the header should be
    ! enriched to deal with these values)
    axis%n   = (prog%intable%head%set%axis(prog%intable%head%set%il)%n-7)/3
    axis%ref = 1.d0
    axis%val = 1.d0
    axis%inc = 1.d0
    call cubetools_header_update_axset_l(axis,prog%oucube%head,error)
    if (error)  return
    !
    ! 2nd axis: from the UV table visi axis
    call cubetools_header_get_axis_head_m(prog%oucube%head,axis,error)
    if (error) return
    axis%name = 'Visibilities'
    axis%unit = ''
    axis%kind = code_unit_fov  ! ZZZ
    axis%genuine = .true.
    axis%regular = .true.
    axis%n   = prog%intable%head%set%axis(prog%intable%head%set%im)%n
    axis%ref = 1.d0
    axis%val = 1.d0
    axis%inc = 1.d0
    call cubetools_header_update_axset_m(axis,prog%oucube%head,error)
    if (error)  return
    !
    ! 3rd axis: none
    call cubetools_header_nullify_axset_c(prog%oucube%head,error)
    if (error)  return
    !
    call prog%factor%header(prog%oucube,error)
    if (error) return
  end subroutine cubetemplate_table2cube_prog_header
  !
  subroutine cubetemplate_table2cube_prog_data(prog,error)
    use cubeadm_opened
    use cubeadm_fullcube_types
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(table2cube_prog_t), intent(inout) :: prog
    logical,                  intent(inout) :: error
    !
    type(cubeadm_iterator_t) :: iter
    type(fullcube_t) :: infull,oufull
    character(len=*), parameter :: rname='TABLE2CUBE>PROG>DATA'
    !
    call cubetemplate_message(templateseve%trace,rname,'Welcome')
    !
    call cubeadm_datainit_all(iter,error)
    if (error) return
    !
    ! Prepare the input cube for getting data
    call infull%associate('infull',prog%intable,error)
    if (error) return
    ! Prepare the output cube for putting data
    call oufull%allocate('oufull',prog%oucube,error)
    if (error) return
    !
    ! Loop on task => Only one task, (i.e. one iteration) => no parallelization statements
    do while (cubeadm_dataiterate_all(iter,error))
       if (error) exit
       if (.not.error) &
         call prog%loop(iter,infull,oufull,error)
    enddo ! itask
  end subroutine cubetemplate_table2cube_prog_data
  !
  subroutine cubetemplate_table2cube_prog_loop(prog,iter,infull,oufull,error)
    use cubeadm_taskloop
    use cubeadm_fullcube_types
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(table2cube_prog_t), intent(inout) :: prog
    type(cubeadm_iterator_t), intent(inout) :: iter
    type(fullcube_t),         intent(inout) :: infull
    type(fullcube_t),         intent(inout) :: oufull
    logical,                  intent(inout) :: error
    !
    character(len=*), parameter :: rname='TABLE2CUBE>PROG>LOOP'
    !
    do while (iter%iterate_entry(error))  ! Only 1 iteration in this case
      ! Get the input full cube
      call infull%get(error)
      if (error) return
      ! Do the computation
      call prog%act(infull,oufull,error)
      if (error) return
      ! Put the output full cube
      call oufull%put(error)
      if (error) return
    enddo  ! ientry
  end subroutine cubetemplate_table2cube_prog_loop
  !
  subroutine cubetemplate_table2cube_prog_act(prog,infull,oufull,error)
    use cubeadm_fullcube_types
    !----------------------------------------------------------------------
    !
    !----------------------------------------------------------------------
    class(table2cube_prog_t), intent(inout) :: prog
    type(fullcube_t),         intent(inout) :: infull
    type(fullcube_t),         intent(inout) :: oufull
    logical,                  intent(inout) :: error
    !
    integer(kind=indx_k) :: ix,iy,iz
    integer(kind=indx_k) :: ox,oy,oz
    character(len=*), parameter :: rname='TABLE2CUBE>PROG>ACT'
    !
    ! print *,'Input dimensions: ',shape(infull%val),' = ',infull%nx,infull%ny,infull%nz
    ! print *,'Output dimensions: ',shape(oufull%val),' = ',oufull%nx,oufull%ny,oufull%nz
    !
    do oz=1,oufull%nz  ! 3rd dimension (should be 1)
      iz = oz
      do oy=1,oufull%ny  ! 2nd dimension (visibilities)
        iy = oy
        do ox=1,oufull%nx  ! 1st dimension (channels)
          ix = 7+ox*3
          oufull%val(ox,oy,oz) = infull%val(ix,iy,iz)
        enddo
      enddo
    enddo
  end subroutine cubetemplate_table2cube_prog_act
end module cubetemplate_table2cube
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
