/* 
 * $Id: client.c,v 1.3 91/05/03 03:02:47 qjb Exp Locker: qjb $
 * $Source: /afs/athena.mit.edu/astaff/project/qcs/src/RCS/client.c,v $
 * $Author: qjb $
 *
 */

#if !defined(lint) && !defined(SABER) || defined(RCS_HDRS)
static char *rcsid = "$Id: client.c,v 1.3 91/05/03 03:02:47 qjb Exp Locker: qjb $";
#endif /* !lint && !SABER || RCS_HDRS */

#include <stdio.h>
#include <curses.h>
#include <ctype.h>
#include <signal.h>
#include "string.h"
#include "qcs.h"

#include "sample.h"

#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define DEF_CMD_LINES 8

typedef struct win_data_rec {
    WINDOW *status;
    WINDOW *divider;
    WINDOW *command;
    char backspace;
    char killword;
    char killline;
} win_data;


static int quit(int s)
{
    endwin();
    exit(0);
    return (0);
}


static void init_display(win_data *windows)
{
    int statuslines;
    int commandlines;

    struct sgttyb tty;
    struct ltchars ltc;

    initscr();
    signal(SIGINT, quit);

    clear();
    refresh();
    
    ioctl(0, TIOCGETP, &tty);
    ioctl(0, TIOCGLTC, (struct sgttyb *)&ltc);
    windows->backspace = tty.sg_erase;
    windows->killline = tty.sg_kill;
    if (ltc.t_werasc == (char) -1)
	windows->killword = '\027'; /* CTRL-W */
    else
	windows->killword = ltc.t_werasc;

    scrollok(stdscr, FALSE);

    cbreak();
    noecho();

    if (LINES >= (2 * DEF_CMD_LINES + 1)) {
	statuslines = LINES - DEF_CMD_LINES - 1;
	commandlines = DEF_CMD_LINES;
    }
    else {
	statuslines = (LINES - 1) / 2;
	commandlines = LINES - 1 - statuslines;
    }

    windows->status = newwin(statuslines, COLS, 0, 0);
    windows->divider = newwin(1, COLS, statuslines, 0);
    windows->command = newwin(commandlines, COLS, statuslines + 1, 0);

    wclear(windows->status);
    wclear(windows->command);
    scrollok(windows->status, TRUE);
    scrollok(windows->command, TRUE);

    box(windows->divider, '-', '-');
    wrefresh(windows->divider);
}


static void do_backspace(WINDOW *win, char *cmd, char **p)
{
    if (*p > cmd) {
	int y, x;
	**p = '\0';
	(*p)--;
	**p = '\0';
	getyx(win, y, x);
	if (--x < 0) {
	    x = COLS-1;
	    y--;
	}
	wmove(win, y, x);
	waddch(win, ' ');
	wmove(win, y, x);
    }
}


/*
 * Read a character from win.  Return TRUE If a line has been read
 * (i.e., a newline had been entered) or false otherwise.  
 *
 * XXX Warning: This routine silently ignores unprintable characters
 * while reading input.  The kill-word and kill-line routines
 * implicitely assume this this is being done because they assume that
 * every character takes exactly one space on the screen.
 */
static qcs_bool read_char(win_data *windows, WINDOW *win, char *cmd) 
{
    char *p;
    int newline = FALSE;

    p = cmd + strlen(cmd);

    *p = wgetch(win);
    if (*p == windows->backspace) {
	*p = '\0';
	do_backspace(win, cmd, &p);
	wrefresh(windows->command);
    }
    else if (*p == windows->killword) {
	*p = '\0';
	int got_nonspace = FALSE;
	while (p > cmd) {
	    if (!isspace(*(p-1))) 
		got_nonspace = TRUE;
	    else {
		if (got_nonspace)
		    break;
	    }
	    do_backspace(win, cmd, &p);
	}
	wrefresh(windows->command);
    }
    else if (*p == windows->killline) {
	*p = '\0';
	while (p > cmd) {
	    *p = '\0';
	    do_backspace(win, cmd, &p);
	}
	wrefresh(windows->command);
    }
    else if (*p == '\n') {
	*p = '\0';
	newline = TRUE;
	waddch(win, '\n');
	wclrtoeol(win);
	wrefresh(win);
    }
    else if (*p == '\f') {
	*p = '\0';
	wrefresh(curscr);
    }
    else {
	int y, x;
	if (isprint(*p)) {
	    wprintw(win, "%c", *p);
	    getyx(win, y, x);
	    if (x == 0)
		wclrtoeol(win);
	    wrefresh(win);
	    p++;
	}
	else {
	    /* XXX Silently ignore unprintable characters */
	    *p = '\0';
	}
    }

    if (newline)
	return (TRUE);
    else
	return (FALSE);
}


qcs_error_t update_status(win_data *windows, char *prefix, char *string)
{
    int y, x;

    if (prefix)
	waddstr(windows->status, prefix);
    waddstr(windows->status, string);
    waddch(windows->status, '\n');
    wrefresh(windows->status);
    getyx(windows->command, y, x);
    wmove(windows->command, y, x);
    wrefresh(windows->command);
    return (QCS_SUCCESS);
}

qcs_error_t handle_error(qcs_t *qcs, char *string, void *data)
{
    return (update_status(data, "ERROR: ", string));
}

qcs_error_t handle_notify(qcs_t *qcs, char *string, void *data)
{
    return (update_status(data, NULL, string));
}

qcs_error_t handle_message(qcs_t *qcs, char *string, void *data)
{
    return (update_status(data, NULL, string));
}

qcs_error_t handle_completion(qcs_t *qcs, qcs_jobno jobno, void *data)
{
    if (qcs->user_qrpc[jobno] == QCS_NO_USERQRPC)
	qcs->user_qrpc[jobno] = NULL;
    else if (qcs->user_qrpc[jobno])
	qrpc_destroy(&(qcs->user_qrpc[jobno]));
    else {
	sprintf(qcs->errmsg, "Internal consistency error: %s", 
		"null user_qrpc for completed job");
	handle_error(qcs, qcs->errmsg, data);
	qcs->errmsg[0] = '\0';
	return (QCS_FAILURE);
    }

    return (QCS_SUCCESS);
}

void prompt(WINDOW *win)
{
    wprintw(win, "qcs> ");
    wclrtoeol(win);
    wrefresh(win);
}

static qcs_bool null_cmd(char *command)
{
    qcs_bool nullcmd = TRUE;
    char *p;

    for (p = command; *p; p++) {
	if (isgraph(*p))
	    nullcmd = FALSE;
    }

    return (nullcmd);
}

qcs_error_t handle_input(qcs_t *qcs, void *userdata)
{
    win_data *windows;
    static char inbuf[BUFSIZ + 1];

    windows = (win_data *)userdata;

    if (read_char(windows, windows->command, inbuf)) {
	if (strlen(inbuf)) {
	    /* XXX This should be done by fixing command_to_argv */
	    if (strcmp(inbuf, "quit") == 0) {
		qcs_disconnect(qcs);
		qcs->done = TRUE;
		return (QCS_SUCCESS);
	    }
	    if (! null_cmd(inbuf)) {
		if (qcs_send_command(qcs, inbuf, userdata) != QCS_SUCCESS) {
		    handle_error(qcs, qcs->errmsg, userdata);
		    qcs->errmsg[0] = '\0';
		    return (QCS_SUCCESS);
		}
	    }
	    
	    bzero(inbuf, sizeof(inbuf));
	}
	prompt(windows->command);
    }

    return (QCS_SUCCESS);
}

qcs_error_t handle_userqrpc(qcs_t *qcs, void *data)
{
    win_data *windows;

    windows = (win_data *)data;
    wprintw(windows->status, "Userdata\n");
    wrefresh(windows->status);
    return (QCS_SUCCESS);
}

main(int argc, char *argv[])
{
    qcs_client_params params;
    qcs_t qcs;
    char *whoami;
    win_data windows;
    
    if (whoami = strrchr(argv[0], '/'))
	whoami++;
    else
	whoami = argv[0];

    bzero(&qcs, sizeof(qcs));

    qcs_setup_client_params(&params, "afs-cmd", "soup", "afs-cmd", 3708, 0,
			    handle_error, handle_notify, handle_message,
			    NULL, handle_input, handle_userqrpc,
			    handle_completion);
    
    if (qcs_connect(&qcs, &params) != QCS_SUCCESS) {
	fprintf(stderr, "%s: %s\n", whoami, qcs.errmsg);
	exit(1);
    }

    init_display(&windows);

    wprintw(windows.command, "Welcome to the sample qcs client.\n");
    wrefresh(windows.command);
    prompt(windows.command);

    if (qcs_client_mainloop(&qcs, &windows) != QCS_SUCCESS) {
	endwin();
	fflush(stdout);
	fprintf(stderr, "%s: %s\n", whoami, qcs.errmsg);
	exit(1);
    }

    endwin();
    exit(0);
}
