/*
 * Present venti hash tree as file system for debugging.
 */

#include <u.h>
#include <libc.h>
#include <venti.h>
#include </sys/src/cmd/vac/vac.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>

VtFile *rootf;

void
responderrstr(Req *r)
{
	char e[ERRMAX];

	rerrstr(e, sizeof e);
	respond(r, e);
}

void
mkqide(Qid *q, VtEntry *e)
{
	if(e->type&VtDirType)
		q->type = QTDIR;
	else
		q->type = 0;
	q->path = *(uvlong*)e->score;
	q->vers = *(ulong*)(e->score+8);
}

void
mkqid(Qid *q, VtFile *f)
{
	VtEntry e;

	vtfilelock(f, VtOREAD);
	vtfilegetentry(f, &e);
	vtfileunlock(f);
	mkqide(q, &e);
}

void
fsdestroyfid(Fid *fid)
{
	if(fid->aux)
		vtfileclose(fid->aux);
}

void
fsattach(Req *r)
{
	r->fid->aux = rootf;
	vtfileincref(rootf);
	mkqid(&r->ofcall.qid, rootf);
	r->fid->qid = r->ofcall.qid;
	respond(r, nil);
}

void
fsopen(Req *r)
{
	if(r->ifcall.mode != OREAD){
		respond(r, "read-only file system");
		return;
	}
	r->ofcall.qid = r->fid->qid;
	respond(r, nil);
}

void
fsreaddir(Req *r)
{
	Dir d;
	int dsize, nn;
	char score[2*VtScoreSize+1];
	uchar edata[VtEntrySize], *p, *ep;
	u32int i, n, epb;
	u64int o;
	VtFile *f;
	VtEntry e;

	memset(&d, 0, sizeof d);
	d.name = "0123456789012345678901234567890123456789";
	d.uid = "venti";
	d.gid = "venti";
	d.muid = "";
	d.atime = time(0);
	d.mtime = time(0);
	d.mode = 0444;
	dsize = sizeD2M(&d);

	p = (uchar*)r->ofcall.data;
	ep = p+r->ifcall.count;
	f = r->fid->aux;
	vtfilelock(f, VtOREAD);
	vtfilegetentry(f, &e);
	epb = e.dsize / VtEntrySize;
	n = vtfilegetdirsize(f);
//fprint(2, "dirsize %d\n", n);
	i = r->ifcall.offset / dsize;
	for(; i<n; i++){
//fprint(2, "%d...", i);
		if(p+dsize > ep)
{
//fprint(2, "done %p %d %p\n", p, dsize, ep);
			break;
}
		o = (i/epb)*e.dsize+(i%epb)*VtEntrySize;
		if(vtfileread(f, edata, VtEntrySize, o) != VtEntrySize){
			vtfileunlock(f);
			responderrstr(r);
			return;
		}
		if(vtentryunpack(&e, edata, 0) < 0){
			fprint(2, "entryunpack: %r\n");
			continue;
		}
		if(!(e.flags&VtEntryActive)){
			strcpy(score, "________________________________________");
			d.qid.type = 0;
		}else{
			snprint(score, sizeof score, "%V", e.score);
			mkqide(&d.qid, &e);
		}
		d.name = score;
		if(e.type&VtDirType){
			d.mode = DMDIR|0555;
			d.qid.type = QTDIR;
		}else{
			d.mode = 0444;
			d.qid.type = 0;
		}
		d.length = e.size;
		nn = convD2M(&d, p, dsize);
		if(nn != dsize)
			fprint(2, "oops: dsize mismatch\n");
		p += dsize;
	}
	r->ofcall.count = p - (uchar*)r->ofcall.data;
	vtfileunlock(f);
	respond(r, nil);
}

void
fsread(Req *r)
{
	long n;

	if(r->fid->qid.type&QTDIR){
		fsreaddir(r);
		return;
	}
	vtfilelock(r->fid->aux, VtOREAD);
	n = vtfileread(r->fid->aux, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
	vtfileunlock(r->fid->aux);
	if(n < 0){
		responderrstr(r);
		return;
	}
	r->ofcall.count = n;
	respond(r, nil);
}

void
fsstat(Req *r)
{
	VtEntry e;

	vtfilelock(r->fid->aux, VtOREAD);
	if(vtfilegetentry(r->fid->aux, &e) < 0){
		vtfileunlock(r->fid->aux);
		responderrstr(r);
		return;
	}
	vtfileunlock(r->fid->aux);
	memset(&r->d, 0, sizeof r->d);
	r->d.name = smprint("%V", e.score);
	r->d.qid = r->fid->qid;
	r->d.mode = 0444;
	if(r->d.qid.type&QTDIR)
		r->d.mode |= DMDIR|0111;
	r->d.uid = estrdup9p("venti");
	r->d.gid = estrdup9p("venti");
	r->d.muid = estrdup9p("");
	r->d.atime = time(0);
	r->d.mtime = time(0);
	r->d.length = e.size;
	respond(r, nil);
}

char*
fswalk1(Fid *fid, char *name, void*)
{
	uchar score[VtScoreSize], edata[VtEntrySize];
	u32int i, n, epb;
	u64int o;
	VtFile *f, *ff;
	VtEntry e;

	if(dec16(score, sizeof score, name, strlen(name)) != VtScoreSize)
		return "file not found";

	f = fid->aux;
	vtfilegetentry(f, &e);
	epb = e.dsize / VtEntrySize;
	n = vtfilegetdirsize(f);
	o = 0;
	for(i=0; i<n; i++){
		o = (i/epb)*e.dsize+(i%epb)*VtEntrySize;
		if(vtfileread(f, edata, VtEntrySize, o) != VtEntrySize)
			return "error in vtfileread";
		if(vtentryunpack(&e, edata, 0) < 0)
			continue;
		if(!(e.flags&VtEntryActive))
			continue;
		if(memcmp(e.score, score, VtScoreSize) == 0)
			break;
	}
	if(i == n)
		return "entry not found";
	ff = vtfileopen(f, i, VtOREAD);
	if(ff == nil)
		return "error in vtfileopen";
	vtfileclose(f);
	fid->aux = ff;
	mkqid(&fid->qid, ff);
	return nil;
}

char*
fsclone(Fid *fid, Fid *newfid, void*)
{
	newfid->aux = fid->aux;
	vtfileincref(fid->aux);
	return nil;
}

void
fswalk(Req *r)
{
	VtFile *f;

	f = r->fid->aux;
	vtfilelock(f, VtOREAD);
	walkandclone(r, fswalk1, fsclone, nil);
	vtfileunlock(f);
}

Srv fs =
{
.destroyfid=	fsdestroyfid,
.attach=		fsattach,
.open=		fsopen,
.read=		fsread,
.stat=		fsstat,
.walk=		fswalk
};

void
usage(void)
{
	fprint(2, "usage: ventifs score\n");
	exits("usage");
}

void
main(int argc, char **argv)
{
	VtConn *z;
	VtCache *c;
	VtBlock *b;
	VtRoot root;
	VtEntry e;
	int bsize;
	uchar score[VtScoreSize];

	bsize = 16384;
	ARGBEGIN{
	case 'D':
		chatty9p++;
		break;
	case 'b':
		bsize = atoi(EARGF(usage()));
		break;
	default:
		usage();
	}ARGEND

	if(argc != 1)
		usage();

	fmtinstall('V', vtscorefmt);

	if((z = vtdial(nil)) == nil)
		sysfatal("vtDial: %r");
	if(vtconnect(z) < 0)
		sysfatal("vtConnect: %r");

	c = vtcachealloc(z, bsize, 50);

	if(vtparsescore(argv[0], nil, score) < 0)
		sysfatal("bad score: %r");
	b = vtcacheglobal(c, score, VtRootType);
	if(b){
		if(vtrootunpack(&root, b->data) < 0)
			sysfatal("bad root: %r");
		memmove(score, root.score, VtScoreSize);
		vtblockput(b);
	}
	b = vtcacheglobal(c, score, VtDirType);
	if(b == nil)
		sysfatal("vtcacheglobal %V: %r", score);
	if(vtentryunpack(&e, b->data, 0) < 0)
		sysfatal("%V: vtentryunpack failed", score);
	fprint(2, "entry: size %llud psize %d dsize %d\n",
		e.size, e.psize, e.dsize);
	vtblockput(b);

	if(!(e.type&VtDirType)){
		b = vtcacheallocblock(c, VtDirType);
		vtentrypack(&e, b->data, 0);
		memmove(e.score, b->score, VtScoreSize);
		e.type |= VtDirType;
		e.size = VtEntrySize;
//		e.depth = 0;
		vtblockput(b);
	}

	rootf = vtfileopenroot(c, &e);
	if(rootf == nil)
		sysfatal("vtFileOpenRoot: %r");

	postmountsrv(&fs, nil, "/n/kremvax", MREPL);
}
