/*     
 *     x-kernel v3.3
 *
 *    Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 */

#ifndef SUNOS
#ifdef LINUX
#include <unistd.h>
#else
#include <sys/systeminfo.h>
#endif
#endif
#include <sys/types.h>
#include <time.h>
#include "xkernel.h"
#include "ip.h"
#include "swp.h"
#include "x_stdio.h"

#define argPrefix(str) ((!strncmp(arg, str, strlen(str))) && strlen(arg)>strlen(str))
#define argEq(str)     (!strcmp(arg, str))
#define HOST_TYPE      IPhost

#ifndef STR2HOST
#define STR2HOST str2ipHost
#endif

#define TIMES 3
#define DELAY 3

typedef struct {
    Sessn    lls;
    Msg      savedMsg;
    XkHandle clientPushResult;
    int      done;
    int      sentMsgLength;
    XTime    starttime, endtime;
} PState;

static HOST_TYPE ServerAddr = { 0, 0, 0, 0 };
static HOST_TYPE ClientAddr = { 0, 0, 0, 0 };
static HOST_TYPE myHost;
static SWPport  serverPort = 1789;
static SWPport  clientPort = 1865;

static int  serverParam, clientParam;
static char *serverString;

/* Sizes of messages sent, in bytes */
#define NUM_TESTS 1
static int size[] = { 1024 };
static int trips = 100;

#ifndef SW_SIZE
#define SW_SIZE 15
#endif

int traceswptestp;

static void     client(Event, void *);
static void     server(Event, void *);
static int      runTest(Protl, int);
static XkReturn serverDemux(Protl, Sessn, Msg *);
static void     processOptions(void);
static XkReturn closeDone(Sessn);
static XkReturn customOpenDone(Protl, Protl, Sessn, Protl);

int swptest_init(Protl);

int swptest_init(Protl self)
{
    Protl  llp;
    PState *ps;
    char   buf[256];
    time_t t = time(0);

    processOptions();
    llp = xGetProtlDown(self, 0);
    if (!xIsProtl(llp))
        Kabort("Test protocol has no lower protocol");
    xControlProtl(xGetProtlDown(self, 0), GETMYHOST, (char *)&myHost,
		  sizeof(HOST_TYPE));
    ps = X_NEW(PState);
    bzero((char *)ps, sizeof(PState));
    self->state = (VOID *)ps;

#if defined(SUNOS) || defined(LINUX)
    gethostname(buf, 255);
#else
    sysinfo(SI_HOSTNAME, buf, 255);
#endif
    printf("Protocol: SWP\n");
    printf("Time: %s", ctime(&t));
    printf("Host: %s\n", buf);
    if (serverParam) {
        printf("Participant: server\n");
        evDetach(evSchedule(server, self, 0));
    }
    else if (clientParam) {
        printf("Participant: client\n");
	printf("Round Trips: %d \n", trips);
        STR2HOST(&ServerAddr, serverString);
        ClientAddr = myHost;
        evDetach(evSchedule(client, self, 0));
    }
    return 0;
}

static void
processOptions()
{
    int  i;
    char *arg;

    for (i = 1; i < globalArgc; i++) {
        arg = globalArgv[i];
        if (argEq("-s"))
	    serverParam = 1;
        else if (argPrefix("-c")) {
	    clientParam = 1;
	    serverString = arg + 2;
	}
        else if (argEq("-c")) {
	    clientParam = 1;
	    serverString = globalArgv[i+1];
	    i++;
        }
        else if (argPrefix("-trips="))
	    sscanf(arg + strlen("-trips="), "%d", &trips);
        else if (argEq ("-trips")) {
	    sscanf(globalArgv[i+1], "%d", &trips);
	    i++;
	}
    }
}

/*
 *  Server startup routine
 */
static void
server(Event ev, VOID *foo)
{
    Part   p;
    Protl  myProtl = (Protl)foo;

    xAssert(xIsProtl(myProtl));

    /* Define protocol methods */
    myProtl->demux = serverDemux;
    myProtl->opendone = customOpenDone;
    myProtl->closedone = closeDone;

    partInit(&p, 1);
    partPush(p, ANY_HOST, 0);
    partPush(p, &serverPort, sizeof(SWPport));

    if (xOpenEnable(myProtl, myProtl, xGetProtlDown(myProtl, 0), &p) ==
	XK_FAILURE) {
        printf("can't openenable lower protocol\n");
    }
}

/*
 * Client initialization routine
 */
static void
client(Event ev, VOID *foo)
{
    Protl  myProtl = (Protl) foo;
    PState *ps;
    int    i;
    Part   p[2];
    SWPsws win_size;
    int    retval;

    xAssert(xIsProtl(myProtl));
    ps = (PState *) myProtl->state;

    /* Define protocol methods */
    /* (we need no methods) */

    partInit(p, 2);
    partPush(p[0], &ServerAddr, sizeof(IPhost));
    partPush(p[0], &serverPort, sizeof(SWPport));
    partPush(p[1], ANY_HOST, 0);
    partPush(p[1], &clientPort, sizeof(SWPport));

    ps->lls = xOpen(myProtl, myProtl, xGetProtlDown(myProtl, 0), p);
    if (ps->lls == ERR_SESSN) {
        printf("open failed!\n");
        Kabort("End of test");
    }

    printf("Window Size: %d\n", SW_SIZE);
    win_size = SW_SIZE;
    xControlSessn(ps->lls, SWP_SET_SWS, (char*) &win_size, sizeof(win_size));
    for (i = 0; i < NUM_TESTS; i++)
        runTest(myProtl, size[i]);

    xClose(ps->lls);
    printf("End of test\n");
}

static
#if defined(__GNUC__)
inline
#endif
void checkHandle(h, str)
XkHandle h;
char     *str;
{
    switch (h) {
        case XMSG_ERR_HANDLE:
        case XMSG_ERR_WOULDBLOCK:
	    sprintf(errBuf, "%s returns error handle %d", str, h);
	    Kabort(errBuf);
        default:
	  break;
    }
}

int runTest(self, len)
Protl self;
int   len;
{
    static int overall_msg_num = 0;
    Msg    msg;
    PState *ps = (PState *)self->state;
    XTime  total;
    int    test;
    int    count;

    xAssert(ps);
    msgConstructAllocate(&ps->savedMsg, len);
    ps->sentMsgLength = len;

    printf("\n\nMessage Length (bytes): %d\n",len);
    printf("Times (sec):\n");
    for (test = 0; test < TIMES; test++) {
        ps->done = 0;
        xGetTime(&(ps->starttime));

	/*  Spin in a tight loop pushing messages - we need to push
	    (trips + WIN_SIZE) messages so that we know that (trips) messages
	    have been received at the server */

	for (count = 0; count < trips + SW_SIZE; count++)
	{
            msgConstructCopy(&msg, &ps->savedMsg);
	    /*  Can store overall message number into the message for
		server-side checking here */

	    ps->clientPushResult = xPush(ps->lls, &msg);
	    checkHandle(ps->clientPushResult, "client push");
	    msgDestroy(&msg);
	    ++overall_msg_num;
	}

	xGetTime(&(ps->endtime));
        xSubTime(&total, ps->endtime, ps->starttime);
        printf("%6d.%06d\n", total.sec, total.usec);

	/*  Wait for the window to clear out - DELAY seconds should be
	    sufficient */
        Delay(DELAY * 1000);
    }

    msgDestroy(&ps->savedMsg);
    return 0;
}

XkReturn
serverDemux(self, lls, dg)
Protl self;
Sessn lls;
Msg   *dg;
{
    PState *ps = (PState *)self->state;
    static unsigned int c = 0;

    xIfTrace(swptestp, TR_MAJOR_EVENTS) {
        putchar('.');
        if (!(c++ % 50))
	    putchar('\n');
    }

    /*	Can put message data check here (make sure packets are received
	in order */

    return XK_SUCCESS;
}

static XkReturn
closeDone(lls)
Sessn lls;
{
    xTrace1(swptestp, TR_MAJOR_EVENTS, "closedone (%x) called", lls);
    if (serverParam)
        xClose(lls);
    return XK_SUCCESS;
}

static XkReturn
customOpenDone(self, llp, s, hlpType)
Protl self, llp, hlpType;
Sessn s;
{
    xTrace0(swptestp, TR_MAJOR_EVENTS, "swp test program openDone");

    if (serverParam)
        xDuplicate(s);

    return XK_SUCCESS;
}

