import Pmw

_ORIGINAL = 0
_MODIFIED = 1
_DISPLAY = 2

class HistoryText(Pmw.ScrolledText):

    def __init__(self, parent = None, **kw):

        # Define the megawidget options.
        optiondefs = (
            ('compressany',         1,          None),
            ('compresstail',        1,          None),
            ('historycommand',      None,       None),
        )
        self.defineoptions(kw, optiondefs)

        # Initialise the base class (after defining the options).
        Pmw.ScrolledText.__init__(self, parent)

        # Initialise instance variables.
        self._list = []
        self._currIndex = 0
        self._pastIndex = None
        self._lastIndex = 0          # pointer to end of history list

        # Check keywords and initialise options.
        self.initialiseoptions()

    def addhistory(self):
        text = self.get()
        if text[-1] == '\n':
            text = text[:-1]

        if len(self._list) == 0:
            # This is the first history entry.  Add it.
            self._list.append([text, text, _MODIFIED])
            return

        currentEntry =  self._list[self._currIndex]
        if text == currentEntry[_ORIGINAL]:
            # The current history entry has not been modified. Check if
            # we need to add it again.

            if self['compresstail'] and self._currIndex == self._lastIndex:
                return

            if self['compressany']:
                return

        # Undo any changes for the current history entry, since they
        # will now be available in the new entry.
        currentEntry[_MODIFIED] = currentEntry[_ORIGINAL]

        historycommand = self['historycommand']
        if self._currIndex == self._lastIndex:
            # The last history entry is currently being displayed,
            # so disable the special meaning of the 'Next' button.
            self._pastIndex = None
            nextState = 'disabled'
        else:
            # A previous history entry is currently being displayed,
            # so allow the 'Next' button to go to the entry after this one.
            self._pastIndex = self._currIndex
            nextState = 'normal'
        if callable(historycommand):
            historycommand('normal', nextState)

        # Create the new history entry.
        self._list.append([text, text, _MODIFIED])

        # Move the pointer into the history entry list to the end.
        self._lastIndex = self._lastIndex + 1
        self._currIndex = self._lastIndex

    def next(self):
        if self._currIndex == self._lastIndex and self._pastIndex is None:
            self.bell()
        else:
            self._modifyDisplay('next')

    def prev(self):
        self._pastIndex = None
        if self._currIndex == 0:
            self.bell()
        else:
            self._modifyDisplay('prev')

    def undo(self):
        if len(self._list) != 0:
            self._modifyDisplay('undo')

    def redo(self):
        if len(self._list) != 0:
            self._modifyDisplay('redo')

    def gethistory(self):
        return self._list

    def _modifyDisplay(self, command):
        # Modify the display to show either the next or previous
        # history entry (next, prev) or the original or modified
        # version of the current history entry (undo, redo).

        # Save the currently displayed text.
        currentText = self.get()
        if currentText[-1] == '\n':
            currentText = currentText[:-1]

        currentEntry =  self._list[self._currIndex]
        if currentEntry[_DISPLAY] == _MODIFIED:
            currentEntry[_MODIFIED] = currentText
        elif currentEntry[_ORIGINAL] != currentText:
            currentEntry[_MODIFIED] = currentText
            if command in ('next', 'prev'):
                currentEntry[_DISPLAY] = _MODIFIED

        if command in ('next', 'prev'):
            prevstate = 'normal'
            nextstate = 'normal'
            if command == 'next':
                if self._pastIndex is not None:
                    self._currIndex = self._pastIndex
                    self._pastIndex = None
                self._currIndex = self._currIndex + 1
                if self._currIndex == self._lastIndex:
                    nextstate = 'disabled'
            elif command == 'prev':
                self._currIndex = self._currIndex - 1
                if self._currIndex == 0:
                    prevstate = 'disabled'
            historycommand = self['historycommand']
            if callable(historycommand):
                historycommand(prevstate, nextstate)
            currentEntry =  self._list[self._currIndex]
        else:
            if command == 'undo':
                currentEntry[_DISPLAY] = _ORIGINAL
            elif command == 'redo':
                currentEntry[_DISPLAY] = _MODIFIED

        # Display the new text.
        self.delete('1.0', 'end')
        self.insert('end', currentEntry[currentEntry[_DISPLAY]])
