#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <memory.h>
#include <unistd.h>
#include <assert.h>
#include "gcache.h"

#define DEBUG		0

#if DEBUG

void Dump(const char *szName,uchar *p,int n)
    {
    printf("%s(",szName);
    for( ; n-- > 0 ; ++p)
	printf("%02x",*p);
    printf(")\n");
    }

#else

#define Dump(a,b,c)

#endif

static void HandleAdd(int nFD)
    {
    uchar *aucKey;
    int nKey;
    Cache *p;

    aucKey=ReadThing(nFD,&nKey);
    Dump("Add",aucKey,nKey);
    p=LocalCacheFind(aucKey,nKey);
    if(p)
	{
	free(p->aucData);
	free(aucKey);
	}
    else
	p=LocalCacheAdd(aucKey,nKey,0);
    p->aucData=ReadThing(nFD,&p->nData);
    read(nFD,&p->tExpiresAt,sizeof p->tExpiresAt);
    }

void HandleGet(int nFD)
    {
    uchar *aucKey;
    int nKey;
    Cache *p;
    int n;

    aucKey=ReadThing(nFD,&nKey);
    Dump("Get",aucKey,nKey);
    p=LocalCacheFind(aucKey,nKey);
    if(!p)
	{
	WriteThing(nFD,NULL,0);
#if DEBUG
	printf("Miss!\n");
#endif
	}
    else
	{
	WriteThing(nFD,p->aucData,p->nData);
	write(nFD,&p->tExpiresAt,sizeof p->tExpiresAt);
	}
    }
	
void HandleClient(int nFD)
    {
    static char c;

    read(nFD,&c,1);
    switch(c)
	{
    case ADD_TO_CACHE:
	HandleAdd(nFD);
	break;

    case GET_FROM_CACHE:
	HandleGet(nFD);
	break;
	}
    }

int main(int argc,char **argv)
    {
    int nPort;
    int nSocket;
    struct sockaddr_in saServer;
    int one=1;

    if(argc != 2)
	{
	fprintf(stderr,"%s <port>\n",argv[0]);
	exit(1);
	}

    nPort=atoi(argv[1]);

    nSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(nSocket < 0)
	{
	perror("socket");
	exit(2);
	}

    if (setsockopt(nSocket,SOL_SOCKET,SO_REUSEADDR,(char *)&one,sizeof one) < 0)
	{
	perror("setsockopt");
        exit(5);
	}

    memset(&saServer,0,sizeof saServer);
    saServer.sin_family=AF_INET;
    saServer.sin_port=htons(nPort);

    if(bind(nSocket,(struct sockaddr *)&saServer,sizeof saServer) < 0)
	{
	perror("bind");
	exit(3);
	}

    if(listen(nSocket,512) < 0)
	{
	perror("listen");
	exit(4);
	}

    for( ; ; )
	{
	int nFD;
	int nLen;
	struct sockaddr saClient;

	nLen=sizeof saClient;
	nFD=accept(nSocket,&saClient,&nLen);
	HandleClient(nFD);
	close(nFD);
	}

    return 0;
    }
