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


class action:
   def __init__(self, actions):
       act, inv = actions
       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)