/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
 * Copyright 1989 Massachusetts Institute of Technology
 */
/*********************\
* 		      *
*  WOOL_OBJECT: Fsm  *
*  BODY		      *
* 		      *
\*********************/

#include "EXTERN.h"
#include <stdio.h>
#include <assert.h>
#include "wool.h"
#include "wl_number.h"
#include "wl_atom.h"
#include "wl_list.h"
#include "wl_func.h"
#include "gwm.h"
#include "wl_event.h"
#include "INTERN.h"
#include "wl_fsm.h"

WOOL_FsmState WLFsmState_copy();
WOOL_FsmState WLState_fp();
WOOL_StateArc WLArc_fp();

#define SAME_STATE  ((WOOL_Atom) -1)

/*
 * Constructor: wool_fsm_make callable from wool
 */

WOOL_Fsm
wool_fsm_make(argc, argv)
int    argc;
WOOL_FsmState *argv;
{
    WOOL_Fsm        fsm;
    int             i;

    for (i = 0; i < argc; i++) /* check args are all states */
	must_be(WLFsmState, argv[i], i);
    fsm = (WOOL_Fsm) Malloc(sizeof(struct _WOOL_Fsm) + Max(0,argc - 1) *
			    sizeof(WOOL_OBJECT));
    zrt_put(fsm);
    fsm -> type = WLFsm;
    fsm -> mask = 0;
    fsm -> user_mask = 0;
    fsm -> states_size = argc;
    for (i = 0; i < argc; i++) {
	fsm -> mask |= argv[i] -> mask;
	fsm -> user_mask |= argv[i] -> user_mask;
	increase_reference(fsm -> states[i] = WLFsmState_copy(argv[i]));
    }
    WLFsm_fix(fsm, argc, argv);
    return fsm;
}

/*
 * fixes the states to go to in the fsm
 */

WLFsm_fix(fsm, argc, argv)
WOOL_Fsm	fsm;
int		argc;
WOOL_FsmState   argv[];
{
    int    nstate, narc, i;

    for (nstate = 0; nstate < fsm -> states_size; nstate++)
	for (narc = 0; narc < fsm -> states[nstate] -> arcs_size; narc++)
	    if (fsm -> states[nstate] -> arcs[narc] -> state != SAME_STATE) {
		for (i = 0; i < argc; i++)
		    if (fsm -> states[nstate] -> arcs[narc] -> state -> c_val
			== (WOOL_OBJECT) ((argv[i]) -> copy_of)) {
			fsm -> states[nstate] -> arcs[narc] -> state =
			    (WOOL_Atom) i;
			break;
		    }
		if (i == argc)
		    wool_error("State %s not found!", fsm -> states[nstate]
			       -> arcs[narc] -> state -> p_name);
	    }
}

/*
 * WLFsm_print prints any object of this package.
 */

WOOL_OBJECT 
WLFsm_print(obj)
WOOL_Fsm     obj;
{
    wool_printf("{%s", WOOL_TYPE_P_NAME(obj -> type));
    wool_printf(" 0x%x}", obj);
    return (WOOL_OBJECT) obj;
}

#ifdef DEBUG
WOOL_Fsm
WLFsm_fp(obj)
    WOOL_Fsm     obj;
{
    int nstate;

    wool_printf("{%s", WOOL_TYPE_P_NAME(obj -> type));
    wool_printf(" 0x%x:\n", obj);
    for (nstate = 0; nstate < obj -> states_size; nstate++) {
	wool_printf("State %d:\n", nstate);
	WLState_fp(obj->states[nstate]);
    }
    wool_printf("}\n");
    return obj;
}

WOOL_FsmState
WLState_fp(obj)
WOOL_FsmState obj;
{
    int narc;
    for (narc = 0; narc < obj-> arcs_size; narc++) {
	wool_puts(" * ");
	WLArc_fp(obj->arcs[narc]);
	wool_puts("\n");
    }
    return obj;
}

WOOL_StateArc
WLArc_fp(obj)
WOOL_StateArc obj;
{
    wool_print(obj->event);
    wool_puts(" ");
    wool_print(obj->action);
    return obj;
}

#endif /*DEBUG*/


/* the main routine:
 * check the incoming event against all transitions of the current
 * state of the fsm, and triggers the action if necessary
 */

WOOL_OBJECT
WLFsm_action(fsm, wob, evt)
WOOL_Fsm	fsm;
Wob             wob;
Event           evt;
{
    int             i;
    WOOL_FsmState state;
    WOOL_StateArc arc;
    WOOL_List       l;
    int             local_zrt_size = zrt_size;

    TargetWob = wob;
    GWM_invalid_context = 1;
    state = fsm -> states[wob -> curstate];
    for (i = 0; i < state -> arcs_size; i++) {
	arc = state -> arcs[i];
	ASSERT(arc -> event -> match);
	if (evt -> type == arc -> event -> x_type &&
	    ((*(arc -> event -> match)) (arc -> event, evt))) {

	    TraceDo('f',
		    GWM_prettyprint_fsm_event(wob, evt, "MATCH",
					      wob -> curstate,
					      arc -> state));
	    if (l = arc -> action) {
		save_wool_error_resume_point();
		if (GWM_invalid_context)
		    SetTarget(wob);
		TriggeringEvent = evt;
		if (set_wool_error_resume_point()) {
		} else {
		    WOOL_send(WOOL_execute, (*(l -> list)), (*(l -> list), l));
		    zrt_gc(local_zrt_size);
		}
		restore_wool_error_resume_point();   
	    }
	    
	    if (arc -> event -> release)
		(*(arc -> event -> release)) (arc -> event, evt);
	    if (arc -> state != SAME_STATE)
	    	wob -> curstate = (int) arc -> state;

	    return NULL;
	}
    }
    TraceDo('f',
	    GWM_prettyprint_fsm_event(wob, evt, "REJECT",
				      wob -> curstate,
				      arc -> state));
    return NULL;
}
    
/*
 * Routine used to construct a state 
 * USAGE:
 * 	(state-make arc1 arc2 ... arcn)
 */

WOOL_FsmState
wool_fsm_state_make(argc, argv)
int 		argc;
WOOL_StateArc	argv[];
{
    WOOL_FsmState   state;
    int             i, j, narc = argc;
    WOOL_FsmState   inc_state;

    for (i = 0; i < argc; i++)
	if (argv[i] -> type == WLFsmState)
	    narc += ((WOOL_FsmState) argv[i]) -> arcs_size - 1;
	else if (argv[i] == (WOOL_StateArc) NIL)
	    narc--;
    state = (WOOL_FsmState)
	Malloc(sizeof(struct _WOOL_FsmState) +
	       Max(0, narc - 1) * sizeof(WOOL_OBJECT));
    state -> type = WLFsmState;
    state -> mask = state -> user_mask = 0;
    zrt_put(state);
    state -> arcs_size = narc;
    state -> copy_of = state;
    narc = 0;
    for (i = 0; i < argc; i++) {
	if (argv[i] -> type == WLStateArc) {
	    increase_reference(state -> arcs[narc++] = argv[i]);
	    state -> mask |= argv[i] -> event -> mask;
	    state -> user_mask |= argv[i] -> event -> user_mask;
	} else if (argv[i] -> type == WLFsmState) {
	    inc_state = (WOOL_FsmState) argv[i];
	    for (j = 0; j < inc_state -> arcs_size; j++) {
		increase_reference(state -> arcs[narc++]
				   = inc_state -> arcs[j]);
		state -> mask |= inc_state -> arcs[j] -> event -> mask;
		state -> user_mask |=
		    inc_state -> arcs[j] -> event -> user_mask;
	    }
	} else if (argv[i] == (WOOL_StateArc) NIL) 
	    ;				/* skip () */
        else
	    bad_argument(argv[i], i, "fsm state");
    }
    return state;
}

    
/*
 * make an arc: (FSUBR)
 * syntax: (on event [action [state]])
 * action (list) and state (atom) MUST NOT BE QUOTED!
 */

WOOL_StateArc
wool_state_arc_make(argc, argv)
int 		argc;
WOOL_OBJECT	argv[];
{
    WOOL_StateArc   arc = (WOOL_StateArc)
    Calloc(sizeof(struct _WOOL_StateArc),1);

    arc -> type = WLStateArc;
    zrt_put(arc);
    if (argc == 0 || argc > 3)
	wool_error(BAD_NUMBER_OF_ARGS, argc);
    arc -> state = SAME_STATE;
    arc -> action = NULL;
    increase_reference(arc -> event = (WOOL_Event)
		       wool_type_or_evaluate(argv[0], WLEvent));
    if (arc -> event == (WOOL_Event) NIL)
	bad_argument(argv[0], 0, "event");
    switch (argc) {
    case 3:
	must_be_atom(argv[2], 2);
	arc -> state = (WOOL_Atom) argv[2];
    case 2:
	must_be_or_nil(WLList, argv[1], 1);
	if (argv[1] != (WOOL_OBJECT) NIL)
	    increase_reference(arc -> action = (WOOL_List) argv[1]);
    }
    return arc;
}

WOOL_OBJECT
WLFsm_free(obj)
WOOL_Fsm obj;
{
    decrease_reference_in_list(obj -> states_size, obj -> states);
    Free(obj);
    return NULL;
}

WOOL_OBJECT
WLFsmState_free(obj)
WOOL_FsmState obj;
{
    decrease_reference_in_list(obj -> arcs_size, obj -> arcs);
    Free(obj);
    return NULL;
}

WOOL_OBJECT
WLStateArc_free(obj)
WOOL_StateArc obj;
{
    decrease_reference(obj -> event);
    decrease_reference(obj -> action);
    Free(obj);
    return NULL;
}

WOOL_OBJECT
WLFsm_open(fsm)
WOOL_Fsm fsm;
{
    return 0;
}

WOOL_StateArc
WLStateArc_copy(oldarc)
WOOL_StateArc oldarc;
{
    WOOL_StateArc   arc =
    (WOOL_StateArc) Malloc(sizeof(struct _WOOL_StateArc));

    arc -> type = WLStateArc;
    zrt_put(arc);
    increase_reference(arc -> event = oldarc -> event);
    if(arc -> action = oldarc -> action)
	increase_reference(arc -> action);
    if((arc -> state = oldarc -> state) != (WOOL_Atom) ANY)
	increase_reference(arc -> state);
    return arc;
}

WOOL_FsmState
WLFsmState_copy(oldstate)
WOOL_FsmState oldstate;
{
    WOOL_FsmState   state = (WOOL_FsmState)
    Malloc(sizeof(struct _WOOL_FsmState) + Max(0,oldstate -> arcs_size - 1)
	   * sizeof(WOOL_OBJECT));
    int             i;

    state -> type = WLFsmState;
    zrt_put(state);
    state -> mask = oldstate -> mask;
    state -> user_mask = oldstate -> user_mask;
    state -> arcs_size = oldstate -> arcs_size;
    state -> copy_of = oldstate -> copy_of;
    for (i = 0; i < state -> arcs_size; i++)
	increase_reference(state -> arcs[i] =
			   WLStateArc_copy(oldstate -> arcs[i]));
    return state;
}

/*
 * NIL_FSM_make:
 * used to build the nil fsm on startup
 */

NIL_FSM_make()
{
    WOOL_FsmState   state = (WOOL_FsmState)
    Malloc(sizeof(struct _WOOL_FsmState));

    NIL_FSM = (WOOL_Fsm) Malloc(sizeof(struct _WOOL_Fsm));
    state -> type = WLFsmState;
    state -> mask = state -> user_mask = 0;
    zrt_put(state);
    increase_reference(state);
    state -> arcs_size = 0;
    zrt_put(NIL_FSM);
    NIL_FSM -> type = WLFsm;
    NIL_FSM -> mask = 0;
    NIL_FSM -> user_mask = 0;
    NIL_FSM -> states_size = 1;
    NIL_FSM -> states[0] = state;
    increase_reference(NIL_FSM);
}

void
fix_fsm(ptr)
WOOL_Fsm	*ptr;
{
    if (*ptr == (WOOL_Fsm) NIL)
	increase_reference(*ptr = (WOOL_Fsm) NIL_FSM);
}
