/*
 * t4event.cpp --
 *
 *	Implementation of the T4Graph command to manage user defined events.
 *
 *	Authors: Jacob Levy and Jean-Claude Wippler.
 *		 jyl@best.com	jcw@equi4.com
 *
 * Copyright (c) 2000-2003, JYL Software Inc.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE, EVEN IF
 * JYL SOFTWARE INC. IS MADE AWARE OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "genobject.h"
#include "t4graphrep.h"

/*
 * T4Graph_EventProc --
 *
 *	This procedure is called when "tgraph::event" is invoked.
 *	Syntax:
 *		tgraph::event define
 *		tgraph::event isdefined <eventtoken>
 *		tgraph::event undefine <eventtoken>
 *
 *	The first form defines a new event token and returns it.
 *	The second form checks whether an event token is defined.
 *	The third form undefines an event token previously returned
 *	    from "tgraph::event define".
 *
 * Results:
 *	A standard Tcl result. The interpreter result will contain a
 *	value appropriate for the subcommand invoked.
 *
 * Side effects:
 *	Depends on the subcommand invoked.
 */

static CONST84 char *subCommands[] = {
    (char *) "define",
    (char *) "isdefined",
    (char *) "undefine",
    (char *) "cause",
    (char *) NULL
};
typedef enum ESubCommands {
    EDefine = 0,
    EIsDefined,
    EUndefine,
    ECause
} ESubCommands;

int
T4Graph_EventProc(ClientData cd, Tcl_Interp *interp, int objc,
		  Tcl_Obj *CONST objv[])
{
    ESubCommands index;
    T4InternalRep *intrep;
    T4Kinds kind;
    e4_Storage s;
    e4_Node n;
    e4_Vertex v;
    int eventcode;

    /*
     * There must be at least two arguments.
     */

    if (objc < 2) {
	Tcl_WrongNumArgs(interp, 0, NULL, "tgraph::event cmd ?arg ...?");
	return TCL_ERROR;
    }

    /*
     * Identify the operation requested.
     */

    if (Tcl_GetIndexFromObj(interp, objv[1], (CONST84 char **) subCommands,
			    (char *) "cmd", 0, (int *) &index)
	!= TCL_OK) {
	return TCL_ERROR;
    }

    /*
     * Do the subcommand:
     */

    switch (index) {
    case EDefine:

	/*
	 * Define a new event code:
	 */

	if (objc != 2) {
	    Tcl_WrongNumArgs(interp, 0, NULL, "tgraph::event define");
	    return TCL_ERROR;
	}
	if (!e4_Storage::DefineEventCode(eventcode)) {
	    Tcl_AppendResult(interp, "cannot define new event", NULL);
	    return TCL_ERROR;
	}
	Tcl_SetIntObj(Tcl_GetObjResult(interp), eventcode);
	return TCL_OK;
    case EIsDefined:

	/*
	 * Check whether a given event code is defined:
	 */

	if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 0, NULL,
			     "tgraph::event isdefined eventtoken");
	    return TCL_ERROR;
	}
	if (Tcl_GetIntFromObj(interp, objv[2], &eventcode) == TCL_ERROR) {
	    return TCL_ERROR;
	}
	Tcl_SetBooleanObj(Tcl_GetObjResult(interp),
			  e4_Storage::IsEventCodeDefined(eventcode) ? 1 : 0);
	return TCL_OK;
    case EUndefine:

	/*
	 * Undefine an existing event code:
	 */

	if (objc != 3) {
	    Tcl_WrongNumArgs(interp, 0, NULL,
			     "tgraph::event undefine eventcode");
	    return TCL_ERROR;
	}
	if (Tcl_GetIntFromObj(interp, objv[2], &eventcode) == TCL_ERROR) {
	    return TCL_ERROR;
	}
	Tcl_SetBooleanObj(Tcl_GetObjResult(interp),
			  e4_Storage::UndefineEventCode(eventcode) ? 1 : 0);
	return TCL_OK;
    case ECause:

	/*
	 * Cause an event on a storage entity:
	 */

	if ((objc != 4) && (objc != 5)) {
	    Tcl_WrongNumArgs(interp, 0, NULL,
			     "tgraph::event cause eventcode ent ?userdata?");
	    return TCL_ERROR;
	}
	if (Tcl_GetIntFromObj(interp, objv[2], &eventcode) == TCL_ERROR) {
	    return TCL_ERROR;
	}
	intrep = (T4InternalRep *) GO_GetUncheckedInternalRep(objv[3]);
	if (intrep == NULL) {
	    Tcl_AppendResult(interp,
			     "incorrect third argument: must be storage, node",
			     ", or vertex", NULL);
	    return TCL_ERROR;
	}
	kind = intrep->KindIdentifier();
	switch (kind) {
	case T4GRAPH_ILLEGAL:
	    Tcl_AppendResult(interp,
			     "incorrect third argument: must be storage, node",
			     ", or vertex", NULL);
	    return TCL_ERROR;
	case T4GRAPH_STORAGE:
	    ((T4Storage *) intrep)->ExternalizeStorage(s);
	    if (s.CauseEvent(eventcode, s, (objc == 5) ? objv[4] : NULL)) {
		return TCL_OK;
	    }
	    Tcl_AppendResult(interp, "cannot cause event ", 
			     Tcl_GetString(objv[2]), " on ",
			     Tcl_GetString(objv[3]), NULL);
	    return TCL_ERROR;
	case T4GRAPH_NODE:
	    ((T4Node *) intrep)->ExternalizeNode(n);
	    if (!n.GetStorage(s)) {
		Tcl_AppendResult(interp, "cannot cause event ", 
				 Tcl_GetString(objv[2]), " on ",
				 Tcl_GetString(objv[3]), NULL);
		return TCL_ERROR;
	    }
	    if (s.CauseEvent(eventcode, n, (objc == 5) ? objv[4] : NULL)) {
		return TCL_OK;
	    }
	    Tcl_AppendResult(interp, "cannot cause event ", 
			     Tcl_GetString(objv[2]), " on ",
			     Tcl_GetString(objv[3]), NULL);
	    return TCL_ERROR;
	case T4GRAPH_VERTEX:
	    ((T4Vertex *) intrep)->ExternalizeVertex(v);
	    if (!v.GetStorage(s)) {
		Tcl_AppendResult(interp, "cannot cause event ", 
				 Tcl_GetString(objv[2]), " on ",
				 Tcl_GetString(objv[3]), NULL);
		return TCL_ERROR;
	    }
	    if (s.CauseEvent(eventcode, v, (objc == 5) ? objv[4] : NULL)) {
		return TCL_OK;
	    }
	    Tcl_AppendResult(interp, "cannot cause event ", 
			     Tcl_GetString(objv[2]), " on ",
			     Tcl_GetString(objv[3]), NULL);
	    return TCL_ERROR;
	}
    }
      
    Tcl_AppendResult(interp, "unreachable code! in tgraph::event handler",
		     NULL);
    return TCL_ERROR;
}
