/*
 * t4stored.cpp --
 *
 *	This file implements a mechanism for defining stored procedures
 *	from vertices containing strings.
 *
 *	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 "t4graphrep.h"

/*
 * T4Graph_DefineStoredProcedure --
 *
 *	Define a new Tcl command named "cmdname" based on the string
 *	value of "v".
 *
 * Results:
 *	If successful, returns a T4CmdInfo * for the newly defined command.
 *
 * Side effects:
 *	May define a new Tcl procedure.
 */

T4CmdInfo *
T4Graph_DefineStoredProcedure(Tcl_Interp *interp,
			      char *cmdname,
			      T4Storage *s,
			      e4_Vertex v)
{
    const char *storedproc;
    Tcl_Obj *obj;
    int objc;
    Tcl_Obj **objv;
    char *buf, *argstr, *bodystr;
    Tcl_CmdInfo ci;
    T4CmdInfo *cmdInfo;

    /*
     * Vertex must have string value.
     */

    if (!v.Get(storedproc) || (storedproc == NULL)) {
	Tcl_AppendResult(interp, cmdname, ": cannot define", NULL);
	return NULL;
    }

    /*
     * Parse the stored procedure into args and body:
     */

    obj = Tcl_NewStringObj(storedproc, strlen(storedproc));
    if (Tcl_ListObjGetElements(interp, obj, &objc, &objv) == TCL_ERROR) {
	return NULL;
    }
    if (objc != 2) {
	Tcl_AppendResult(interp, cmdname, ": malformed stored procedure",
			 NULL);
	return NULL;
    }

    argstr = Tcl_GetString(objv[0]);
    bodystr = Tcl_GetString(objv[1]);

    /*
     * Modify the args to pass "this" as first argument. When called, the
     * first argument will be set to Tcl_Obj for the containing node.
     */

    buf = Tcl_Alloc(6 + strlen(argstr));
    sprintf(buf, "this %s", argstr);
    argstr = buf;

    /*
     * Now prepare a new command to create a proc. Then invoke it with
     * Tcl_Eval.
     */

    buf = Tcl_Alloc(4 + 1 + strlen(cmdname) + 
		    1 + 2 + strlen(argstr) +
		    1 + 2 + strlen(bodystr) + 1);
    sprintf(buf, "proc %s {%s} {%s}", cmdname, argstr, bodystr);
    if (Tcl_Eval(interp, buf) == TCL_ERROR) {
	return NULL;
    }
    Tcl_Free(buf);
    Tcl_Free(argstr);
    Tcl_DecrRefCount(obj);

    /*
     * Prepare a T4CmdInfo for the new command.
     */

    if (Tcl_GetCommandInfo(interp, cmdname, &ci) == 0) {
	return NULL;
    }
    cmdInfo = (T4CmdInfo *) Tcl_Alloc(sizeof(T4CmdInfo));
    cmdInfo->cmdName = cmdname;
    cmdInfo->objProc = ci.objProc;
    cmdInfo->objClientData = ci.objClientData;

    /*
     * Associate the cmdInfo with this vertex.
     */

    s->SetVertexStoredCmdInfo(interp, v, cmdInfo);

    Tcl_ResetResult(interp);

    return cmdInfo;
}
