"""Defines Finite State Machine (FSM), an object with distinct states
and update functions for each state (actions)."""

class FSM:
    """A simple FSM (Finite State Machine), a base class for most objects
    in the game.

    An FSM is characterized by states and state update functions (actions)
    defined for its states. Not all states have actions associated with them.
    Actions are stored in a dictionary and can be retrieved by state
    id (name).

    When FSM's update() function is called, it calls self.action(), where
    self.action is a pointer to the current action. Set State function of
    FSM updates this pointer depending on the current state.

    The basic FSM doesn't have any message handling.
    """

    def __init__(self, name="Anonymous"):
        self.name = name
        self.fsm_debug = 0
        self.currState = None
        self.timeInState = 0
        self.actions = {}     # a dictionary of actions for states
        self.action = None    # current action, called from update()

    def trace(self, arg):
        """A simplistic debug function"""
        if (self.fsm_debug):
            print self.name+": "+str(arg)

    def updateAction(self, timeInc = 1):
        """Calls current action function if it is set."""
        if (self.action!=None):
            self.action()
        else:
            self.trace("no action set")

    def update(self, timeInc = 1):
        """Updates the state and returns it as the result."""
        self.updateAction(timeInc)
        self.timeInState += timeInc
        return self.currState

    def setAction(self):
        """Looks for the action by key=self.currState, sets it if found."""
        if (self.actions.has_key(self.currState)):
            self.trace("action found, setting action")
            self.action = self.actions[self.currState]
        else:
            self.trace(("action not found: ",self.currState))
            self.action = None

    def setState(self, state):
        """Sets the current action function in addition to just changing
        the state variable. Returns 1 if the state was changed, 0 otherwise."""
        if (state != self.currState):
            self.timeInState = 0
            self.currState = state
            self.trace("state changed")
            self.setAction()
            return 1
        else:
            self.trace("the state didn't change: "+str(self.currState))
            return 0

#########################################################
# module test code
#########################################################

if (__name__=="__main__"):
    class HappyFSM(FSM):
        def __init__(self, ImHappyString, happyTime):
            FSM.__init__(self)
            self.ImHappyString = ImHappyString
            self.happyTime = happyTime
            self.actions["happy"] = self.Action_BeHappy
        def Action_BeHappy(self):
            if (self.timeInState < self.happyTime):
                print self.ImHappyString
            else:
                self.setState("idle")

    t = HappyFSM("I am a happy FSM!", 5)
    t.fsm_debug=1
    t.setState("idle")

    j = 0
    for i in range(3):
        print j
        j+=1
        t.update()
        print "state = ", t.currState, "time in state = ",t.timeInState
    t.setState("happy")
    for i in range(10):
        print j
        j+=1
        t.update()
        print "state = ", t.currState, "time in state = ",t.timeInState