/*
 * $RCSfile: asptest.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1996,1993,1991,1990  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 "asp.h"
#include "ip.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
#define FAILURE_LIMIT 5

typedef struct {
    Sessn    lls;
    Sessn    controlSessn;
    Msg      savedMsg;
    XkHandle clientPushResult;
    XkHandle serverPushResult;
    int      done, idle;
    int      clientReceived;
    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 ASPport   serverPort = 1789;
static ASPport   clientPort = 1865;

static int  serverParam, clientParam;
static char *serverString;

/* Sizes of messages sent, in bytes */
#define NUM_TESTS 10
static int size[] = { 1, 200, 400, 600, 800, 1024, 2048, 4096, 8192, 16384 };
static int trips = 100;

int traceasptestp;

static void     client(Event, void *);
static void     server(Event, void *);
static XkReturn clientDemux(Protl, Sessn, Msg *);
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 asptest_init(Protl);

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

    processOptions();
    llp = xGetProtlDown(self, 0);
    if (llp == ERR_PROTL)
        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: ASP\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;

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

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

    if (xOpenEnable(myProtl, myProtl, xGetProtlDown(myProtl, 0), &p) ==
	XK_FAILURE) {
        printf("server 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];

    ps = (PState *)myProtl->state;

    /* Define protocol methods */
    myProtl->demux = clientDemux;

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

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

    customOpenDone(myProtl, xGetProtlDown(myProtl, 0), ps->lls, myProtl);

    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;
{
    Msg    msg;
    PState *ps = (PState *)self->state;
    XTime  total;
    int    test, failures;

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

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

        msgConstructCopy(&msg, &ps->savedMsg);
        ps->clientPushResult = xPush(ps->lls, &msg);
        checkHandle(ps->clientPushResult, "client push");
        msgDestroy(&msg);

        /* Wait until test is completed. */
        do {
	    ps->idle = 1;
	    Delay(DELAY * 1000);
	    if (ps->idle) {
	        failures++;
	        if (failures >= FAILURE_LIMIT)
		    break;
	    }
	} while (!ps->done);

        if (!ps->done) {
	    printf("\nTest failed after %d trips... terminating\n\n",
		   ps->clientReceived);
	    continue;
	}

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

        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 int c = 1;

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

    /* Echo packet back to client */
    ps->serverPushResult = xPush(lls, dg);
    checkHandle(ps->serverPushResult, "server push");
    return XK_SUCCESS;
}

static XkReturn
clientDemux(self, lls, dg)
Protl self;
Sessn lls;
Msg   *dg;
{
    PState *ps = (PState *)self->state;
    Msg    tmpMsg;
    static int c = 1;

    ps->clientReceived++;
    ps->idle = 0;
    xIfTrace(asptestp, TR_MAJOR_EVENTS) {
        putchar('.');
        if (!(c++ % 50))
	    putchar('\n');
    }

    if (ps->clientReceived < trips) {
        /*
         * We need to construct a copy of the original message rather than
	 * just loop back the incoming message to avoid an increasingly
	 * fragmented message structure in the case of loopback.
         */

        msgConstructCopy(&tmpMsg, &ps->savedMsg);
        ps->clientPushResult = xPush(lls, &tmpMsg);
        checkHandle(ps->clientPushResult, "client push");
        msgDestroy(&tmpMsg);
    }
    else {
        xGetTime(&ps->endtime);
        ps->done = 1;
    }
    return XK_SUCCESS;
}

static XkReturn
closeDone(lls)
Sessn lls;
{
    xTrace1(asptestp, 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(asptestp, TR_MAJOR_EVENTS, "asp test program openDone");
    if (serverParam)
        xDuplicate(s);
    return XK_SUCCESS;
}
