/*
 * PROPRIETARY INFORMATION.  This software is proprietary to
 * Side Effects Software Inc., and is not to be reproduced,
 * transmitted, or disclosed in any way without written permission.
 *
 * Produced by:
 *	Side Effects
 *	477 Richmond Street West
 *	Toronto, Ontario
 *	Canada   M5V 3E7
 *	416-504-9876
 *
 * NAME:	TCL glue for CMD library (C++)
 *
 * COMMENTS:
 *
 * HISTORY:	$XRevision: 1.4 $
 */

#include <stdlib.h>
#include <malloc.h>
#include <strstream.h>
#include <tools/hpath.h>
#include <UT/UT_DSOVersion.h>
#include <UT/UT_Defines.h>
#include <CMD/CMD_Args.h>
#include <CMD/CMD_Manager.h>

static CMD_Manager	*theCommandManager;

static void
checkEnvironmentVariables()
{
    static char	*tcllib = 0;
    static char	*tklib = 0;
    char	 *hfs, work[1024];

    if (!getenv("TCL_LIBRARY"))
    {
    	if (!(hfs = getenv("HFS"))) hfs = "/hfs";
	strcpy(work, hfs);
	strcat(work, "/houdini/scripts/tcl");
	tcllib = (char *)malloc(strlen(work)+64);
	sprintf(tcllib, "TCL_LIBRARY=%s", work);
	putenv(tcllib);
    }

    if (!getenv("TK_LIBRARY"))
    {
    	if (!(hfs = getenv("HFS"))) hfs = "/hfs";
	strcpy(work, hfs);
	strcat(work, "/houdini/scripts/tk");
	tklib = (char *)malloc(strlen(work)+64);
	sprintf(tklib, "TK_LIBRARY=%s", work);
	putenv(tklib);
    }
}

//
//  First, we have all the TCL initialization stuff...
//	Here, we've modified the Tcl_Main() so that it has the option to
//	return control to the caller.  This is done via the "Tcl_SetQuitFlag"
//	function which simply makes sets the quitFlag variable.  The main
//	loop then breaks out, and sets frees the Tcl_Interp that it allocates
//	at the top.
//

//
// Since we're dealing with C++ here, we simply block the whole thing as
//	an extern "C" block.
//

extern "C" {

#include "tcl.h"
#include "tk.h"
#include "tkInt.h"

extern Tcl_HscriptCmd(ClientData, Tcl_Interp *, int, char **);
extern Tcl_HscriptExit(ClientData, Tcl_Interp *, int, char **);

static int	fromTk = 0;

int
Tcl_AppInit(Tcl_Interp *interp)
{
    int		ok;
    /*
     * Call the init procedures for included packages.  Each call should
     * look like this:
     *
     * if (Mod_Init(interp) == TCL_ERROR) {
     *     return TCL_ERROR;
     * }
     *
     * where "Mod" is the name of the module.
     */

    ok = 1;
    if (Tcl_Init(interp) == TCL_ERROR)
	ok = 0;

    if (fromTk)
    {
	if (Tk_Init(interp) == TCL_ERROR)
	    ok = 0;
	// If Tk initialization fails, then we still have to add some commands
    }

    /*
     * Create additional commands and math functions for testing Tcl.
     */

    Tcl_CreateCommand(interp, "hscript", Tcl_HscriptCmd, (ClientData) 0,
	    (Tcl_CmdDeleteProc *) NULL);
    Tcl_CreateCommand(interp, "exit", Tcl_HscriptExit, (ClientData) 0,
	    (Tcl_CmdDeleteProc *) NULL);

    /*
     * Specify a user-specific startup file to invoke if the application
     * is run interactively.  If this line is deleted then no user-specific
     * startup file will be run under any conditions.
     */

    tcl_RcFileName = (fromTk) ? "~/.wishrc" : "~/.tclshrc";
    return (ok) ? TCL_OK : TCL_ERROR;
}

//
//  Now, we define our own Exit command from tcl, so that control is returned
//	to Houdini.
//
int
Tcl_HscriptExit(ClientData, Tcl_Interp *interp, int argc, char **argv)
{
    int value;

    if ((argc != 1) && (argc != 2)) {
	Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
		" ?returnCode?\"", (char *) NULL);
	return TCL_ERROR;
    }
    if (argc == 1) {
	Tcl_SetQuitFlag(1);
    }
    else if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) {
	return TCL_ERROR;
    }
    if (fromTk)
    {
	while (tkMainWindowList != NULL) {
	    Tk_DestroyWindow((Tk_Window)tkMainWindowList->winPtr);
	}
    }
    Tcl_SetQuitFlag(1);
    return TCL_OK;
}

}	// End of "C" block

//
// Now, we can define our "glue" to the command manager
//

//
// Here is the "tcl" command for the command library.  Note that it passes
//	args down to the tcl interpreter so that sourcing of files can
//	be done.  Same for the "tk" command.
//

static char *
houdiniPathResolve(const char *filename)
{
    char	file[1024];

    if (!filename) return 0;
    if (*filename == '/' || !strncmp(filename, "./", 2)
			 || !strncmp(filename, "../", 3)) return 0;
    sprintf(file, "scripts/tk/%s", filename);
    return HoudiniFindFile(file, 0);
}

static void
cmd_tcl(CMD_Args &args)
{
    char	*argv[CMD_MAX_ARGS];
    char	*path;
    int		 i;

    for (i = 0; i < args.argc(); i++)
	argv[i] = args.argv(i);
    argv[i] = 0;

    // Do a houdini path search on the script file
    path = houdiniPathResolve(args.argv(1));
    if (path) argv[1] = path;

    fromTk = 0;
    Tcl_Main(args.argc(), argv, Tcl_AppInit);

    if (path) free(path);
}

static void
cmd_tk(CMD_Args &args)
{
    char	*argv[CMD_MAX_ARGS];
    char	*path;
    int		 i;

    for (i = 0; i < args.argc(); i++)
	argv[i] = args.argv(i);
    argv[i] = 0;

    // Do a houdini path search on the script file
    path = houdiniPathResolve(args.argv(1));
    if (path) argv[1] = path;

    fromTk = 1;
    Tk_Main(args.argc(), argv, Tcl_AppInit);

    if (path) free(path);
}

//
//  Here's the reverse glue.  The tcl command which allows CMD_Manager
//	commands to be run from tcl.
//
int
Tcl_HscriptCmd(ClientData, Tcl_Interp *interp, int argc, char **argv)
{
    CMD_Manager	*cmd;
    char	*pcmd, *nl;
    ostrstream	 out;

    cmd = theCommandManager;

    pcmd = Tcl_Merge(argc-1, argv+1);
    cmd->execute(pcmd, (int)0, &out, &out);
    out << ends;

    pcmd = out.str();
    if (pcmd)
    {
	nl = strrchr(pcmd, '\n');
	if (nl) *nl = 0;
	Tcl_AppendResult(interp, pcmd, NULL);
	delete [] pcmd;
    }
    return TCL_OK;
}

//
//  Now, here's where everything gets initializes.  The CMD library calls
//	this function as a DSO command.  Then everything gets linked in.
//	Note, that when we link this DSO, we have to link with the tcl/tk
//	library so that all symbols are resolved, otherwise, there's a DSO
//	error.
//

void
CMDextendLibrary(CMD_Manager *cman)
{
    // Install the "tcl" command into the command manager
    // It takes no options, it's help tag is "CMD_tcl" and the callback is
    //		cmd_tcl

    checkEnvironmentVariables();

    cman->installCommand("tcl",	"",	"CMD_tcl",	cmd_tcl);
    cman->installCommand("tk",	"",	"CMD_tk",	cmd_tk);

    theCommandManager = cman;		// Stash away for future use
}
