!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
module cubetuple_tuple
  use cubetools_parameters
  use cubeio_cube
  use cubetuple_messaging
  !
  public :: cubetuple_t,cubetuple_norder,cubetuple_trans_t ! [private]
  public :: code_trans_none,code_trans_memo,code_trans_disk
  public :: cubetuple_trans_reset
  private
  !
  integer(kind=code_k), parameter :: code_trans_none=0
  integer(kind=code_k), parameter :: code_trans_memo=1
  integer(kind=code_k), parameter :: code_trans_disk=2
  !
  ! Description of the pending transposition, if relevant
  type cubetuple_trans_t
    ! ZZZ This type will become useless once the cube_t provides directly
    !     its own cubedag_tuple_t. ZZZ I am not so sure now...
    integer(kind=code_k)  :: do=code_trans_none    ! Which transposition is enabled?
    integer(kind=4)       :: from=0   ! From this cube
    integer(kind=4)       :: to=0     ! To this cube
    character(len=trop_l) :: code=''  ! Transposition string code
  end type cubetuple_trans_t
  !
  integer(kind=4), parameter :: cubetuple_norder=2
  type cubetuple_t
    ! type(???)                                      ! Description of the N cubes, methods, etc
    type(cubeio_cube_t)          :: cube(cubetuple_norder)
    type(cubeio_cube_t), pointer :: current=>null()  ! Current access in use
    type(cubetuple_trans_t)      :: trans            ! [private?]  Transposition description
  contains
    procedure :: init          => cubetuple_tuple_init
    procedure :: order         => cubetuple_get_order
    procedure :: access        => cubetuple_get_access
    procedure :: iscplx        => cubetuple_get_iscplx
    procedure :: haskind       => cubetuple_has_filekind
    procedure :: nbytes        => cubetuple_get_nbytes
    procedure :: ndata         => cubetuple_get_ndata
    procedure :: nentry        => cubetuple_get_nentry
    procedure :: memsize       => cubetuple_get_memsize
    procedure :: list          => cubetuple_list
    procedure :: close         => cubetuple_close
    procedure :: free          => cubetuple_free
    procedure :: attach_file   => cubetuple_tuple_attach_file
    procedure :: elapsed_read  => cubetuple_tuple_elapsed_read
    procedure :: elapsed_write => cubetuple_tuple_elapsed_write
    final     :: cubetuple_final
  end type cubetuple_t
  !
contains
  !
  subroutine cubetuple_trans_reset(trans,error)
    type(cubetuple_trans_t), intent(out)   :: trans
    logical,                 intent(inout) :: error
    !
    ! All done by intent(out)
    return
  end subroutine cubetuple_trans_reset
  !
  subroutine cubetuple_tuple_init(tuple,error)
    !---------------------------------------------------------------------
    ! Initialize a cubetuple_t
    !---------------------------------------------------------------------
    class(cubetuple_t), intent(inout), target :: tuple
    logical,            intent(inout)         :: error
    ! Local
    integer(kind=4) :: iaccess
    !
    do iaccess=1,cubetuple_norder
      call tuple%cube(iaccess)%init(error)
      if (error)  return
    enddo
    ! Assume no one is used yet, use first arbitrarily
    tuple%current => tuple%cube(1)
    !
    call cubetuple_trans_reset(tuple%trans,error)
    if (error)  return
  end subroutine cubetuple_tuple_init
  !
  subroutine cubetuple_tuple_attach_file(tuple,file,error)
    use cubeio_desc_setup
    use cubeio_file
    !-------------------------------------------------------------------
    ! Attach the given cubeio_file_t as the current file, and recompute
    ! its IO description.
    ! Assume nothing has to be kept in the tuple.
    ! ---
    ! ZZZ I would like to moved this subroutine as a method of the
    ! cubeio_cube_t, but I have a circular dependency when invoking
    ! cubeio_set_descriptor_intrinsic
    !-------------------------------------------------------------------
    class(cubetuple_t),  intent(inout), target :: tuple
    type(cubeio_file_t), pointer               :: file
    logical,             intent(inout)         :: error
    !
    ! Discard previous use
    tuple%current => tuple%cube(1)
    call cubeio_cube_final(tuple%current)
    !
    ! Same as cubeio_cube_init:
    call tuple%current%time%init(error)
    if (error)  return
    tuple%current%file => file
    !
    ! Recompute the IO description
    call cubeio_set_descriptor_intrinsic(tuple%current,error)
    if (error)  return
    ! ZZZ is this needed?
    ! call cubeio_set_descriptor_external(cubset,cubdef,old,tuple%cube(1),error)
    ! if (error)  return
    !
  end subroutine cubetuple_tuple_attach_file
  !
  function cubetuple_get_order(tuple)
    !-------------------------------------------------------------------
    ! Return the CURRENT cube order
    !-------------------------------------------------------------------
    integer(kind=code_k) :: cubetuple_get_order
    class(cubetuple_t), intent(in) :: tuple
    if (.not.associated(tuple%current)) then
      cubetuple_get_order = code_null
    else
      cubetuple_get_order = tuple%current%order()
    endif
  end function cubetuple_get_order
  !
  function cubetuple_get_access(tuple)
    !-------------------------------------------------------------------
    ! Return the CURRENT access mode.
    !-------------------------------------------------------------------
    integer(kind=code_k) :: cubetuple_get_access
    class(cubetuple_t), intent(in) :: tuple
    if (.not.associated(tuple%current)) then
      cubetuple_get_access = code_null
    else
      cubetuple_get_access = tuple%current%access()
    endif
  end function cubetuple_get_access
  !
  function cubetuple_get_iscplx(tuple)
    !-------------------------------------------------------------------
    ! Return .true. if the tuple data is complex*4
    !-------------------------------------------------------------------
    logical :: cubetuple_get_iscplx
    class(cubetuple_t), intent(in) :: tuple
    if (.not.associated(tuple%current)) then
      cubetuple_get_iscplx = .false.
    else
      cubetuple_get_iscplx = tuple%current%iscplx()
    endif
  end function cubetuple_get_iscplx
  !
  function cubetuple_has_filekind(tuple,code_filekind)
    !-------------------------------------------------------------------
    ! Return .true. if the tuple provides the given kind description
    !-------------------------------------------------------------------
    logical :: cubetuple_has_filekind
    class(cubetuple_t),   intent(in) :: tuple
    integer(kind=code_k), intent(in) :: code_filekind
    if (.not.associated(tuple%current)) then
      cubetuple_has_filekind = .false.
    else
      cubetuple_has_filekind = tuple%current%haskind(code_filekind)
    endif
  end function cubetuple_has_filekind
  !
  function cubetuple_get_nbytes(tuple)
    !-------------------------------------------------------------------
    ! Return the number of bytes per data value
    !-------------------------------------------------------------------
    integer(kind=4) :: cubetuple_get_nbytes
    class(cubetuple_t), intent(in) :: tuple
    if (.not.associated(tuple%current)) then
      cubetuple_get_nbytes = 0
    else
      cubetuple_get_nbytes = tuple%current%nbytes()
    endif
  end function cubetuple_get_nbytes
  !
  function cubetuple_get_ndata(tuple)
    !-------------------------------------------------------------------
    ! Return the number of data values
    !-------------------------------------------------------------------
    integer(kind=data_k) :: cubetuple_get_ndata
    class(cubetuple_t), intent(in) :: tuple
    if (.not.associated(tuple%current)) then
      cubetuple_get_ndata = 0
    else
      cubetuple_get_ndata = tuple%current%ndata()
    endif
  end function cubetuple_get_ndata
  !
  function cubetuple_get_nentry(tuple)
    !-------------------------------------------------------------------
    ! Return the number of entries (Nchan/NPix) for the CURRENT access
    ! mode.
    !-------------------------------------------------------------------
    integer(kind=entr_k) :: cubetuple_get_nentry
    class(cubetuple_t), intent(in) :: tuple
    if (.not.associated(tuple%current)) then
      cubetuple_get_nentry = 0
    else
      cubetuple_get_nentry = tuple%current%nentry()
    endif
  end function cubetuple_get_nentry
  !
  function cubetuple_get_memsize(tuple)
    !-------------------------------------------------------------------
    ! Return the memory footprint in bytes
    !-------------------------------------------------------------------
    integer(kind=size_length) :: cubetuple_get_memsize
    class(cubetuple_t), intent(in) :: tuple
    !
    integer(kind=4) :: iaccess
    !
    cubetuple_get_memsize = 0
    do iaccess=1,cubetuple_norder
      cubetuple_get_memsize = cubetuple_get_memsize + tuple%cube(iaccess)%memsize()
    enddo
  end function cubetuple_get_memsize
  !
  subroutine cubetuple_list(tuple,code_dataformat,error)
    !---------------------------------------------------------------------
    ! List the tuple header under requested format if available
    !---------------------------------------------------------------------
    class(cubetuple_t),   intent(in)    :: tuple
    integer(kind=code_k), intent(in)    :: code_dataformat
    logical,              intent(inout) :: error
    !
    character(len=*), parameter :: rname='LIST'
    !
    if (.not.associated(tuple%current)) then
      call cubetuple_message(seve%e,rname,'No current IO cube to show')
      error = .true.
      return
    endif
    call tuple%current%file%list(code_dataformat,error)
    if (error)  return
  end subroutine cubetuple_list
  !
  subroutine cubetuple_close(tuple,error)
    !---------------------------------------------------------------------
    ! GIO-close a 'cubetuple_t' instance. This is worth calling this
    ! subroutine as GIO slots are a limited ressource
    !---------------------------------------------------------------------
    class(cubetuple_t), intent(inout) :: tuple
    logical,            intent(inout) :: error
    ! Local
    integer(kind=4) :: iaccess
    !
    do iaccess=1,cubetuple_norder
      call tuple%cube(iaccess)%close(error)
      if (error)  continue
    enddo
  end subroutine cubetuple_close
  !
  subroutine cubetuple_free(tuple,error)
    !---------------------------------------------------------------------
    ! Free the memory-consuming components of a 'cubetuple_t' instance.
    ! The cubetuple_t remains useable after this free. It is the
    ! responsibility of the caller to ensure the data remains available
    ! elsewhere (most likely on disk).
    ! Use cubetuple_final to free consistently all the object.
    !---------------------------------------------------------------------
    class(cubetuple_t), intent(inout) :: tuple
    logical,            intent(inout) :: error
    ! Local
    integer(kind=4) :: iaccess
    !
    do iaccess=1,cubetuple_norder
      call tuple%cube(iaccess)%free(error)
      if (error)  return
    enddo
    nullify(tuple%current)
  end subroutine cubetuple_free
  !
  function cubetuple_tuple_elapsed_read(tuple)
    !-------------------------------------------------------------------
    ! Return the time spent reading data
    !-------------------------------------------------------------------
    real(kind=8) :: cubetuple_tuple_elapsed_read
    class(cubetuple_t), intent(in) :: tuple
    ! Local
    integer(kind=4) :: iaccess
    !
    cubetuple_tuple_elapsed_read = 0.d0
    do iaccess=1,cubetuple_norder
      cubetuple_tuple_elapsed_read = cubetuple_tuple_elapsed_read +  &
        tuple%cube(iaccess)%time%read%curr%elapsed
    enddo
  end function cubetuple_tuple_elapsed_read
  !
  function cubetuple_tuple_elapsed_write(tuple)
    !-------------------------------------------------------------------
    ! Return the time spent writing data
    !-------------------------------------------------------------------
    real(kind=8) :: cubetuple_tuple_elapsed_write
    class(cubetuple_t), intent(in) :: tuple
    ! Local
    integer(kind=4) :: iaccess
    !
    cubetuple_tuple_elapsed_write = 0.d0
    do iaccess=1,cubetuple_norder
      cubetuple_tuple_elapsed_write = cubetuple_tuple_elapsed_write +  &
        tuple%cube(iaccess)%time%writ%curr%elapsed
    enddo
  end function cubetuple_tuple_elapsed_write
  !
  subroutine cubetuple_final(tuple)
    !---------------------------------------------------------------------
    ! Finalize a 'cubetuple_t' instance, i.e. free all its components
    ! before deleting the object itself
    !---------------------------------------------------------------------
    type(cubetuple_t), intent(inout) :: tuple
    ! Local
    integer(kind=4) :: iaccess
    !
    do iaccess=1,cubetuple_norder
      call cubeio_cube_final(tuple%cube(iaccess))
    enddo
    nullify(tuple%current)
  end subroutine cubetuple_final
end module cubetuple_tuple
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
