# -*- test-case-name: vertex.test.test_statemachine -*-

NOTHING = 'nothing'             # be quiet (no output)

class StateError(Exception):
    """
    """

class StateMachine:

    initialState = None         # a str describing initial state

    states = None     # dict, mapping state to dict of str input: (str output,
                      # str new-state)


    def __init__(self, initialState=None):
        if initialState is None:
            initialState = self.initialState
        self.state = self.initialState

    def transition(self, oldstate, newstate, datum, *a, **kw):
        if oldstate == newstate:
            return
        # print hex(id(self)), 'Going from', oldstate, 'to', newstate, 'because', datum
        exitmeth = getattr(self, 'exit_%s' % (oldstate,), None)
        entermeth = getattr(self, 'enter_%s' % (newstate,), None)
        transmeth = getattr(self, 'transition_%s_to_%s' % (
                oldstate, newstate), None)
        for meth in exitmeth, entermeth, transmeth:
            if meth is not None:
                meth(*a, **kw)
        self.state = newstate

    def input(self, datum, *a, **kw):
        oldstate = self.state
        if datum == NOTHING:
            return
        try:
            output, newstate = self.states[self.state][datum]
        except KeyError:
            self.invalidInput(datum)
        else:
            OLDSTATE = self.state.upper()
            NEWSTATE = newstate.upper()
            DATUM = datum.upper()
            self.transition(OLDSTATE, NEWSTATE, DATUM, *a, **kw)
            self.output(output, *a, **kw)

    def output(self, datum, *a, **kw):
        foo = getattr(self, 'output_' + datum.upper(), None)
        if foo is not None:
            foo(*a, **kw)

    def invalidInput(self, datum):
        raise StateError("Invalid input in %r: %r" % (self.state, datum))
