/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#include <stdio.h>
#include <varargs.h>

#include "../h/cdefs.h"
#include "../h/errorStack.h"
#include "../h/rciTask.h"
#include "../h/rciOptions.h"

extern ERROR_STACK_DESC *errorStack;

static ERROR_STACK_DESC *getErrorStack(td)
RCI_DESC  *td;
{
	if (td)
	 { return ((ERROR_STACK_DESC*)td->estk);
	 }
	else
	 { if (!errorStack)
	    { numErrors();
	    }
	   return (errorStack);
	 }
}

rciErrorMsg (va_alist)
va_dcl
{
	ERROR_STACK_DESC *estack;
	RCI_DESC *td;
	int code;
	char *format;
	va_list ap;
	int retval;

	va_start(ap);
	td = va_arg(ap, RCI_DESC*);
	code = va_arg(ap, int);
	format = va_arg(ap, char*);

	estack = getErrorStack(td);

	retval = verrMsg (estack, code, format, ap);
	va_end(ap);
	return (retval);
}

rciNumErrors (td)
RCI_DESC *td;
{
	ERROR_STACK_DESC *estack;

	estack = getErrorStack(td);
	return (estack->num);
}

char *rciGetErrorMsg (td, num)
RCI_DESC *td;
int num;
{
	ERROR_STACK_DESC *estack;
	 
	estack = getErrorStack(td);
	return ((char *)getErrMsg (estack, num));
}

rciGetErrorCode (td, num)
RCI_DESC *td;
int num;
{
	ERROR_STACK_DESC *estack;
	 
	estack = getErrorStack(td);
	return (getErrCode (estack, num));
}

rciSetErrorCode (td, num, value)
RCI_DESC *td;
int num;
int value;
{
	ERROR_STACK_DESC *estack;

	estack = getErrorStack(td);	 
	return (setErrCode (estack, num, value));
}

rciPopError (td)
RCI_DESC *td;
{
	ERROR_STACK_DESC *estack;
	 
	estack = getErrorStack(td);
	return (popErr (estack));
}

rciClearErrors (td)
RCI_DESC *td;
{
	ERROR_STACK_DESC *estack;
	 
	estack = getErrorStack(td);
	clearErrs (estack);
}

char *rciDumpErrorMsgs (td, buf, size)
RCI_DESC *td;
char *buf;
int size;
{
	ERROR_STACK_DESC *estack;

	estack = getErrorStack(td);
	return (dumpErrMsgs (estack, buf, size));
}

#define EMSG_BUF_SIZE	1024	/* XXX */

rciPrintErrors (td)
RCI_DESC *td;
{
	ERROR_STACK_DESC *estack;
	/* this function does NOT clear the error stack */

	char buf[EMSG_BUF_SIZE];
	int k;
	 
	estack = getErrorStack(td);
	dumpErrMsgs (estack, buf, EMSG_BUF_SIZE);
	rciPrintf ("%s", buf);
	if (estack->overflow)
	 { rciPrintf ("META-ERROR -- error stack overflow\n");
	 }
	return (1);
}

#if TEST

#include "../h/assignMem.h"

#define TESTMSG1	64
/* This format is assumed to result in a message TESTMSG1 bytes long */
char *testmsg1 =
"%5d 78901234567890123456789012345678901234567890123456789012\n";
#define TESTMSG2	61
/* This format is assumed to result in a message TESTMSG1 bytes long */
char *testmsg2 =
"%5d 789_123456789_123456789_123456789_123456789_123456789\n";

char *longmsg = 
"%5d 789012345678901234567890123456789012345678901234567890*\n\
______789012345678901234567890123456789012345678901234567890*\n\
______789012345678901234567890123456789012345678901234567890*\n";

char longbuf[1024];

loadError(td, arg)
RCI_DESC *td;
int arg;
{
	rciErrorMsg (NULL, arg, "%s %d\n", rciTaskName(td), arg);
}

main()
{
	RCI_DESC *td_foo, *td_bar;

	char *pool;
	int errsize;
	int bufsize;
	int maxerrs;
	int spaceLeft;
	int truncate;
	int count, k;

	/* fill the error stack error stack */

	rciSetOptions(EXIT_ON_ERROR);

	td_foo = rciCreate ("foo", 0x1);
	td_bar = rciCreate ("bar", 0x1);
	pool = ((ERROR_STACK_DESC*)td_foo->estk)->top - ERROR_STACK_SIZE;

	bufsize = ERROR_STACK_SIZE + (ALIGN_MEM(pool) - pool);
	for (k=0, count=0; count<=bufsize; k++)
	 { count += ((k & 1) ? TESTMSG2 : TESTMSG1) + sizeof(ERROR_DESC);
	 }
	count -= ((k & 1) ? TESTMSG2 : TESTMSG1) + sizeof(ERROR_DESC);
	maxerrs = k-1;
	spaceLeft = bufsize - count;

	if (maxerrs < 4)
	 { printf ("Test program trouble: maxerrors < 4\n");
	   return (1);
	 }
	for (k=0; k<maxerrs; k++)
	 { char *msg;
	   msg = (k & 1) ? testmsg2 : testmsg1;
	   if (!rciErrorMsg (td_foo, k, msg, k))
	    { printf ("ERROR(1) -- can't place test message %d\n", k);
	      return (1);
	    }
	 }
	/* now check each message */
	for (k=0; k<maxerrs; k++)
	 { if (!checkError (td_foo, k))
	    { printf ("ERROR(2) -- message %d doesn't check out\n", k);
	      return (1);
	    }
	 }
	/* now check all the messages */
	if (!checkDump(td_foo, maxerrs))
	 { printf ("ERROR(3) -- checkDump() failed\n");
	   return (1);
	 }
	/* check the number of errors */
	if (rciNumErrors(td_foo) != k)
	 { printf ("ERROR(4) -- rciNumErrors() returns %d\n", rciNumErrors(td_foo));
	   return (1);
	 }
	/* now try to push the limit */
	if (spaceLeft >= sizeof(ERROR_DESC))
	 { if (!rciErrorMsg (td_foo, k, testmsg1, k))
	    { printf ("ERROR(5) -- rciErrorMsg() failed\n");
	      return (1);
	    }
	   k++;
	   if (rciErrorMsg (td_foo, k, testmsg1, k))
	    { printf ("ERROR(6) -- rciErrorMsg() suceeded\n");
	      return (1);
	    }
	   if (!rciPopError(td_foo))
	    { printf ("ERROR(7) -- rciPopError() failed\n");
	      return (1);
	    }
	   k--;
	 }
	else
	 { if (rciErrorMsg (td_foo, k, testmsg1, k))
	    { printf ("ERROR(8) -- rciErrorMsg() suceeded\n");
	      return (1);
	    }
	 }
	/* now test the truncation mechanism. Delete one msg, and 
	   create an error message much longer than the previous ones */
	if (!rciPopError(td_foo))
	 { printf ("ERROR(9) -- rciPopError() failed\n");
	   return (1);
	 }
	k--;
	if (!rciErrorMsg (td_foo, k, longmsg, k))
	 { printf ("ERROR(10) -- rciErrorMsg() failed\n");
	   return (1);
	 }
	sprintf (longbuf, longmsg, k);
	/* message should have been truncated. check this */
	errsize = (((rciNumErrors(td_foo)-2) & 1) ? TESTMSG2 : TESTMSG1)
		  + sizeof(ERROR_DESC);
	truncate = spaceLeft + errsize - sizeof(ERROR_DESC);
	longbuf[truncate-1] = '\0';
printf("truncate=%d\n", truncate);
	if (strcmp (rciGetErrorMsg (td_foo, rciNumErrors(td_foo)-k-1), longbuf) != 0)
	 { printf ("ERROR(11) -- longbuf is different\n");
	   return (1);
	 }
	/* now pop three errors of the stack and test the integrity
	   of the whole things again */
	if (!(rciPopError(td_foo)) || !(rciPopError(td_foo)) || !(rciPopError(td_foo)))
	 { printf ("ERROR(12) -- couldn't pop errors\n");
	   return (1);
	 }
	for (k=0; k<rciNumErrors(td_foo); k++)
	 { if (!checkError (td_foo, k))
	    { printf ("ERROR(13) -- message %d doesn't check out\n", k);
	      return (1);
	    }
	 }
	if (!checkDump(td_foo, rciNumErrors(td_foo)))
	 { printf ("ERROR(14) -- checkDump() failed\n");
	   return (1);
	 }
	/* now pop the rest of everything */
	while (rciNumErrors(td_foo))
	 { if (!rciPopError(td_foo))
	    { printf ("ERROR(15) -- couldn't pop error\n");
	      return (1);
	    }
	 }
	if (rciPopError(td_foo))
	 { printf ("ERROR(16) -- pop error suceeded\n");
	   return (1);
	 }
	for (k=0; k<maxerrs; k++)
	 { char *msg;
	   msg = (k & 1) ? testmsg2 : testmsg1;
	   if (!rciErrorMsg (td_foo, k, msg, k))
	    { printf ("ERROR(17) -- can't place test message %d\n", k);
	      return (1);
	    }
	 }
	rciClearErrors(td_foo);
	for (k=0; k<maxerrs; k++)
	 { char *msg;
	   msg = (k & 1) ? testmsg2 : testmsg1;
	   if (!rciErrorMsg (td_foo, k, msg, k))
	    { printf ("ERROR(18) -- can't place test message %d\n", k);
	      return (1);
	    }
	 }
	rciPrintErrors(td_foo);

	/* now test with the regular error stack */

	rciErrorMsg (0, 123, "%d hi there", 11);
	rciClearErrors(td_foo);
	rciClearErrors(td_bar);

	if (rciNumErrors(0) != 1 ||
	    strcmp(rciGetErrorMsg(0, 0), "11 hi there") != 0 ||
	    rciGetErrorCode (0, 0) != 123)
	 { printf ("ERROR(19) -- can't place msg on default stack\n");
	   return (1);
	 }

	rciClearErrors(NULL);
	rciExecute (td_foo, loadError, 111, NULL);
	if (rciNumErrors(NULL) != 0 || 
	    rciNumErrors(td_bar) != 0 ||
	    rciNumErrors(td_foo) != 1 ||
	    strcmp(rciGetErrorMsg(td_foo, 0), "foo 111\n") != 0 ||
	    rciGetErrorCode (td_foo, 0) != 111)
	 { printf ("ERROR(20) -- can't place msg from task foo\n");
	   return (1);
	 }

	rciExecute (td_bar, loadError, 222, NULL);
	if (rciNumErrors(NULL) != 0 || 
	    rciNumErrors(td_bar) != 1 ||
	    rciNumErrors(td_bar) != 1 ||
	    strcmp(rciGetErrorMsg(td_bar, 0), "bar 222\n") != 0 ||
	    rciGetErrorCode (td_bar, 0) != 222)
	 { printf ("ERROR(21) -- can't place msg from task bar\n");
	   return (1);
	 }

	printf ("\nROUTINES OK\n\n");	
	return (0);
}

checkError (td, k)
RCI_DESC *td;
int k;
{
	char buf[1024];
	
	sprintf (buf, (k & 1) ? testmsg2 : testmsg1, k);
	if (strcmp (buf, rciGetErrorMsg (td, rciNumErrors(td)-k-1)) != 0)
	 { return (0);
	 }
	if (rciGetErrorCode (td, rciNumErrors(td)-k-1) != k)
	 { return (0);
	 }
	return (1);
}
	
checkDump (td, maxerrs)
RCI_DESC *td;
int maxerrs;
{
	int k;
	char buf[1024];
	char checkbuf[ERROR_STACK_SIZE*2];
	char dumpbuf[ERROR_STACK_SIZE*2];

	checkbuf[0] = '\0';

	for (k=maxerrs-1; k>=0; k--)
	 { sprintf (buf, (k & 1) ? testmsg2 : testmsg1, k);
	   strcat (checkbuf, buf);
	 }
	rciDumpErrorMsgs (td, dumpbuf, ERROR_STACK_SIZE*2);
	if (strcmp (checkbuf, dumpbuf) != 0)
	 { return (0);
	 }
	return (1);
}

#endif
