/*
 * Copyright 1995 TriloBYTE Software Solutions.
 *
 * Author: Amen Zwa, TriloBYTE.
 */



static char* sccsid = "%W%\t%G%\tAmen Zwa, TriloBYTE.";



/* imgclt.c: image client */



#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include "imgclt.h"



#define STRLEN		80

static int sd;				/* connection to image server */

static IDisconnFunc disconnfunc;	/* function called after disconnect */
static void* disconndata;		/* data passed to disconnect func */

static time_t tcconn;			/* connect time */

static int numbyte, numframe;		/* number of bytes and frames sent */

static char buf[STRLEN];



static void error(const char* s)
{
    fprintf(stderr, "imgclt: %s\n", s);
}



/* read n bytes; return number of bytes remaining, -1 if error */
static int readn(int fd, void* msg, int siz)
{
    char* tmp = (char*) msg;
    int numleft = siz, numread;
    while (numleft > 0) {
        numread = read(fd, tmp, numleft);
	if (numread == 0) break;	/* EOF */
	if (numread == -1) return -1;	/* error */
        numleft -= numread;
        tmp += numread;
    }
    return siz - numleft;
}



/* write n bytes; return number of bytes remaining, -1 if error */
static int writen(int fd, const void* msg, int siz)
{
    char* tmp = (char*) msg;
    int numleft = siz, numwrite;
    while (numleft > 0) {
        numwrite = write(fd, tmp, numleft);
        if (numwrite == -1) return -1;	/* error */
        numleft -= numwrite;
        tmp += numwrite;
    }
    return siz - numleft;
}



#if defined(BSD43)
#if defined(sun)
void sigpipe_handler(int, ...)
#elif defined(mips)
void sigpipe_handler()
#endif	/* sun, mips */
#elif defined(SVR4)
#if defined(sun)
void sigpipe_handler(int sig)
#elif defined(__sgi)
void sigpipe_handler()
#endif	/* sun, __sgi */
#endif	/* BSD43, SVR4 */
{
#if defined(TEST)
    error("Connection to image server terminated.");
#endif
    exit(1);
}



/* connect to image server; return 0 if ok, -1 if not */
int IConnect(const char* svrname, int svrport)
{
    struct hostent* hent;
    struct sockaddr_in svraddr;

    signal(SIGPIPE, sigpipe_handler);
    if (sd) return 0;

    /* get host entry */
    hent = gethostbyname(svrname);
    if (!hent) {
	sprintf(buf, "Cannot get host entry for server %s.", svrname);
	error(buf);
	return -1;
    }

    /* set server address */
    memset(&svraddr, 0, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_port = htons(svrport);
    svraddr.sin_addr.s_addr = ((struct in_addr*)(hent->h_addr))->s_addr;

    /* connect to server */
    sd = socket(AF_INET, SOCK_STREAM, 0);
    if (sd == -1) {
	error("Cannot create socket.");
	return -1;
    }
    if (connect(sd, (struct sockaddr*)&svraddr, sizeof(svraddr)) == -1) {
	sprintf(buf, "Cannot connect to server %s.", svrname);
	error(buf);
	return -1;
    }
/****
    if (fcntl(sd, F_SETFL, O_NONBLOCK) == -1) {
        error("Cannot set non-blocking on socket.");
	return -1;
    }
****/
    tcconn = time(0);
    numbyte = numframe = 0;
    return 0;
}



/* disconnect from image server; return 0 if ok, -1 if not */
int IDisconnect()
{
    int net;

    if (!sd) return -1;

    /* write I_Disconn request */
    net = htonl(I_Disconn);
    if (writen(sd, &net, sizeof(net)) == -1) {
	error("Cannot write I_Disconn request.");
	return -1;
    }
    if (shutdown(sd, 2) == -1) {
	error("Cannot shutdown connection to image server.");
	return -1;
    }
    if (close(sd) == -1) {
	error("Cannot close connection to image server.");
	return -1;
    }

    if (disconnfunc) disconnfunc(disconndata);
    return 0;
}



/* set function to be called after disconnect time; return previous function */
IDisconnFunc ISetDisconnFunc(IDisconnFunc func, void* usrdata)
{
    IDisconnFunc prvfunc = disconnfunc;
    disconnfunc = func;
    disconndata = usrdata;
    return prvfunc;
}



/* set image dimensions; return 0 if ok, -1 if not */
int IDimension(int width, int height)
{
    int net, msgsiz = 0;
    char msg[BUFSIZ], *m = msg;

    if (!sd) return -1;

    /* write I_ImgDim request */
    net = htonl(I_ImgDim);
    *((int*)m) = net;
    m += sizeof(I_ImgDim);

    /* write image width */
    net = htonl(width);
    *((int*)m) = net;
    m += sizeof(width);

    /* write image height */
    net = htonl(height);
    *((int*)m) = net;
    m += sizeof(height);

    msgsiz = m - msg;
    if (writen(sd, msg, msgsiz) == -1) {
	error("Cannot write I_ImgDim request.");
	return -1;
    }
    return 0;
}



/* update image with RGB color data; return 0 if ok, -1 if not */
int IColorRGB(const unsigned char* r, const unsigned char* g,
    const unsigned char* b, int size)
{
    int net, msgsiz = 0;
    static char* msg = 0;
    char* m;

    if (!sd) return -1;
    msgsiz = 2 * sizeof(int) + 3 * size;
    numbyte += msgsiz;
    numframe++;
    if (!msg) {
	msg = (char*) calloc(msgsiz, sizeof(char));
	if (!msg) {
	    error("Cannot allocate message buffer for RGB color values.");
	    return -1;
	}
    }
    m = msg;

    /* write I_ColRGB request */
    net = htonl(I_ColRGB);
    *((int*)m) = net;
    m += sizeof(I_ColRGB);

    /* write color vector size (image width * image height) */
    net = htonl(size);
    *((int*)m) = net;
    m += sizeof(size);

    /* write red data */
    memcpy(m, r, size * sizeof(unsigned char));
    m += size;

    /* write green data */
    memcpy(m, g, size * sizeof(unsigned char));
    m += size;

    /* write blue data */
    memcpy(m, b, size * sizeof(unsigned char));
    m += size;

    if (writen(sd, msg, msgsiz) == -1) {
	error("Cannot write I_ColRGB request.");
	return -1;
    }
    return 0;
}



/* update image with colormap index data; return 0 if ok, -1 if not */
int IColorIndex(const unsigned char* c, int size)
{
    int net, msgsiz = 0;
    static char *msg = 0;
    char *m;

    if (!sd) return -1;
    msgsiz = 2 * sizeof(int) + size;
    numbyte += msgsiz;
    numframe++;
    if (!msg) {
	msg = (char*) calloc(msgsiz, sizeof(char));
	if (!msg) {
	    error("Cannot allocate message buffer for RGB color values.");
	    return -1;
	}
    }
    m = msg;

    /* write I_ColMap request */
    net = htonl(I_ColMap);
    *((int*)m) = net;
    m += sizeof(I_ColMap);

    /* write index vector size (image width * image height) */
    net = htonl(size);
    *((int*)m) = net;
    m += sizeof(size);

    /* write color index data */
    memcpy(m, c, size * sizeof(unsigned char));
    m += size;

    if (writen(sd, msg, msgsiz) == -1) {
	error("Cannot write I_ColMap request.");
	return -1;
    }
    return 0;
}



/* get data rate (in KB/sec) */
float IGetDataRate(void)
{
    return (float)numbyte / (float)(time(0) - tcconn) / (float)1024;
}



/* get frame rate (in fps) */
float IGetFrameRate(void)
{
    return (float)numframe / (float)(time(0) - tcconn);
}
