module cubeadm_undo
  use cubetools_structure
  use cube_types
  use cubedag_parameters
  use cubeadm_cubeid_types
  use cubeadm_messaging
  !
  public :: undo
  public :: cubeadm_undo_command
  private
  !
  type :: undo_comm_t
    type(option_t), pointer :: comm
  contains
    procedure, public  :: register => cubeadm_undo_register
    procedure, private :: parse    => cubeadm_undo_parse
    procedure, private :: main     => cubeadm_undo_main
  end type undo_comm_t
  type(undo_comm_t) :: undo
  !
  type undo_user_t
    integer(kind=iden_l) :: firstid
    integer(kind=iden_l) :: lastid
  contains
    procedure, private :: toprog => cubeadm_undo_user_toprog
  end type undo_user_t
  !
  type undo_prog_t
    integer(kind=iden_l) :: firstid
    integer(kind=iden_l) :: lastid
  contains
    procedure, public :: do => cubeadm_undo_do
  end type undo_prog_t
  !
contains
  !
  subroutine cubeadm_undo_command(line,error)
    !-------------------------------------------------------------------
    ! Support routine for command UNDO
    !-------------------------------------------------------------------
    character(len=*), intent(in)    :: line
    logical,          intent(inout) :: error
    !
    type(undo_user_t) :: user
    character(len=*), parameter :: rname='UNDO>COMMAND'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call undo%parse(line,user,error)
    if (error) return
    call undo%main(user,error)
    if (error) return
  end subroutine cubeadm_undo_command
  !
  !---------------------------------------------------------------------
  !
  subroutine cubeadm_undo_register(undo,error)
    !-------------------------------------------------------------------
    ! Register ADM\UNDO command and its options
    !-------------------------------------------------------------------
    class(undo_comm_t), intent(inout) :: undo
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: comm_help = strg_id
    type(standard_arg_t) :: stdarg
    character(len=*), parameter :: rname='UNDO>REGISTER'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    ! Command
    call cubetools_register_command(&
         'UNDO','[FirstCommandID] [LastCommandID]',&
         'Undo a command and remove it from history',&
         'Without argument, undo the last command (i.e. remove the command &
         &from the history and delete the products which were created).'&
         //strg_cr//strg_cr//&
         'With a positive identifier as argument, undo the command &
         &according to its ID. With a range of identifiers as arguments, &
         &undo the commands from last to first.'&
         //strg_cr//strg_cr//&
         'With a negative position as argument, undo the command &
         &according to its position from the end (0=last, -1=before last, &
         &etc).'&
         //strg_cr//strg_cr//&
         'Examples:'&
         //strg_cr//&
         '  CUBE> UNDO      ! Undo last command in HISTORY'&
         //strg_cr//&
         '  CUBE> UNDO 10   ! Undo command #10 in HISTORY'&
         //strg_cr//&
         '  CUBE> UNDO 7 10 ! Undo commands #10 #9 #8 #7 in HISTORY'&
         //strg_cr//&
         '  CUBE> UNDO 0    ! Undo last command in HISTORY'&
         //strg_cr//&
         '  CUBE> UNDO -1   ! Undo before last command in HISTORY'&
         //strg_cr//strg_cr//&
         'It is not possible to undo a command whose output products &
         &where used as inputs to other commands.',&
         cubeadm_undo_command,&
         undo%comm,error)
    if (error) return
    call stdarg%register( &
         'COMMAND', &
         '[FirstCommandID]',  &
         strg_id,&
         code_arg_optional, &
         error)
    if (error) return
    call stdarg%register( &
         'COMMAND', &
         '[LastCommandID]',  &
         strg_id,&
         code_arg_optional, &
         error)
    if (error) return
    !
  end subroutine cubeadm_undo_register
  !
  subroutine cubeadm_undo_parse(undo,line,user,error)
    use cubetools_disambiguate
    !-------------------------------------------------------------------
    ! Parsing facility for command
    !   UNDO
    !-------------------------------------------------------------------
    class(undo_comm_t), intent(inout) :: undo
    character(len=*),   intent(in)    :: line
    type(undo_user_t),  intent(out)   :: user
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname='UNDO>PARSE'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    user%firstid = 0  ! 0=last (same as CubeID)
    call cubetools_getarg(line,undo%comm,1,user%firstid,.not.mandatory,error)
    if (error) return
    !
    user%lastid = user%firstid  ! Default is no range
    call cubetools_getarg(line,undo%comm,2,user%lastid,.not.mandatory,error)
    if (error) return
  end subroutine cubeadm_undo_parse
  !
  subroutine cubeadm_undo_main(undo,user,error)
    !-------------------------------------------------------------------
    ! 
    !-------------------------------------------------------------------
    class(undo_comm_t), intent(in)    :: undo
    type(undo_user_t),  intent(in)    :: user
    logical,            intent(inout) :: error
    !
    type(undo_prog_t) :: prog
    character(len=*), parameter :: rname='UNDO>MAIN'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    call user%toprog(prog,error)
    if (error)  return
    call prog%do(error)
    if (error)  return
  end subroutine cubeadm_undo_main
  !
  subroutine cubeadm_undo_user_toprog(user,prog,error)
    !-------------------------------------------------------------------
    ! 
    !-------------------------------------------------------------------
    class(undo_user_t), intent(in)    :: user
    type(undo_prog_t),  intent(out)   :: prog
    logical,            intent(inout) :: error
    !
    character(len=*), parameter :: rname='UNDO>USER2PROG'
    !
    call cubeadm_message(admseve%trace,rname,'Welcome')
    !
    prog%firstid = user%firstid  ! >0 or <=0 resolved by
                                 ! cubedag_history_removecommand itself
    prog%lastid  = user%lastid
  end subroutine cubeadm_undo_user_toprog
  !
  subroutine cubeadm_undo_do(prog,error)
    use cubedag_dag
    use cubedag_history
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    class(undo_prog_t), intent(in)    :: prog
    logical,            intent(inout) :: error
    !
    integer(kind=iden_l) :: iid
    !
    ! Nota: this works fine for >0 values (identifiers)
    ! For <0 values (positions), we should remove 0 (the last one)
    ! again and again as many times necessary.
    do iid=prog%lastid,prog%firstid,-1
      call cubedag_history_removecommand(iid,error)
      if (error)  return
    enddo
    call cubedag_dag_updatecounter(error)
    if (error)  return
  end subroutine cubeadm_undo_do
end module cubeadm_undo
