/*
 *                         C M D Q U E U E . C
 *
 *  The communication with the server is asynchron.  Sent commands get in a
 *  FIFO queue.  The first command in the queue determines the destination
 *  of the incoming data.
 *
 *  Version      : $Revision: 1.7 $
 *
 *  Created      : Thu May 26 20:01:25 1994
 *  Author       : Ulrich Drepper <drepper@mydec>
 *
 *  Last modified: Fri Jul 15 21:15:49 1994
 *  Author       : Ulrich Drepper <drepper@mydec>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */
#if !defined(lint)
static const char *vcid = "$Id: cmdqueue.c,v 1.7 1994/07/15 21:49:14 drepper Exp $";
#endif /* lint */

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include <stdio.h>
#include <sys/file.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#if defined(sgi)
#include <fcntl.h>
#endif

#include "empire.h"
#include "proto.h"

#if defined(NEED_strerror)
char *strerror(int errnoval);
#endif /* NEED_strerror */

/*
 * local type definitions
 */
typedef struct _CmdQueue {
    int first;
    long serial;
    Bool (*fct)(Bool, Bool, char *, void *);
    void *data;
    struct _CmdQueue *next;
    struct _CmdQueue *prev;
} CmdQueue;

/*
 * prototypes for local variables
 */
static void dequeueCmd(void);

/*
 * exported variables
 */
CmdQueue *cmdQueue = NULL;
long lastSerial = -1;

/*
 * local variables
 */
static FILE *redirFP = NULL;

/*
 * exported functions
 */
Bool
enqueueCmd(long serial, Bool (*fct)(Bool, Bool, char *, void *), void *data)
{
    CmdQueue *new = (CmdQueue*)malloc(sizeof(CmdQueue));

    if (!new) {
	message(ERROR, "out of memory\ncould not send command");
	return False;
    }

    new->first  = True;
    new->serial = serial;
    new->fct    = fct;
    new->data   = data;
    if (cmdQueue) {
	new->next       = cmdQueue->next;
	new->prev       = cmdQueue;
	cmdQueue->next  = new;
	new->next->prev = new;
    } else {
	cmdQueue        = new;
	new->next       = new;
	new->prev       = new;

	if (connected) {
	    XtRemoveTimeOut(timerId);
	}
    }

    return True;
}

void
feedCommand(ServerAnswer answerCode, char *buf)
{
    Bool dequeue = True;
    
    assert(cmdQueue != NULL);

    if (!strncmp(buf, "Update in progress...command failed", 35)) {
	/* will not be processed */
	message(WARN, "update in progress...command failed");
	return;
    }

    switch (answerCode) {
    case C_PROMPT:
	if (redirFP) {
	    fclose(redirFP);
	    redirFP = NULL;
	}
	if (cmdQueue->fct) {
	    dequeue = cmdQueue->fct(cmdQueue->first, True, NULL,
				    cmdQueue->data);
	}
	if (dequeue) {
	    /* processXXX determines when the command has to be dequeued */
	    lastSerial = cmdQueue->serial;
	    dequeueCmd();
	}
	sscanf(buf, "%d %d", &restMin, &restBTU);
	showMessageLine();
	break;
    case C_DATA:
	/* filter telegram announcements */
	if (!strncmp(buf, "You have ", 9) &&
	    strstr(buf, "telegram") &&
	    strstr(buf, "waiting")) {
	    /* set flag for telegram and signal */
	} else {
	    if (redirFP) {
		fputs(buf, redirFP);
	    }
	    if (cmdQueue->fct) {
		cmdQueue->fct(cmdQueue->first, False, buf, cmdQueue->data);
	    } else {
		/* output to edit area */
#if XtSpecificationRelease<6 || defined(UseXaw3d)
		XawTextBlock block = { 0, strlen(buf), buf, FMT8BIT };
#else
		XawTextBlock block = { 0, strlen(buf), buf, XawFmt8Bit };
#endif
		currentEditAdd(True, &block, True);
	    }
	}
	cmdQueue->first = False;
	break;
    case C_FLUSH:
	if (cmdQueue->fct) {
	    /* ignore */
	} else {
#if XtSpecificationRelease<6 || defined(UseXaw3d)
		XawTextBlock block = { 0, strlen(buf), buf, FMT8BIT };
#else
		XawTextBlock block = { 0, strlen(buf), buf, XawFmt8Bit };
#endif
		currentEditAdd(True, &block, True);
		lastSerial = cmdQueue->serial;
		dequeueCmd();
	}
	break;
    case C_REDIR:
	{
	    char *how;
	    char *name;
	    int mode;
	    int fd;
	    
	    assert(redirFP == NULL);
	    how = buf++;
	    if (*buf == '>' || *buf == '!') buf++;
	    while (isspace(*buf)) buf++;
	    name = buf;
	    while (*buf && !isspace(*buf)) buf++;
	    *buf = '\0';
	    mode = O_WRONLY | O_CREAT;
	    if (how[1] == '>') {
		mode |= O_APPEND;
	    } else if (how[1] == '!') {
		mode |= O_TRUNC;
	    } else {
		mode |= O_EXCL;
	    }
	    if (!*name) {
		message(WARN, "Null file name after redirection");
	    } else {
		if ((fd = open(name, mode, 0600)) < 0) {
		    message(ERROR, "Redirect open failed: %s",
			    strerror(errno));
		} else {
		    redirFP = fdopen(fd, "w");
		}
	    }
	}
	break;
    default:
	if (cmdQueue->fct) {
	    assert(True == False);     /* should not happen */
	}
	cmdQueue->first = False;
	break;
    }
}


void
dequeueLastCmd(void)
{
    CmdQueue *q;

    assert(cmdQueue != NULL);

    q = cmdQueue->next;

    if (cmdQueue->next == cmdQueue) {
	cmdQueue = NULL;
    } else {
	cmdQueue->next = q->next;
	q->next->prev  = cmdQueue;
    }
    
    free(q);
}

/*
 * module local functions
 */
static void
dequeueCmd(void)
{
    CmdQueue *q;

    assert(cmdQueue != NULL);

    q = cmdQueue;
    if (cmdQueue->next == cmdQueue) {
	cmdQueue = NULL;
    } else {
	cmdQueue = q->next->prev = q->prev;
	q->prev->next = q->next;
    }

    free(q);
}

/*
 * Local Variables:
 *  mode:c
 *  c-indent-level:4
 *  c-continued-statement-offset:4
 *  c-continued-brace-offset:0
 *  c-brace-offset:0
 *  c-imaginary-offset:0
 *  c-argdecl-indent:4
 *  c-label-offset:-2
 * End:
 */
