module cubeio_readwrite_iterator
  use cubetools_parameters
  use cubetools_setup_types
  use cubeio_messaging
  use cubeio_cube
  use cubeio_range
  !---------------------------------------------------------------------
  ! Small utility which allows reading/writing data blocks to/from
  ! memory from/to file, by parts. Doing this by parts has some
  ! advantages (like trapping CTRL-C) with limited overheads.
  !
  ! Note: as of today this is implemented when reading the whole file
  ! from disk to memory (i.e. first touch in memory mode). On the other
  ! hand, we have no use case which writes the whole memory buffer to
  ! disk all at once. In particular, the EXPORT command is implemented
  ! by forcing the disk mode and iterating entries, which flushes
  ! blocks at regular intervals.
  !---------------------------------------------------------------------
  !
  type :: readwrite_iterator_t
    character(len=8),     private :: action
    integer(kind=data_k), private :: n3perblock
    integer(kind=data_k), private :: tf3,tl3     ! 3rd dimension total range to read
    integer(kind=data_k), public  :: first,last  ! 3rd dimension current range to read
    type(cubeio_range_t), public  :: range
  contains
    procedure, public :: init    => readwrite_iterator_init
    procedure, public :: iterate => readwrite_iterator_iterate
  end type readwrite_iterator_t

contains

  subroutine readwrite_iterator_init(iter,cubset,cube,action,error)
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    class(readwrite_iterator_t), intent(inout) :: iter
    type(cube_setup_t),          intent(in)    :: cubset
    type(cubeio_cube_t),         intent(in)    :: cube
    character(len=*),            intent(in)    :: action
    logical,                     intent(inout) :: error
    !
    iter%action = action
    !
    call cube%plane_per_block(cubset%buff%block,'SET\BUFFER /BLOCK',  &
                              iter%n3perblock,error)
    if (error)  return
    !
    ! Note: it could also be possible to read a range on 3rd dimension
    ! instead of the whole cube
    iter%tf3 = 1
    iter%tl3 = cube%desc%n3
    !
    ! Prepare first iteration
    iter%range%blc(:) = 0
    iter%range%trc(:) = 0
    iter%last = iter%tf3-1
  end subroutine readwrite_iterator_init
  !
  function readwrite_iterator_iterate(iter,cube,error)
    use gkernel_interfaces
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    logical :: readwrite_iterator_iterate
    class(readwrite_iterator_t), intent(inout) :: iter
    type(cubeio_cube_t),         intent(in)    :: cube
    logical,                     intent(inout) :: error
    !
    character(len=mess_l) :: mess
    character(len=*), parameter :: rname='READWRITE>ITERATOR'
    !
    readwrite_iterator_iterate = .false.
    if (iter%last.ge.iter%tl3)  return
    if (sic_ctrlc()) then
       call cubeio_message(seve%w,rname,trim(iter%action)//' aborted by user CTRL-C')
       error = .true.
       return
    endif
    !
    readwrite_iterator_iterate = .true.
    iter%first = iter%last+1
    iter%last  = min(iter%last+iter%n3perblock,iter%tl3)
    iter%range%blc(cube%desc%i3) = iter%first
    iter%range%trc(cube%desc%i3) = iter%last
    !
    write(mess,'(3(a,i0))')  &
      trim(iter%action)//' planes ',iter%first,' to ',iter%last,' over ',cube%desc%n3
    call cubeio_message(ioseve%others,rname,mess)
  end function readwrite_iterator_iterate
  !
end module cubeio_readwrite_iterator
