/* Written by Germano Caronni and Werner Almesberger */
/* (c) by G. Caronni in '94 */
/* This program is under the GNU Public License Version 2 */

#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>

#include "dic.h"
#include "host.h"

typedef struct _client {
    int port;
    int got,did;
    struct _client *next;
    void *client_field;
} CLIENT;

#ifndef USE_PIPE


typedef void *HASH[256];

extern int debug;
extern double space,total;

static CLIENT *clients = NULL;
static int num_cli = 0,done = 0;


static void status(struct sockaddr_in *addr,CLIENT *client)
{
    printf("%4d:%5d: %8X.%-5d %4d/%4d %14.0f/%14.0f=%6.2f    %s",num_cli,done,
      (unsigned int)addr->sin_addr.s_addr,client->port,
      client->did,client->got,total,space,
      total == 0.0 ? 0.0 : total/space*100.0, debug?"\n":"\r");
    fflush(stdout);
}


static void **follow(CLIENT **start, unsigned char byte)
{
    assert(start);
    if (*start == NULL) return NULL;
    /*return (void **) ((int) start+(byte*sizeof(void *))); WERNER!!!!!????*/
    return (void **) ((int) (*start)+(byte*sizeof(void *)));
    /* return (void **) &(*start)[byte]; */
}


static void **create(CLIENT **start, unsigned char byte)
{
    assert(start);
    if (*start == NULL) {
	if ((*start = (CLIENT *) malloc(256*sizeof(void *))) == NULL)
	    PDIE("malloc");
	memset(*start,0,256*sizeof(void *));
    }
    /*return (void **) ((int) start+(byte*sizeof(void *))); werner version*/
    return (void **) ((int) (*start)+(byte*sizeof(void *)));
    /* return (void **) &(((k) (*start))[byte]); */
}


void **client_register(void *addr)
{
    CLIENT **start;
    unsigned char *a;
    int n;
    struct sockaddr_in *ad=(struct sockaddr_in *)addr;

    a = (unsigned char *) &ad->sin_addr.s_addr;
    start = &clients;

    for (n = 0; n < 4; n++) {
	start = (CLIENT **) create(start,*a);
	a++;
    }
    while (*start) {
	if ((*start)->port == ntohs(ad->sin_port)) 
		return &(*start)->client_field;
	start = &(*start)->next;
    }

    if ((*start = (CLIENT *) malloc(sizeof(CLIENT))) == NULL) PDIE("malloc");
    (*start)->port = ntohs(ad->sin_port);
    (*start)->got = (*start)->did = 0;
    (*start)->next = NULL;
    (*start)->client_field=(void **)0;
    num_cli++;
    return &(*start)->client_field;
}


static CLIENT *find_client(struct sockaddr_in *addr)
{
    CLIENT **start,*walk;
    unsigned char *a;
    void **next;
    int n;

    a = (unsigned char *) &addr->sin_addr.s_addr;
    start = &clients;
    for (n = 0; n < 4; n++) {
	if ((next = follow(start,*a)) == NULL) return NULL;
	start = (CLIENT **) next;
	a++;
    }
    for (walk = *start; walk; walk = walk->next)
	if (walk->port == ntohs(addr->sin_port)) return walk;
    return NULL;
}


void did_job(void *addr)
{
    CLIENT *client;

    if ((client = find_client((struct sockaddr_in *)addr)) == NULL) return;
    client->did++;
    done++;
    status(addr,client);
}


void got_job(void *addr)
{
    CLIENT *client;

    if ((client = find_client((struct sockaddr_in *)addr)) == NULL) return;
    client->got++;
    status(addr,client);
}

void print_addr(FILE *out, void *vaddr)
{
    struct sockaddr_in *addr=(struct sockaddr_in *)vaddr;
    if (!addr) fprintf(out, "<???>");
    else {
	struct hostent *h=gethostbyaddr((char *)&addr->sin_addr, 4,AF_INET);
        fprintf(out, "%s,%d",h?h->h_name:inet_ntoa(addr->sin_addr),
	        addr->sin_port);
    }
}

#else /* USE_PIPE */

extern int debug;
extern double space,total;
static int got=0, done = 0;

CLIENT clnt;

status()
{
    printf("<PIPE> %4d/%4d %14.0f/%14.0f=%6.2f    %s",done,got,total,space,
      total == 0.0 ? 0.0 : total/space*100.0, debug?"\n":"\r");
    fflush(stdout);
}

got_job(){got++;status();}
did_job(){done++;status();}
void **client_register(){ return &(clnt.client_field); }
void print_addr(void *addr) {printf("<The client>");}

#endif
