module cubeadm_opened
  use cubelist_types
  use cubedag_parameters
  use cubetools_flag_types
  use cubedag_link_type
  use cubedag_node_class
  use cubetuple_format_class
  use cubetopology_cuberegion_types
  use cubeadm_cubeid_types
  use cubeadm_cubeprod_types
  use cubeadm_taskloop
  use cubeadm_opened_types
  use cubeadm_messaging
  !---------------------------------------------------------------------
  ! Support module for registering and keep track of cubes opened by
  ! current command, up to the moment they are closed with
  ! 'cubeadm_finish_all'
  !---------------------------------------------------------------------
  !
  ! Keep a list of the parents and children currently 'opened'.
  ! At some point they are all 'flushed' in the DAG, with the proper
  ! cross-references.
  type(cubeadm_opened_list_t) :: all
  type(cubeadm_opened_list_t) :: pa
  type(cubeadm_opened_list_t) :: ch
  !
  integer(kind=code_k), parameter :: code_parent_noarg=1
  integer(kind=code_k), parameter :: code_parent=2
  integer(kind=code_k), parameter :: code_child=3
  integer(kind=code_k), parameter :: code_child_noprod=4
  !
  interface cubeadm_parents_add
    module procedure cubeadm_parents_add_noarg
    module procedure cubeadm_parents_add_witharg
  end interface cubeadm_parents_add
  !
  interface cubeadm_children_add
    module procedure cubeadm_children_add_noprod
    module procedure cubeadm_children_add_withprod
  end interface cubeadm_children_add
  !
  interface cubeadm_datainit_all
    module procedure cubeadm_datainit_allcubes_full
    module procedure cubeadm_datainit_allcubes_region
  end interface cubeadm_datainit_all
  !
  public :: cubeadm_parents_add,cubeadm_children_add
  public :: cubeadm_datainit_all,cubeadm_dataiterate_all
  public :: cubeadm_finish_all
  public :: cubeadm_iterator_t  ! For convenience to users
  public :: cubeadm_parents_children_pop
  private
  !
contains
  !
  subroutine cubeadm_parents_add_noarg(dno,action)
    !-------------------------------------------------------------------
    ! Add a parent cube as command input
    ! ---
    ! This version does not provide a cubeid_arg_t nor a
    ! cubeid_user_cube_t
    !-------------------------------------------------------------------
    class(cubedag_node_object_t), pointer    :: dno
    integer(kind=code_k),         intent(in) :: action
    !
    type(cubeadm_opened_t), pointer :: obj  ! Pointer to the actual object
    logical :: error,skip
    !
    error = .false.
    call cubeadm_add_opened_node(code_parent_noarg,  &
                                 action,  &
                                 ' ',     &  ! User input => unknown with this API
                                 dno,     &
                                 null(),  &  ! Argument/product => unknown with this API
                                 obj,skip,error)
    if (error)  return
    if (skip)   return
    !
    call pa%associate(obj,error)
    if (error)  return
  end subroutine cubeadm_parents_add_noarg
  !
  subroutine cubeadm_parents_add_witharg(arg,user,dno,action)
    !-------------------------------------------------------------------
    ! Add a parent cube as command input
    ! ---
    ! This version provides a cubeid_arg_t (for input cube expectations)
    ! and a cubeid_user_cube_t (for actual user inputs)
    !-------------------------------------------------------------------
    type(cubeid_arg_t), target,   intent(in) :: arg
    type(cubeid_user_cube_t),     intent(in) :: user
    class(cubedag_node_object_t), pointer    :: dno
    integer(kind=code_k),         intent(in) :: action
    !
    type(cubeadm_opened_t), pointer :: obj  ! Pointer to the actual object
    class(list_object_t), pointer :: arg_or_prod
    logical :: error,skip
    !
    error = .false.
    arg_or_prod => arg
    call cubeadm_add_opened_node(code_parent,  &
                                 action,       &
                                 user%id,      &  ! User input => keep only the non-parsed string
                                 dno,          &
                                 arg_or_prod,  &
                                 obj,skip,error)
    if (error)  return
    if (skip)   return
    !
    call pa%associate(obj,error)
    if (error)  return
  end subroutine cubeadm_parents_add_witharg
  !
  subroutine cubeadm_children_add_noprod(dno,action)
    !-------------------------------------------------------------------
    ! Add a child cube as command output
    ! ---
    ! This version does not provide a cube_prod_t
    !-------------------------------------------------------------------
    class(cubedag_node_object_t), pointer    :: dno
    integer(kind=code_k),         intent(in) :: action
    !
    type(cubeadm_opened_t), pointer :: obj  ! Pointer to the actual object
    logical :: error,skip
    !
    error = .false.
    call cubeadm_add_opened_node(code_child_noprod,  &
                                 action,  &
                                 ' ',     &  ! User input => unknown with this API
                                 dno,     &
                                 null(),  &  ! Argument/product => unknown with this API
                                 obj,skip,error)
    if (error)  return
    if (skip)   return
    !
    call ch%associate(obj,error)
    if (error)  return
  end subroutine cubeadm_children_add_noprod
  !
  subroutine cubeadm_children_add_withprod(prod,dno,action)
    !-------------------------------------------------------------------
    ! Add a child cube as command output
    ! ---
    ! This version does not provide a cube_prod_t
    !-------------------------------------------------------------------
    type(cube_prod_t), target,    intent(in) :: prod
    class(cubedag_node_object_t), pointer    :: dno
    integer(kind=code_k),         intent(in) :: action
    !
    type(cubeadm_opened_t), pointer :: obj  ! Pointer to the actual object
    class(list_object_t), pointer :: arg_or_prod
    logical :: error,skip
    !
    error = .false.
    arg_or_prod => prod
    call cubeadm_add_opened_node(code_child,   &
                                 action,       &
                                 ' ',          &  ! User input => irrelevant
                                 dno,          &
                                 arg_or_prod,  &
                                 obj,skip,error)
    if (error)  return
    if (skip)   return
    !
    call ch%associate(obj,error)
    if (error)  return
  end subroutine cubeadm_children_add_withprod
  !
  subroutine cubeadm_add_opened_node(status,action,userid,dno,arg_or_prod,  &
    obj,skip,error)
    !-------------------------------------------------------------------
    ! Insert a new opened node in the 'all' list. Skip if already
    ! referenced.
    !-------------------------------------------------------------------
    integer(kind=code_k),         intent(in)    :: status
    integer(kind=code_k),         intent(in)    :: action
    character(len=*),             intent(in)    :: userid
    class(cubedag_node_object_t), pointer       :: dno
    class(list_object_t),         pointer       :: arg_or_prod
    type(cubeadm_opened_t),       pointer       :: obj
    logical,                      intent(out)   :: skip
    logical,                      intent(inout) :: error
    !
    integer(kind=list_k) :: iall
    class(list_object_t), pointer :: lot
    type(cubeadm_opened_t) :: model  ! Dummy model (template) for the Fortran allocation
    !
    ! Avoid double insertions. This can happen if a cube is re-accessed
    ! (cubeadm_access_header) during the command execution
    do iall=1,all%n
      obj => cubeadm_opened_ptr(all%list(iall)%p,error)
      if (error)  return
      if (associated(obj%dno,dno)) then
        skip = .true.
        return
      endif
    enddo
    skip = .false.
    !
    ! Allocate a new 'opened_t' object
    call all%allocate(model,lot,error)
    if (error)  return
    obj => cubeadm_opened_ptr(lot,error)
    if (error)  return
    !
    ! Fill the object
    obj%status = status
    obj%action = action
    obj%userid = userid
    obj%dno => dno
    obj%arg_or_prod => arg_or_prod
  end subroutine cubeadm_add_opened_node
  !
  !---------------------------------------------------------------------
  !
  subroutine cubeadm_parents_children_pop(dno,error)
    !-------------------------------------------------------------------
    ! Pop out the given node from the parents/children lists
    !-------------------------------------------------------------------
    class(cubedag_node_object_t), pointer       :: dno
    logical,                      intent(inout) :: error
    !
    call pa%unlink(dno,error)
    call ch%unlink(dno,error)
    call all%unlink(dno,error)
  end subroutine cubeadm_parents_children_pop
  !
  subroutine cubeadm_parents_children_reset()
    logical :: error
    error = .false.
    call pa%final(error)
    call ch%final(error)
    call all%final(error)
  end subroutine cubeadm_parents_children_reset
  !
  subroutine cubeadm_list_opened(error)
    use cubelist_list
    use cubedag_list_columns
    logical, intent(inout) :: error
    ! Local
    integer(kind=4) :: larg,lflag,luser,nc,icol
    character(len=mess_l) :: mess
    integer(kind=entr_k) :: icub
    type(cubeadm_opened_t), pointer :: opened
    type(cubedag_link_t) :: link
    type(cubeid_arg_t), pointer :: arg
    type(cube_prod_t), pointer :: prod
    character(len=*), parameter :: colnames(7) = &
         ['IDENTIFIER ','TYPE       ','CUBEID     ','OBSERVATORY',  &
          'SOURCE     ','LINE       ','DATASIZE   ']
    type(list_column_list_t) :: cols
    character(len=*), parameter :: rname='LIST>OPENED'
    !
    ! Set up columns
    do icol=1,7
      call cubedag_list_columns_fetch(colnames(icol),cols,error)
      if (error)  return
    enddo
    call all%to_link(link,error)
    if (error)  return
    call cols%widths(link,error)
    if (error)  return
    call link%final(error)
    if (error)  return
    !
    ! Automatic widths for leading columns
    larg = 1   ! Argument name width
    lflag = 1  ! Flag list width
    luser = 1  ! User input width
    do icub=1,all%n
      opened => cubeadm_opened_ptr(all%list(icub)%p,error)
      if (error)  return
      select case (opened%status)
      case (code_parent_noarg)  ! A parent with no associated cubeid_arg_t nor cubeid_user_cube_t
        ! Argument name size unchanged
        ! Flag column size unchanged
        ! User input column size unchanged
      case (code_parent)
        arg => cubedag_cubeid_arg_ptr(opened%arg_or_prod,error)
        if (error)  return
        ! Argument name size from command's cubeid_arg_t
        larg = max(larg,len_trim(arg%name))
        ! Flag column size from command's cubeid_arg_t
        call cubetools_flaglist_tostr(arg%flag,arg%nflag,lstrflag=nc,error=error)
        if (error) return
        lflag = max(lflag,nc)
        ! User input column size from user input
        luser = max(1,len_trim(opened%userid))
      case (code_child_noprod)
        ! Argument name size unchanged
        ! Flag column size from child cube itself
        call opened%dno%node%flag%repr(lstrflag=nc,error=error)
        if (error) return
        lflag = max(lflag,nc)
        ! User input column size not changed (irrelevant)
      case (code_child)
        prod => cubeadm_cubeprod_ptr(opened%arg_or_prod,error)
        if (error)  return
        ! Product name size from command's cube_prod_t
        larg = max(larg,len_trim(prod%name))
        ! Flag column size from command's cube_prod_t
        call cubetools_flaglist_tostr(prod%flag,prod%nflag,lstrflag=nc,error=error)
        if (error) return
        lflag = max(lflag,nc)
        ! User input column size not changed (irrelevant)
      case default
        call cubeadm_message(seve%w,rname,'Cube is neither parent nor child')
      end select
    enddo
    !
    ! Actual print
    do icub=1,all%n
      nc = 1
      opened => cubeadm_opened_ptr(all%list(icub)%p,error)
      if (error)  return
      select case (opened%status)
      case (code_parent_noarg)  ! A parent with no associated cubeid_arg_t nor cubeid_user_cube_t
        mess(nc:) = 'I'
        nc = nc+2
        mess(nc:) = ' '  ! No argument name
        nc = nc+larg+1
        mess(nc:) = ' '  ! No argument flags
        nc = nc+lflag+1
        mess(nc:) = ' '  ! No user input
        nc = nc+luser
      case (code_parent)
        arg => cubedag_cubeid_arg_ptr(opened%arg_or_prod,error)
        if (error)  return
        mess(nc:) = 'I'
        nc = nc+2
        mess(nc:) = arg%name
        nc = nc+larg+1
        call cubetools_flaglist_tostr(arg%flag,arg%nflag,strflag=mess(nc:),error=error)
        if (error) return
        nc = nc+lflag+1
        mess(nc:) = opened%userid
        nc = nc+luser
      case (code_child_noprod)
        mess(nc:) = 'O'
        nc = nc+2
        mess(nc:) = ' '  ! No argument name
        nc = nc+larg+1
        call opened%dno%node%flag%repr(strflag=mess(nc:),error=error)
        if (error) return
        nc = nc+lflag+1
        mess(nc:) = opened%userid
        nc = nc+luser
      case (code_child)
        prod => cubeadm_cubeprod_ptr(opened%arg_or_prod,error)
        if (error)  return
        mess(nc:) = 'O'
        nc = nc+2
        mess(nc:) = prod%name
        nc = nc+larg+1
        call cubetools_flaglist_tostr(prod%flag,prod%nflag,strflag=mess(nc:),error=error)
        if (error) return
        nc = nc+lflag+1
        mess(nc:) = opened%userid
        nc = nc+luser
      end select
      mess(nc:) = ' => '
      nc = nc+4
      call cols%line(opened%dno,mess(nc:),error)
      if (error) return
      call cubeadm_message(seve%r,rname,mess)
    enddo
    !
    call cols%final(error)
    if (error) return
  end subroutine cubeadm_list_opened
  !
  subroutine cubeadm_datainit_allcubes_full(iter,error,align)
    use cubetuple_iterator
    !-------------------------------------------------------------------
    ! Prepare the data IO for all the registered cubes
    !-------------------------------------------------------------------
    type(cubeadm_iterator_t),       intent(out)   :: iter
    logical,                        intent(inout) :: error
    integer(kind=code_k), optional, intent(in)    :: align
    ! Local
    character(len=*), parameter :: rname='DATAINIT>ALLCUBES>FULLSET'
    type(cuberegion_prog_t), pointer :: region
    integer(kind=code_k) :: lalign
    !
    if (present(align)) then
      lalign = align
    else
      lalign = code_align_auto
    endif
    region => null()
    call cubeadm_datainit_allcubes_do(iter,lalign,region,error)
    if (error) return
    !
  end subroutine cubeadm_datainit_allcubes_full
  !
  subroutine cubeadm_datainit_allcubes_region(iter,region,error)
    use cubetuple_iterator
    !-------------------------------------------------------------------
    ! Prepare the data IO for all the registered cubes
    !-------------------------------------------------------------------
    type(cubeadm_iterator_t),        intent(out)   :: iter
    type(cuberegion_prog_t), target, intent(in)    :: region
    logical,                         intent(inout) :: error
    ! Local
    character(len=*), parameter :: rname='DATAINIT>ALLCUBES>REGION'
    type(cuberegion_prog_t), pointer :: pregion
    !
    pregion => region
    call cubeadm_datainit_allcubes_do(iter,code_align_auto,pregion,error)
    if (error) return
  end subroutine cubeadm_datainit_allcubes_region
  !
  subroutine cubeadm_datainit_allcubes_do(iter,align,region,error)
    use cubetools_access_types
    use cubeadm_entryloop
    !-------------------------------------------------------------------
    ! Prepare the data IO for all the registered cubes
    !-------------------------------------------------------------------
    type(cubeadm_iterator_t), intent(inout) :: iter
    integer(kind=code_k),     intent(in)    :: align
    type(cuberegion_prog_t),  pointer       :: region
    logical,                  intent(inout) :: error
    ! Local
    character(len=*), parameter :: rname='DATAINIT>ALLCUBES>DO'
    type(cubeadm_opened_t), pointer :: opened
    class(format_t), pointer :: incube,oucube
    integer(kind=entr_k) :: icube,refnum
    integer(kind=code_k) ::refaccess,reforder
    character(len=mess_l) :: mess
    type(cubedag_link_t) :: plink,clink
    !
    ! Loop on all the input cubes to check their consistency
    refnum = 0
    do icube=1,pa%n
      opened => cubeadm_opened_ptr(pa%list(icube)%p,error)
      if (error)  return
      if (opened%action.eq.code_read_head)  cycle  ! No need for data consistency check
      incube => cubetuple_format_ptr(opened%dno,error)
      if (error) return
      if (refnum.eq.0) then
        refnum = icube
        refaccess = incube%access()
        reforder = incube%order()
      else
        if (incube%order().ne.reforder .or. &
            incube%access().ne.refaccess) then
          call cubeadm_message(seve%e,rname,'Inconsistent cube accesses or orders:')
          write(mess,'(A,I0,4A)')  '  Cube #',refnum,  &
                                   ': access ',trim(cubetools_accessname(refaccess)),  &
                                   ', order ',cubetools_ordername(reforder)
          call cubeadm_message(seve%e,rname,mess)
          write(mess,'(A,I0,4A)')  '  Cube #',icube,  &
                                   ': access ',trim(cubetools_accessname(incube%access())),  &
                                   ', order ',cubetools_ordername(incube%order())
          call cubeadm_message(seve%e,rname,mess)
          error = .true.
          return
        endif
      endif
      call incube%tuple%elapsed_init(error)
      if (error)  return
    enddo
    !
    ! Loop on all the output cubes to prepare them for data access
    do icube=1,ch%n
      opened => cubeadm_opened_ptr(ch%list(icube)%p,error)
      if (error)  return
      oucube => cubetuple_format_ptr(opened%dno,error)
      if (error) return
      call cubeadm_datainit_one(oucube,error)
      if (error) return
      ! After the IO desc is available:
      call oucube%default_iorder(error)
      if (error)  return
    enddo
    !
    ! Taskloop iterator is set by taking all the input and output cubes
    ! into account
    call pa%to_link(plink,error)
    if (error)  return
    call ch%to_link(clink,error)
    if (error)  return
    call cubeadm_taskloop_init(plink,clink,align,region,iter,error)
    if (error) return
    call plink%final(error)
    if (error) return
    call clink%final(error)
    if (error) return
    ! Entryloop iterator
    call cubeadm_entryloop_init(iter%ne,error)
    if (error) return
    !
    ! Initialize processing
    do icube=1,ch%n
      opened => cubeadm_opened_ptr(ch%list(icube)%p,error)
      if (error)  return
      oucube => cubetuple_format_ptr(opened%dno,error)
      if (error) return
      call cubeadm_processinginit_one(oucube,iter%nt,error)
      if (error) return
      call oucube%tuple%elapsed_init(error)
      if (error)  return
    enddo
    !
    ! User feedback (after the output cubes have been prepared)
    call cubeadm_list_opened(error)
    if (error) return
  end subroutine cubeadm_datainit_allcubes_do
  !
  subroutine cubeadm_datainit_one(oucube,error)
    use cubeio_highlevel
    !-------------------------------------------------------------------
    ! Prepare the data IO for one cube
    !-------------------------------------------------------------------
    class(format_t), intent(inout) :: oucube
    logical,         intent(inout) :: error
    !
    ! Prepare HGDF and the descriptor (the later is needed between now
    ! and first data access)
    call cubeio_clone_header(oucube%user,oucube%prog,oucube,oucube%tuple%current,error)
    if (error) return
  end subroutine cubeadm_datainit_one
  !
  subroutine cubeadm_processinginit_one(oucube,ntasks,error)
    use cubetools_header_methods
    !-------------------------------------------------------------------
    ! Prepare the processing for one cube
    !-------------------------------------------------------------------
    class(format_t),      intent(inout) :: oucube
    integer(kind=entr_k), intent(in)    :: ntasks
    logical,              intent(inout) :: error
    !
    ! Re-initialize the extrema since they are about to be recomputed
    ! in the upcoming processing loop (OBSOLESCENT)
    ! BEWARE: ndat is also computed below
    call cubetools_header_extrema_init(oucube%head,error)
    if (error) return
    !
    ! Extrema are not recomputed if disabled or if complex. Leave them
    ! reinitialized on purpose.
    if (.not.oucube%user%output%extrema .or. oucube%iscplx())  return
    !
    ! Re-initialize the per-task extrema since they are about to be
    ! recomputed in the upcoming processing loop
    call oucube%proc%allocate_extrema(oucube%head,ntasks,error)
    if (error)  return
  end subroutine cubeadm_processinginit_one
  !
  function cubeadm_dataiterate_all(iter,error)
    !-------------------------------------------------------------------
    ! Iterate the iterator until all data is processed, and ensure all
    ! the registered cubes are ready to access their first to last
    ! entries.
    !-------------------------------------------------------------------
    logical :: cubeadm_dataiterate_all
    type(cubeadm_iterator_t), intent(inout) :: iter
    logical,                  intent(inout) :: error
    ! Local
    type(cubeadm_opened_t), pointer :: opened
    integer(kind=entr_k) :: icube
    !
    ! Iteration
    cubeadm_dataiterate_all = cubeadm_taskloop_iterate(iter,error)
    if (error) return
    if (.not.cubeadm_dataiterate_all)   return  ! All done
    !
    ! Parents
    do icube=1,pa%n
      opened => cubeadm_opened_ptr(pa%list(icube)%p,error)
      if (error)  return
      if (opened%action.eq.code_read .or.  &
          opened%action.eq.code_update) then  ! Skip parent which is code_read_head
        call cubeadm_dataiterate_one(opened%dno,iter%prange,error)
        if (error) return
      endif
    enddo
    !
    ! Children
    do icube=1,ch%n
      opened => cubeadm_opened_ptr(ch%list(icube)%p,error)
      if (error)  return
      call cubeadm_dataiterate_one(opened%dno,iter%prange,error)
      if (error) return
    enddo
    !
  end function cubeadm_dataiterate_all
  !
  subroutine cubeadm_dataiterate_one(dno,refrange,error)
    use cubeadm_ioloop
    !-------------------------------------------------------------------
    ! Ensure the identified cube is ready to access its iter%first to
    ! iter%last entries.
    !-------------------------------------------------------------------
    class(cubedag_node_object_t), pointer       :: dno
    integer(kind=indx_k),         intent(in)    :: refrange(2)
    logical,                      intent(inout) :: error
    ! Local
    class(format_t), pointer :: cube
    integer(kind=coor_k) :: thisrange(2)
    logical, parameter :: truncate=.true.
    character(len=*), parameter :: rname='DATAITERATE>ONE'
    !
    cube => cubetuple_format_ptr(dno,error)
    if (error)  return
    if (cube%access().eq.code_access_fullset) then
      call cube%iter%fullrange(thisrange,error)
      if (error)  return
    else
      call cube%iter%range(refrange,truncate,thisrange,error)
      if (error)  return
    endif
    call cubeadm_io_iterate_planes(thisrange(1),thisrange(2),cube,error)
    if (error) return
  end subroutine cubeadm_dataiterate_one
  !
  subroutine cubeadm_finish_all(comm,line,error)
    use cubedag_dag_buffer
    use cubedag_history_buffer
    use cubedag_links_tool
    use cubedag_remove_tool
    !---------------------------------------------------------------------
    ! Finish all cubes currently opened in index.
    ! ---
    ! Beware this subroutine can be called in an error recovery context,
    ! i.e. with error = .true. on input
    !---------------------------------------------------------------------
    character(len=*), intent(in)    :: comm
    character(len=*), intent(in)    :: line
    logical,          intent(inout) :: error
    ! Local
    character(len=*), parameter :: rname='FINISH>ALL'
    integer(kind=entr_k) :: ient,hid
    logical :: lerror
    type(cubeadm_opened_t), pointer :: opened
    type(cubedag_link_t) :: plink,clink
    !
    lerror = .false.  ! Local error
    !
    call pa%to_link(plink,lerror)
    if (lerror)  continue
    call ch%to_link(clink,lerror)
    if (lerror)  continue
    !
    ! Insert in history
    if (.not.error) then
      ! If you plan to put failed commands in the HISTORY list,
      ! you can not reference output cubes ('ch' list) which are
      ! not inserted in DAG but destroyed instead.
      call cubedag_history_add_tohx(comm,line,plink,clink,hid,lerror)
      if (lerror) continue
    endif
    !
    if (.not.error) then
      ! Update the links between parents and children, according to their read/write
      ! status. This is to be done before the cubes are freed (finalized)
      call nodeln%all_links(plink,clink,hid,lerror)
      if (lerror) continue
    endif
    !
    call plink%final(lerror)
    if (lerror)  continue
    call clink%final(lerror)
    if (lerror)  continue
    !
    ! Deal with the input cubes (including cubes opened in UPDATE mode, needing
    ! to be written on disk).
    do ient=1,pa%n
      opened => cubeadm_opened_ptr(pa%list(ient)%p,lerror)
      if (lerror) exit
      call opened%dno%finish(lerror)
      if (lerror) exit
    enddo
    !
    ! Deal with the output cubes
    do ient=1,ch%n
      opened => cubeadm_opened_ptr(ch%list(ient)%p,lerror)
      if (lerror) exit
      call opened%dno%finish(lerror)
      if (lerror) exit
      if (error) then
        call noderm%destroy(opened%dno,lerror)
        if (lerror) exit
      else
        call cubedag_dag_attach(opened%dno,lerror)
        if (lerror) exit
      endif
    enddo
    !
    ! Reset parents and children list
    call cubeadm_parents_children_reset()
    !
    ! Ensure the ID counter is up-to-date (in particular if an error
    ! occured, i.e. the output cubes were discarded)
    call cubedag_dag_updatecounter(lerror)
    if (lerror) continue
    !
    if (lerror) error = .true.
    !
  end subroutine cubeadm_finish_all
end module cubeadm_opened
