/* ----------------------------------------------------------------
 *   FILE
 *	xexemon.c	postgres executor monitoring program
 *
 *   DESCRIPTION
 *	this is a simple interface to Doug Young's tree widget.
 *	The program creates a viewport widget containing a tree
 *	widget and then uses XAddInput() to direct input from
 *	stdin to input_callback(), a routine which collects the
 *	input and passes it to process_input, which does all
 *	the "real" work.
 *
 *   NOTES
 *	This code depends on the X11 athena toolkit.
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/support/RCS/xexemon.c,v 1.1 1990/08/26 16:48:17 cimarron Exp $
 * ----------------------------------------------------------------
 */
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/Viewport.h>
#include <X11/Xaw/Label.h>
#include "Tree.h"    

/* ----------------
 *	global variables
 * ----------------
 */
Widget  toplevel;
Widget  viewport;
Widget  tree;
Widget  root;

Arg     args[10];

/* ----------------
 *	problem	- subroutine to print an error message
 * ----------------
 */
void problem(m1, m2)
    char *m1;
    char *m2;
{
    fprintf(stderr, "%s [%s]\n", m1, m2);
}

/* ----------------
 *	strmake - subroutine to make a copy of a string
 * ----------------
 */
char  *strmake(str, len)
    char *str;
    int len;
{
    char *newstr;
    if (str == NULL) return NULL;
    if (len <= 0) len = strlen(str);
    
    newstr = (char *) malloc((unsigned) len+1);
    (void) strncpy(newstr, str, len);
    newstr[len] = (char) 0;
    return newstr;
}

/* ----------------
 *	pindex - subroutine to find the position of a char in a string
 * ----------------
 */
int pindex(s, c)
    char *s;
    char c;
{
    char *p = (char *) index(s, c);
    
    if (p == NULL)
       return -1;

    return p-s;
}

/* ----------------
 *	get_param - subroutine to pull a sequence of characters from
 *		    a string up to the first comma and return a pointer
 *		    to the first char after the comma.
 * ----------------
 */
char *get_param(cmd, return_param)
    char *cmd;
    char **return_param;
{
    int comma;
    
    comma = pindex(cmd, ',');
    if (comma == -1) {
	(*return_param) = NULL;
	return cmd;
    }

    if (comma == 0) {
	(*return_param) = NULL;
	return cmd + 1;
    }
    
    (*return_param) = strmake(cmd, comma);

    return (char *)
	cmd + comma + 1;
}


/* ----------------
 *	process_input - subroutine to take a command line, determine
 *			what kind of operation is requested, pull parameters
 *			from the command line via get_param() as necessary
 *			and finally call do the appropriate X magic.
 * ----------------
 */
void process_input(cmd)
    char *cmd;
{
    char *original_cmd = cmd;

    switch (*cmd++) {
	/* ----------------
	 *	ignore nulls
	 * ----------------
	 */
    case '\0':
	return;
	
	/* ----------------
	 *   Create a new tree
	 *
	 *   Format:	N;
	 * ----------------
	 */
    case 'N':
	if (root != NULL) {
	    /* ----------------
	     *	we only need to actually recreate the widget when
	     *  we already have some trees..
	     * ----------------
	     */
	    root = NULL;
	    XtDestroyWidget(tree);
	    tree = XtCreateManagedWidget("tree",
					 XstreeWidgetClass, 
					 viewport,
					 NULL,
					 0);
	}
	break;
	
	/* ----------------
	 *   Add a node to the tree:
	 *
	 *   Format:	Aname,label,super;
	 * ----------------
	 */
    case 'A':
	{
	    char *nodename;
	    char *supername;
	    char *label;
	    Widget supernode;

	    /* ----------------
	     *	get the name of the new node
	     * ----------------
	     */
	    cmd = get_param(cmd, &nodename);
	    if (nodename == NULL) {
		problem("node name is NULL", original_cmd);
		return;
	    }
	    
	    /* ----------------
	     *	get the label of the new node
	     * ----------------
	     */
	    cmd = get_param(cmd, &label);

	    /* ----------------
	     *	if this is the first node, then it becomes the root..
	     * ----------------
	     */
	    if (root == NULL) {
		XtSetArg(args[0], XtNlabel, label);
		root = XtCreateManagedWidget(nodename, labelWidgetClass,
					     tree, args, 1);
		free(nodename);
		free(label);
		return;
	    }

	    /* ----------------
	     *	get the name of the node's supernode and
	     *  find that node in the tree
	     * ----------------
	     */
	    supername = cmd; 
	    supernode = XtNameToWidget(tree, supername);
	    if (supernode == NULL) {
		problem("super does not exist", original_cmd);
		free(nodename);
		free(label);
		return;
	    }

	    /* ----------------
	     *	create a new widget "beneath" the supernode
	     *  and return.
	     * ----------------
	     */
	    XtSetArg(args[0], XtNsuperNode, supernode);
	    XtSetArg(args[1], XtNlabel, label);
	    XtCreateManagedWidget(nodename, labelWidgetClass,
				  tree, args, 2);
	    free(nodename);
	    free(label);
	    return;
	}
	break;
	
	/* ----------------
	 *   Send a signal to a node
	 *
	 *   Format:	Sname,code,parameters...;
	 * ----------------
	 */
    case 'S':
	{
	    char *nodename;
	    char *message;
	    Widget node;

	    /* ----------------
	     *	get the name of the new node
	     * ----------------
	     */
	    cmd = get_param(cmd, &nodename);
	    if (nodename == NULL) {
		problem("node name is NULL", original_cmd);
		return;
	    }
	    node = XtNameToWidget(tree, nodename);
	    if (node == NULL) {
		problem("node not found", original_cmd);
		return;
	    }

	    /* ----------------
	     *	get the signal code
	     * ----------------
	     */
	    cmd = get_param(cmd, &message);
	    switch (*message) {
		/* ----------------
		 *	T -- update node text
		 * ----------------
		 */
	    case 'T':
		{
		    XtSetArg(args[0], XtNlabel, cmd);
		    XtSetValues(node, args, 1);
		    break;
		}
		/* ----------------
		 *	B -- set the border width
		 * ----------------
		 */
	    case 'B':
		{
		    int width = atoi(cmd);
		    XtSetArg(args[0], XtNborderWidth, width);
		    XtSetValues(node, args, 1);
		}
		break;
	    default:
		problem("signal code not understood", original_cmd);
		break;
	    }
	    
	    free(message);
	    free(nodename);
	    return;
	}
   
	/* ----------------
	 *   Map the top level widget
	 *
	 *   Format:	M;
	 * ----------------
	 */
    case 'M':
	XtMapWidget(toplevel);
	return;
	
	/* ----------------
	 *   Unmap the top level widget
	 *
	 *   Format:	U;
	 * ----------------
	 */
    case 'U':
	XtUnmapWidget(toplevel);
	return;

	/* ----------------
	 *   Quit the process
	 *
	 *   Format:	Q;
	 * ----------------
	 */
    case 'Q':
	exit(0);
	
    default:
	problem("protocol error: ", cmd);
	break;
    }
}

/* ----------------
 *	input_callback is called whenever there is input on stdin.
 *	it moves characters from the callback's input buffer to the
 *	command buffer until a ';' is read.  Then the command buffer
 *	is processed and collection begins again..
 * ----------------
 */

char	buf1[BUFSIZ];
char	buf2[BUFSIZ];
int 	j = 0;

void input_callback(w, fd, id)
    Widget     	w;
    int	   	*fd;
    XtInputId  	*id;
{
    int		nbytes;
    int		i;

    nbytes = read(*fd, buf1, BUFSIZ);
    
    if (nbytes < BUFSIZ)
	buf1[ nbytes ] = '\0';

    for (i = 0; i < nbytes; i++) {
	if (buf1[i] == ';') {
	    buf2[j] = '\0';
	    process_input(buf2);
	    bzero(buf2, j);
	    j = 0;
	    while(buf1[++i] == '\n') /* ignore \n's right after the ; */
		;
	    i--;
	} else
	    buf2[j++] = buf1[i];
    }
}

/* ----------------
 *	main just creates the initial widgets and then enters
 *	the Xt event loop
 * ----------------
 */

main(ac, av)
    int ac;
    char **av;
{
    /* ----------------
     *	initialize the toolkit
     * ----------------
     */
    toplevel = XtInitialize(NULL,	/* shell widget name */
			    "Example",	/* application class name */
			    NULL,	/* resource options */
			    0,		/* number of resource options */
			    &ac,	/* command line arg count */
			    av);	/* command line argument list */

    /* ----------------
     * 	create a viewport widget to hold the tree
     * ----------------
     */
    XtSetArg(args[0], XtNwidth,      300);
    XtSetArg(args[1], XtNheight,     200);
    XtSetArg(args[2], XtNallowHoriz, TRUE);
    XtSetArg(args[3], XtNallowVert,  TRUE);
    
    viewport = XtCreateManagedWidget("viewport",
				     viewportWidgetClass,
				     toplevel,
				     args,
				     4);

    /* ----------------
     *	create the initial tree widget
     * ----------------
     */
    tree = XtCreateManagedWidget("tree",
				 XstreeWidgetClass, 
				 viewport,
				 NULL,
				 0);

    /* ----------------
     *	start off with no tree..
     * ----------------
     */
    root = (Widget) NULL;
    
    /* ----------------
     *	add the input callback to the viewport widget and
     *  realize everything.
     * ----------------
     */
    (void) XtAddInput(fileno(stdin), XtInputReadMask,
		input_callback, (caddr_t) viewport);
    
    XtRealizeWidget(toplevel);
    XtMainLoop();
}
