#!/usr/bin/env python2
###########################################################################
#
# UndoRedoStack implements the usual undo/redo capabilities of a GUI
#
# Author: Orest Shardt
# Created: July 23, 2007
#
###########################################################################

class action:
  def __init__(self,act,inv):
    self.act = act
    self.inv = inv
  def undo(self):
    #print "Undo:",self
    self.inv()
  def redo(self):
    #print "Redo:",self
    self.act()
  def __str__(self):
    return "A generic action"

class beginActionGroup:
  pass

class endActionGroup:
  pass

class actionStack:
  def __init__(self):
    self.clear()

  def add(self,action):
    self.undoStack.append(action)
    #print "Added",action
    self.redoStack = []

  def undo(self):
    if len(self.undoStack) > 0:
      op = self.undoStack.pop()
      if op is beginActionGroup:
        level = 1
        self.redoStack.append(endActionGroup)
        while level > 0:
          op=self.undoStack.pop()
          if op is endActionGroup:
            level -= 1
            self.redoStack.append(beginActionGroup)
          elif op is beginActionGroup:
            level += 1
            self.redoStack.append(endActionGroup)
          else:
            op.undo()
            self.redoStack.append(op)
      elif op is endActionGroup:
        raise Exception,"endActionGroup without previous beginActionGroup"
      else:
        self.redoStack.append(op)
        op.undo()
      #print "undid",op
    else:
      pass #print "nothing to undo"

  def redo(self):
    if len(self.redoStack) > 0:
      op = self.redoStack.pop()
      if op is beginActionGroup:
        level = 1
        self.undoStack.append(endActionGroup)
        while level > 0:
          op = self.redoStack.pop()
          if op is endActionGroup:
            level -= 1
            self.undoStack.append(beginActionGroup)
          elif op is beginActionGroup:
            level += 1
            self.undoStack.append(endActionGroup)
          else:
            op.redo()
            self.undoStack.append(op)
      elif op is endActionGroup:
        raise Exception,"endActionGroup without previous beginActionGroup"
      else:
        self.undoStack.append(op)
        op.redo()
      #print "redid",op
    else:
      pass #print "nothing to redo"

  def setCommitLevel(self):
    self.commitLevel = len(self.undoStack)

  def changesMade(self):
    if len(self.undoStack) != self.commitLevel:
      return True
    else:
      return False

  def clear(self):
    self.redoStack = []
    self.undoStack = []
    self.commitLevel = 0

if __name__=='__main__':
  import sys
  def opq():
    print "action1"
  def unopq():
    print "inverse1"
  q = action(opq,unopq)
  w = action(lambda:sys.stdout.write("action2\n"),lambda:sys.stdout.write("inverse2\n"))
  e = action(lambda:sys.stdout.write("action3\n"),lambda:sys.stdout.write("inverse3\n"))
  s = actionStack()
  s.add(q)
  s.add(w)
  s.add(e)
