/* ----------------------------------------------------------------
 *   FILE
 *	ex_xdebug.c
 *
 *   DESCRIPTION
 *	routines to graphically demonstrate query execution
 *      (via communication with a separate X process)
 *
 *   INTERFACE ROUTINES
 *	ExecXInitialize   - initialize the X interface process
 *	ExecXNewTree	  - initialize the tree
 *	ExecXAddNode      - add a node to the tree
 *	ExecXSignalNode   - send a message to a node in the tree
 *	ExecXShowTree	  - map the viewport holding the tree
 *	ExecXHideTree	  - unmap the viewport with the tree
 *	ExecXTerminate	  - shut down the X interface process
 *
 *	x_at_init	  - hook to initialize the X process
 *	x_post_init	  - do nothing
 *	x_pre_proc	  \ hooks to highlight node being processed
 *	x_post_proc	  /
 *	x_pre_end	  - do nothing
 *	x_post_end	  - hook to terminate the X process
 *
 *	InitXHook	  - function to initialize a hook full of
 *			    the above X debugging routines.
 *
 *   NOTES
 *	This is a simple interface to an X program which
 *	takes simple commands from its standard input and displays
 *	the query tree interactively.  As the query is processed,
 *	the nodes in the tree are visually updated so the user
 *	sees the query execute.
 *
 *   IDENTIFICATION
 *	$Header: /data/01/postgres/src/backend/executor/RCS/ex_xdebug.c,v 1.3 1991/11/17 21:10:46 mer Exp $
 * ----------------------------------------------------------------
 */

#include "executor/executor.h"
 RcsId("$Header: /data/01/postgres/src/backend/executor/RCS/ex_xdebug.c,v 1.3 1991/11/17 21:10:46 mer Exp $");

/* ----------------
 *	global vars, etc.
 * ----------------
 */
bool ExecXInitialized = false;

char xexemon_buf[BUFSIZ];
FILE *xexemon_fd;
char *xexemon_path;

#define XEXEMON(x) \
    fprintf(xexemon_fd, x)

#define NODE_ENTER 1
#define NODE_LEAVE 2

/* ----------------------------------------------------------------
 *		     xexemon interface routines
 * ----------------------------------------------------------------
 */

/* ----------------
 *	ExecXInitialize forks the xexemon process
 * ----------------
 */
void
ExecXInitialize()
{
    if (ExecXInitialized)
	return;
    
    xexemon_path = (char *) getenv("PGXEXEMONPATH");
    if (xexemon_path == NULL) {
	xexemon_fd = (FILE *) NULL;
	return;
    }
    
    xexemon_fd = (FILE *) popen(xexemon_path, "w");

    /* ----------------
     *	to delay while X process starts..
     * ----------------
     */
    gets(xexemon_buf);
    
    ExecXInitialized = true;
}

/* ----------------
 *	ExecXNewTree
 * ----------------
 */
void
ExecXNewTree()
{
    if (xexemon_fd == NULL)
	return;
    XEXEMON("N;");
}

/* ----------------
 *	ExecXAddNode
 * ----------------
 */
void
ExecXAddNode(base_id, title, supernode_id)
    int		base_id;
    char 	*title;
    int 	supernode_id;
{
    if (xexemon_fd == NULL)
	return;

    sprintf(xexemon_buf, "A%d,%s,%d;", base_id, title, supernode_id);
    XEXEMON(xexemon_buf);
}

/* ----------------
 *	ExecXSignalNode
 * ----------------
 */
void
ExecXSignalNode(base_id, signal)
    int		base_id;
    int		signal;
{
    if (xexemon_fd == NULL)
	return;
    
    /* XEXEMON("Sname,code,parameters;"); */

    switch (signal) {
    case NODE_ENTER:
	sprintf(xexemon_buf, "S%d,B,3;", base_id);
	break;

    case NODE_LEAVE:
	sprintf(xexemon_buf, "S%d,B,1;", base_id);
	break;

    default:
	return;
    }
    
    XEXEMON(xexemon_buf);
}

/* ----------------
 *	ExecXShowTree
 * ----------------
 */
void
ExecXShowTree()
{
    if (xexemon_fd == NULL)
	return;
    XEXEMON("M;");
}

/* ----------------
 *	ExecXHideTree
 * ----------------
 */
void
ExecXHideTree()
{
    if (xexemon_fd == NULL)
	return;
    XEXEMON("U;");
}

/* ----------------
 *	ExecXTerminate
 * ----------------
 */
void
ExecXTerminate()
{
    if (xexemon_fd == NULL)
	return;
    
    XEXEMON("Q;");

    /* ----------------
     *	reset the initialized flag and the file descriptor..
     * ----------------
     */
    ExecXInitialized = false;
    xexemon_fd = NULL;
}
 
/* ----------------------------------------------------------------
 *			X hook functions
 * ----------------------------------------------------------------
 */

void
x_at_init(node, state)
    Plan 	node;		/* plan node */
    BaseNode 	state;		/* node's private state */
{
    int  	base_id = 	get_base_id(state);
    Plan	base_parent = 	(Plan) get_base_parent(state);
    BaseNode 	parentstate;		
    int  	parent_id;
    
    if (base_parent == NULL) {
	ExecXInitialize();
	parent_id = 0;
    } else {
	parentstate = (BaseNode) get_base_parent_state(state);
	parent_id = get_base_id(parentstate);
    }
    
    ExecXAddNode(base_id, (char *)GetNodeName((Node)node), parent_id);
}
    
void
x_post_init(node, state)
    Plan node;			/* plan node */
    BaseNode state;		/* node's private state */
{
}

void
x_pre_proc(node, state)
    Plan node;			/* plan node */
    BaseNode state;		/* node's private state */
{
    int  	base_id = 	get_base_id(state);
    Plan 	base_parent = 	(Plan) get_base_parent(state);
    BaseNode 	parentstate;		
    int  	parent_id;

    /* ----------------
     *	"leave" the parent node and "enter" this node
     * ----------------
     */
    if (base_parent != NULL) {
	parentstate = (BaseNode) get_base_parent_state(state);
	parent_id = get_base_id(parentstate);
	ExecXSignalNode(parent_id, NODE_LEAVE);
    }
	
    ExecXSignalNode(base_id,   NODE_ENTER);
}

void
x_post_proc(node, state)
    Plan node;			/* plan node */
    BaseNode state;		/* node's private state */
{
    int  	base_id = 	get_base_id(state);
    Plan 	base_parent = 	(Plan) get_base_parent(state);
    BaseNode 	parentstate;		
    int  	parent_id;

    /* ----------------
     *	"leave" this node and "enter" the parent node
     * ----------------
     */
    ExecXSignalNode(base_id,   NODE_LEAVE);

    if (base_parent != NULL) {
	parentstate = (BaseNode) get_base_parent_state(state);
	parent_id = get_base_id(parentstate);
	ExecXSignalNode(parent_id, NODE_LEAVE);
    }
}
  
void
x_pre_end(node, state)
    Plan node;			/* plan node */
    BaseNode state;		/* node's private state */
{
}

void
x_post_end(node, state)
    Plan node;			/* plan node */
    BaseNode state;		/* node's private state */
{
    Plan base_parent = 	(Plan) get_base_parent(state);

    /* ----------------
     *	terminate the X process when we get all the way back
     *  to the root of the tree..
     * ----------------
     */
    if (base_parent == NULL)
	ExecXTerminate();
}
 
/* ----------------
 *	InitXHook assigns the X hook routines to the given hook.
 * ----------------
 */
void
InitXHook(hook)
    HookNode hook;
{
    set_hook_at_initnode(hook,   x_at_init);
    set_hook_pre_procnode(hook,  x_pre_proc);
    set_hook_pre_endnode(hook,   x_pre_end);
    set_hook_post_initnode(hook, x_post_init);
    set_hook_post_procnode(hook, x_post_proc);
    set_hook_post_endnode(hook,  x_post_end);
}
