#include "stdinc.h"
#include "dat.h"
#include "fns.h"

int debug;

static Packet   *srvRead(VtSession *z, uchar score[VtScoreSize], int type, int n);
static int      srvWrite(VtSession *z, uchar score[VtScoreSize], int type, Packet *p);
static void     srvClosing(VtSession *z, int clean);
static void     daemon(char *vaddr);

static VtServerVtbl serverVtbl = {
read    srvRead,
write   srvWrite,
closing srvClosing,
};

void
main(int argc, char *argv[])
{
       char *config, *haddr, *vaddr;
       u32int mem, icmem, bcmem;

       vaddr = "tcp!*!venti";
       haddr = nil;
       config = nil;
       mem = 0xffffffffUL;
       icmem = 0;
       bcmem = 0;
       ARGBEGIN{
       case 'a':
               vaddr = ARGF();
               if(vaddr == nil)
                       goto usage;
               break;
       case 'B':
               bcmem = unittoull(ARGF());
               break;
       case 'c':
               config = ARGF();
               if(config == nil)
                       goto usage;
               break;
       case 'C':
               mem = unittoull(ARGF());
               break;
       case 'd':
               debug = 1;
               break;
       case 'h':
               haddr = ARGF();
               if(haddr == nil)
                       goto usage;
               break;
       case 'I':
               icmem = unittoull(ARGF());
               break;
       case 'w':
               queueWrites = 1;
               break;
       default:
               goto usage;
       }ARGEND

       if(argc){
 usage:
               fprint(2, "usage: venti [-dw] [-a ventiaddress] [-h httpaddress] [-c config] [-C cachesize] [-I icachesize] [-B blockcachesize]\n");
               exits("usage");
       }

       if(config == nil)
               config = "venti.conf";

       vtAttach();

       if(!initArenaSum())
               fprint(2, "warning: can't initialize arena summing process: %R");

       if(!initVenti(config))
               fatal("can't init server: %R");

       if(mem == 0xffffffffUL)
               mem = 1 * 1024 * 1024;
       fprint(2, "initialize %d bytes of lump cache for %d lumps\n", mem, mem / (8 * 1024));
       initLumpCache(mem, mem / (8 * 1024));

       icmem = u64log2(icmem / (sizeof(IEntry) + sizeof(IEntry*)) / ICacheDepth);
       if(icmem < 4)
               icmem = 4;
       fprint(2, "initialize %d bytes of index cache for %d index entries\n",
               (sizeof(IEntry) + sizeof(IEntry*)) * (1 << icmem) * ICacheDepth, (1 << icmem) * ICacheDepth);
       initICache(icmem, ICacheDepth);

       /*
        * need a block for every arena and every process
        */
       if(bcmem < maxBlockSize * (mainIndex->narenas + mainIndex->nsects * 4 + 16))
               bcmem = maxBlockSize * (mainIndex->narenas + mainIndex->nsects * 4 + 16);
       fprint(2, "initialize %d bytes of disk block cache\n", bcmem);
       initDCache(bcmem);

       fprint(2, "sync arenas and index...\n");
       if(!syncIndex(mainIndex, 1))
               fatal("can't sync server: %R");

       if(queueWrites){
               fprint(2, "initialize write queue...\n");
               if(!initLumpQueues(mainIndex->nsects)){
                       fprint(2, "can't initialize lump queues, disabling write queueing: %R");
                       queueWrites = 0;
               }
       }

       if(haddr){
               fprint(2, "starting http server at %s\n", haddr);
               if(httpdInit(haddr) < 0)
                       fprint(2, "warning: can't start http server: %R");
       }

       fprint(2, "starting server\n");
       daemon(vaddr);

       vtDetach();

       exits(0);
}

static void
daemon(char *vaddr)
{
       int cfd, lcfd, fd;
       char adir[100], ldir[100];
       VtSession *z;

       cfd = announce(vaddr, adir);
       if(cfd < 0)
               fatal("can't announce: %s", vtOSError());
       for(;;){
               /* listen for a call */
               lcfd = listen(adir, ldir);
               if(lcfd < 0)
                       fatal("listen: %s", vtOSError());
fprint(2, "new call on %s\n", ldir);
checkLumpCache();
checkDCache();
//printStats();
               fd = accept(lcfd, ldir);
               close(lcfd);
               z = vtServerAlloc(&serverVtbl);
               vtSetDebug(z, debug);
               vtSetFd(z, fd);
               vtExport(z);
       }
}

static Packet *
srvRead(VtSession *z, uchar score[VtScoreSize], int type, int n)
{
       Packet *p;
       USED(z);

       p = readLump(score, type, n);
       return p;
}

static int
srvWrite(VtSession *z, uchar score[VtScoreSize], int type, Packet *p)
{
       int ok;

       USED(z);
       ok = writeLump(p, score, type, 0);
       return ok;
}

static void
srvClosing(VtSession *z, int clean)
{
       long rt, t[4];

       USED(z);
       if(!clean)
               fprint(2, "not a clean exit: %s\n", vtGetError());
       rt = times(t);
       fprint(2, "times: %.2fu %.2fs %.2fr\n", t[0]*.001, t[1]*.001, rt*.001);
packetStats();
}