#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

static char Ebad[] = "something bad happened";
static char Enomem[] = "no memory";

typedef struct Ramfile  Ramfile;
struct Ramfile {
       char *data;
       int ndata;
};

void
fsread(Req *r)
{
       Ramfile *rf;
       vlong offset;
       long count;

       rf = r->fid->file->aux;
       offset = r->ifcall.offset;
       count = r->ifcall.count;

//print("read %ld %lld\n", *count, offset);
       if(offset >= rf->ndata){
               r->ofcall.count = 0;
               respond(r, nil);
               return;
       }

       if(offset+count >= rf->ndata)
               count = rf->ndata - offset;

       memmove(r->ofcall.data, rf->data+offset, count);
       r->ofcall.count = count;
       respond(r, nil);
}

void
fswrite(Req *r)
{
       void *v;
       Ramfile *rf;
       vlong offset;
       long count;

       rf = r->fid->file->aux;
       offset = r->ifcall.offset;
       count = r->ifcall.count;

       if(offset+count >= rf->ndata){
               v = realloc(rf->data, offset+count);
               if(v == nil){
                       respond(r, Enomem);
                       return;
               }
               rf->data = v;
               rf->ndata = offset+count;
               r->fid->file->length = rf->ndata;
       }
       memmove(rf->data+offset, r->ifcall.data, count);
       r->ofcall.count = count;
       respond(r, nil);
}

void
fscreate(Req *r)
{
       Ramfile *rf;
       File *f;

       if(f = createfile(r->fid->file, r->ifcall.name, r->fid->uid, r->ifcall.perm, nil)){
               rf = emalloc9p(sizeof *rf);
               f->aux = rf;
               r->fid->file = f;
               r->ofcall.qid = f->qid;
               respond(r, nil);
               return;
       }
       respond(r, Ebad);
}

void
fsopen(Req *r)
{
       Ramfile *rf;

       rf = r->fid->file->aux;

       if(rf && (r->ifcall.mode&OTRUNC)){
               rf->ndata = 0;
               r->fid->file->length = 0;
       }

       respond(r, nil);
}

void
fsdestroyfile(File *f)
{
       Ramfile *rf;

//fprint(2, "clunk\n");
       rf = f->aux;
       if(rf){
               free(rf->data);
               free(rf);
       }
}

Srv fs = {
       .open=  fsopen,
       .read=  fsread,
       .write= fswrite,
       .create=        fscreate,
};

void
usage(void)
{
       fprint(2, "usage: ramfs [-D] [-s srvname] [-m mtpt]\n");
       exits("usage");
}

void
main(int argc, char **argv)
{
       char *addr = nil;
       char *srvname = nil;
       char *mtpt = nil;
       Qid q;

       fs.tree = alloctree(nil, nil, DMDIR|0777, fsdestroyfile);
       q = fs.tree->root->qid;

       ARGBEGIN{
       case 'D':
               chatty9p++;
               break;
       case 'a':
               addr = EARGF(usage());
               break;
       case 's':
               srvname = EARGF(usage());
               break;
       case 'm':
               mtpt = EARGF(usage());
               break;
       default:
               usage();
       }ARGEND;

       if(argc)
               usage();

       if(chatty9p)
               fprint(2, "srvname %s mtpt %s\n", srvname, mtpt);
       if(addr == nil && srvname == nil && mtpt == nil)
               sysfatal("must specify -a, -s, or -m option");
       if(addr)
               listensrv(&fs, addr);

       if(srvname || mtpt)
               postmountsrv(&fs, srvname, mtpt, MREPL|MCREATE);
       exits(0);
}