/*
 * State machine class definition
 */

class StateMachine
{
	states = make_array (0);
	entry_hook = make_array (0);
	exit_hook = make_array (0);
	current_state;
	previous_state;
}

method StateMachine.StateEnterHook (newnum, trigger_sym)
{
	nil;
}

method StateMachine.StateExitHook (newnum, trigger_sym)
{
	nil;
}

method StateMachine.Trigger (trigger_sym)
{
	local		func;

	func = car (.states[.current_state]);
	if (func)
		funcall (eval(func), list (self, list (#quote, trigger_sym)));
}

method StateMachine.ChangeState (newnum, trigger_sym)
{
	local		prev;

	if (.current_state)
	{
		exit_state (self, .states[.current_state]);
		self.StateExitHook (newnum, trigger_sym);
		if (self.exit_hook[.current_state])
			eval(self.exit_hook[.current_state]) (self, newnum, trigger_sym);
	}
	.current_state = newnum;
	enter_state (self, .states[newnum]);
	self.StateEnterHook (newnum, trigger_sym);
	if (self.entry_hook[newnum])
		eval(self.entry_hook[newnum]) (self, newnum, trigger_sym);
	/* Should I pass nil here?  Should I call this at all? */
	/* self.Trigger (nil); */
}

method StateMachine.ChangeStateFrom (oldnum, newnum, trigger_sym)
{
	if (.current_state == oldnum)
		self.ChangeState (newnum, trigger_sym);
}

method StateMachine.AddState (statenum, func, entry_hook, exit_hook,
							  symbols...)
{
	local		newstate, args;
	for (; symbols; symbols = cdr(symbols))
		args = cons (list (#quote, car (symbols)), args);
	args = cons (list (#quote, func), args);
	newstate = funcall (create_state, args);
	.states[statenum] = newstate;
	.entry_hook[statenum] = entry_hook;
	.exit_hook[statenum] = exit_hook;
}

method StateMachine.Register ()
{
	for (i = length(.states) - 1; i >= 0; i = i - 1)
	{
		for (sym = cdr (.states[i]); sym; sym = cdr (sym))
		{
			set (car (sym), register_point (car (sym)));
		}
	}
}

/*
 * Take advantage of the fact that 'this' is defined for all exception,
 * set, echo and read functions.
 *
 * The function trigger_state_machine is an undefined reference which MUST
 * be declared.  It is used internally to by the state machine functions.
 */

function trigger_state_machine (state)
{
	state.previous_state = state.current_state;
	state.Trigger (this);
}
