module cubedag_undo
  use cubetools_structure
  use cubedag_parameters
  use cubedag_messaging
  use cubedag_dag_buffer
  use cubedag_history_buffer
  !
  public :: undo
  private
  !
  type :: undo_comm_t
    type(option_t), pointer :: comm
  contains
    procedure, public  :: register => cubedag_undo_register
    procedure, private :: parse    => cubedag_undo_parse
    procedure, private :: main     => cubedag_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 => cubedag_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 => cubedag_undo_do
  end type undo_prog_t
  !
contains
  !
  subroutine cubedag_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 cubedag_message(dagseve%trace,rname,'Welcome')
    !
    call undo%parse(line,user,error)
    if (error) return
    call undo%main(user,error)
    if (error) return
  end subroutine cubedag_undo_command
  !
  !---------------------------------------------------------------------
  !
  subroutine cubedag_undo_register(undo,error)
    !-------------------------------------------------------------------
    ! Register ADM\UNDO command and its options
    !-------------------------------------------------------------------
    class(undo_comm_t), intent(inout) :: undo
    logical,            intent(inout) :: error
    !
    type(standard_arg_t) :: stdarg
    character(len=*), parameter :: rname='UNDO>REGISTER'
    !
    call cubedag_message(dagseve%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_bl//&
         '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_bl//&
         'With a negative position as argument, undo the command &
         &according to its position from the end (0=last, -1=before last, &
         &etc).'&
         //strg_bl//&
         '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_bl//&
         'It is not possible to undo a command whose output products &
         &where used as inputs to other commands.',&
         cubedag_undo_command,&
         undo%comm,error)
    if (error) return
    call stdarg%register( &
         'COMMAND', &
         '[FirstCommandID]',  &
         strg_emp,&
         code_arg_optional, &
         error)
    if (error) return
    call stdarg%register( &
         'COMMAND', &
         '[LastCommandID]',  &
         strg_emp,&
         code_arg_optional, &
         error)
    if (error) return
    !
  end subroutine cubedag_undo_register
  !
  subroutine cubedag_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 cubedag_message(dagseve%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 cubedag_undo_parse
  !
  subroutine cubedag_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 cubedag_message(dagseve%trace,rname,'Welcome')
    !
    call user%toprog(prog,error)
    if (error) return
    call prog%do(error)
    if (error) return
  end subroutine cubedag_undo_main
  !
  subroutine cubedag_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 cubedag_message(dagseve%trace,rname,'Welcome')
    !
    ! Convert id's to their actual values (in particular negative
    ! ones, i.e. counted from the end)
    prog%firstid = cubedag_history_entrynum_hx(user%firstid,error)
    if (error)  return
    prog%lastid  = cubedag_history_entrynum_hx(user%lastid,error)
    if (error)  return
  end subroutine cubedag_undo_user_toprog
  !
  subroutine cubedag_undo_do(prog,error)
    !-------------------------------------------------------------------
    !
    !-------------------------------------------------------------------
    class(undo_prog_t), intent(in)    :: prog
    logical,            intent(inout) :: error
    !
    integer(kind=iden_l) :: iid
    !
    ! Only valid with >0 id's
    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 cubedag_undo_do
end module cubedag_undo
