/*
 * Provide a 'network attached device' for ASYN labs
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <epicsThread.h>
#include <epicsString.h>
#include <osiSock.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define BASE_PORT    24742

struct workerParms {
    char *clientName;
    int   fd;
};

static void
workerThread(void *a)
{
    struct workerParms *p = (struct workerParms *)a;
    FILE *fp;
    char cbuf[512];
    int l;
    double volts = 0;

    if ((fp = fdopen(p->fd, "r+")) == NULL) {
        fprintf(stderr, "Can't create stdio stream for %s\n", p->clientName);
        return;
    }
    for (;;) {
        fflush(fp);
        if (fgets(cbuf, sizeof cbuf, fp) == NULL) {
            if (ferror(fp))
                fprintf(stderr, "Error reading %s: %s\n", p->clientName, strerror(errno));
            break;
        }
        l = strlen(cbuf);
        if (l <= 1)
            continue;
        if (cbuf[l-1] != '\n') {
            int c;
            fprintf(fp, "ERROR -- Command too long.\n");
            while (((c = getc(fp)) != '\n') && (c != EOF))
                continue;
            continue;
        }
        cbuf[l-1] = '\0';
        if (epicsStrCaseCmp("*IDN?", cbuf) == 0) {
            fprintf(fp, "USPAS-2007 Network attached device.          This is a really long identification string to check for buffer overflow.\n");
        }
        else if (epicsStrCaseCmp("LOADAV?", cbuf) == 0) {
            double avg[3];
            if(getloadavg(avg, 3) != 3) {
                fprintf(fp, "ERROR -- Can't get load average.\n");
            }
            else {
                fprintf(fp, "%g %g %g\n", avg[0], avg[1], avg[2]);
            }
        }
        else if (epicsStrCaseCmp("CLIENT?", cbuf) == 0) {
            fprintf(fp, "%s\n", p->clientName);
        }
        else if (epicsStrCaseCmp("VOLTAGE?", cbuf) == 0) {
            fprintf(fp, "%g\n", volts);
        }
        else if (sscanf(cbuf, "VOLTAGE %lf", &volts) == 1) {
        }
        else {
            fprintf(fp, "BAD COMMAND \"%s\"\n", cbuf);
        }
    }
    fclose(fp);
    free(a);
}

int
main (int argc, char **argv)
{
    int s, s1;
    struct sockaddr_in myAddr, farAddr;
    int addrlen;
    epicsThreadId tid;
    int tnum = 0;

    s = socket (AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
        fprintf(stderr, "Can't create socket: %s\n", strerror (errno));
        return 1;
    }
    myAddr.sin_family = AF_INET;
    myAddr.sin_port = htons (BASE_PORT);
    myAddr.sin_addr.s_addr = INADDR_ANY;
    memset (myAddr.sin_zero, '\0', sizeof myAddr.sin_zero);
    if (bind (s, (struct sockaddr *)&myAddr, sizeof myAddr) < 0) {
        fprintf(stderr, "Can't bind socket: %s\n", strerror (errno));
        return 1;
    }
    if (listen (s, 2) < 0) {
        fprintf(stderr, "Can't listen on socket: %s\n", strerror (errno));
        return 1;
    }
    for (;;) {
        struct workerParms *p;
        char name[1000];
        addrlen = sizeof farAddr;
        s1 = accept (s, (struct sockaddr *)&farAddr, &addrlen);
        if (s1 < 0) {
            fprintf(stderr, "Can't accept connection: %s\n", strerror (errno));
            return 1;
        }
        sockAddrToA(&farAddr, name, sizeof name);
        p = malloc(sizeof *p);
        if (p == NULL) {
            fprintf(stderr, "Can't allocate memory: %s\n", strerror (errno));
            return 1;
        }
        p->fd = s1;
        p->clientName = epicsStrDup(name);
        sprintf(name, "Worker %9.9d", ++tnum);
        tid = epicsThreadCreate(name,
                                epicsThreadPriorityMedium,
                                epicsThreadGetStackSize(epicsThreadStackMedium),
                                workerThread,
                                p);
        if (tid == 0) {
            fprintf(stderr, "Can't create worked thread\n");
            close(s1);
        }
    }
}
