/* For history stack: undo/redo
    Operations:
       add, last, previous, next, removeLast, inMiddle?
       if inMiddle?(self), then next add removes entries after current.
 */!!

inherit(OrderedCollection, #HistoryStack, #(inMiddle /* true (index) if not at end */
maxDepth /* if non-nil, trim at this depth */), 2, 0)!!

now(class(HistoryStack))!!

now(HistoryStack)!!

/* 06/20/92 - PUBLIC
   Add anObject to self.
   If inMiddle?(self), then trim off elements after inMiddle.
   If > maxDepth, then remove 1st (oldest) element.
*/
Def add(self, anObject | obj)
{
  /* flush elements after inMiddle */
  if inMiddle then
    do(over((inMiddle+1),lastElement),
      {using(ignoredIndex)
        if (obj := removeLast(self)) then
          destroyDataObj(obj);
        endif;  
      });
    inMiddle := nil ; /* now at end */
  endif ;
  
  /* maintain maxDepth invariant */
  if maxDepth cand (size(self) = maxDepth) then
    if (obj := removeFirst(self)) then
      destroyDataObj(obj);
    endif;
  endif ;
  add(self:ancestor, anObject);
}
!!

/* 06/20/92 - PUBLIC */
Def inMiddle?(self)
{ 
  ^inMiddle 
}
!!

/* 06/20/92 - PUBLIC */
Def maxDepth(self)
{ 
  ^maxDepth 
}
!!

/* 06/20/92 - PUBLIC
   The caller does not know if anObject has already been added to the end.
   If it is not, add it at the end, else ignore it. 
   Unlile add, does not trim stack.
*/
Def maybeAdd(self, anObject | obj)
{
  /* If no elements, we can always add it */
  if (size(self) = 0) then
    ^add(self, anObject)
  else
    if ( anObject <> last(self) ) then
      if maxDepth cand (size(self) = maxDepth) then /* maintain maxDepth invariant */
        if (obj := removeFirst(self)) then
          destroyDataObj(obj);
        endif;  
      endif ;
      ^add(self:ancestor, anObject);
    endif ;
  endif ;

  ^nil  /* did not add anObject; already have it */
}
!!

/* 06/20/92 - PUBLIC
   Return but do not remove the next element in the stack (or nil).
*/
Def next(self)
{ 
  if (size(self) = 0) then
    ^nil
  endif ;

  if inMiddle then
    inMiddle := (1 + inMiddle) ;
    if (inMiddle + 1) >= lastElement then
      inMiddle := nil ;  /* at end again */
      ^last(self) ;
    endif ;
    ^self:Object[inMiddle]
  endif;
  ^nil /* can't return object after end */

}!!

/* 06/20/92 - PUBLIC
   Return but do not remove the next element in the stack (or nil).
*/
Def previous(self | newInMiddle)
{ 
  if (size(self) = 0) then
    ^nil
  endif ;

  if not(inMiddle) then
    newInMiddle := inMiddle := lastElement ;
  endif ;
  
  inMiddle := (inMiddle - 1) ;
  if ( inMiddle  < firstElement ) then
    /* back off */
    if newInMiddle then
      inMiddle := nil ;
    else 
      inMiddle := (inMiddle + 1) ;
    endif ;
    ^nil /* can't give value before 1st */
  endif;
  ^self:Object[inMiddle];
}!!

/* 06/20/92 - PUBLIC */
Def setMaxDepth(self, newDepth | obj)
{
  maxDepth := newDepth;
  if maxDepth < size(self) then
    /* Trim off the extra elements */
    do(over( 0, (size(self) - maxDepth + 2)),
      {using(ignored)
        if (obj := removeFirst(self)) then
          destroyDataObj(obj);
        endif;  
      });
  endif ;
}
!!
