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

#define BUCKSIZE 524288
#define MAXQ 512
#define SMBUF 256

typedef struct Hub	Hub;
typedef struct Msgq	Msgq;

struct Hub{
	char name[SMBUF];			/* name */
	char bucket[BUCKSIZE];		/* data buffer */
	char *inbuckp;				/* location to store next message */
	int buckfull;				/* amount of data stored in bucket */
	Req *qreqs[MAXQ];			/* pointers to queued Reqs */
	int rstatus[MAXQ];			/* status of requests */
	int qrnum;				/* index of Reqs waiting to be filled */
	int qans;					/* number of Reqs answered */
	int sleeptime;				/* delay time */
};

struct Msgq{
	ulong myfid;
	char *nxt;
	int bufuse;
};

enum {
	UP = 1,
	DOWN = 2,
	WAIT = 3,
	DONE = 4,
};

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

void msgsend(Hub *h, char *source);
void zerohub(Hub *h);
void fsread(Req *r);
void fswrite(Req *r);
void fscreate(Req *r);
void fsopen(Req *r);
void fsdestroyfile(File *f);
void usage(void);

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

void
msgsend(Hub *h, char *source)
{
	Msgq *mq;
	u32int count;
	for(int i = h->qans; i <= h->qrnum; i++){
		if(h->rstatus[i] != WAIT){
			if(verbosity == UP){
				print("\t%s qreq %d DONE\n", h->name, i);
			}
			if(i == h->qans){
				h->qans++;
				if(verbosity == UP){
					print("\t%s incremented h->qans to %d\n", h->name, h->qans);
				}
			}
			continue;
		}
		mq = h->qreqs[i]->fid->aux;
		if(mq->nxt != h->inbuckp){
			if(verbosity == UP){
				print("\t\t%s qans %d pointer at %d bucket at %d preparing reply\n", h->name, i, (int)mq->nxt, (int)h->inbuckp);
			}
			count = h->qreqs[i]->ifcall.count;
			if(mq->bufuse + count >= BUCKSIZE){
				mq->nxt = h->bucket;
				mq->bufuse = 0;
			}
			if((mq->bufuse + count > h->buckfull) && (mq->bufuse < h->buckfull)){
				if(verbosity == UP){
					print("\tbufuse + count %d greater than %d buckfull, adjusting to %d\n", mq->bufuse + count, h->buckfull, h->buckfull - mq->bufuse);
				}
				count = h->buckfull - mq->bufuse;
			}
			memmove(h->qreqs[i]->ofcall.data, mq->nxt, count);
			h->qreqs[i]->ofcall.count = count;
			if(verbosity == UP){
				print("\t\t%s %s msgsend respond qans %d mq fid %ld from %d count %d\n", h->name, source, i, mq->myfid, (int)mq->nxt, count);
			}
			mq->nxt += count;
			mq->bufuse += count;
			h->rstatus[i] = DONE;
			if(i == h->qans){
				h->qans++;
			}
			respond(h->qreqs[i], nil);
		} else {
			if(verbosity == UP){
				print("\t%s qans %d mq->nxt == h->bucket\n", h->name, i);
			}
		}
	}
}

void
fsread(Req *r)
{
	Hub *h;

	h = r->fid->file->aux;
	h->qrnum++;
	if(h->qrnum == MAXQ){
		msgsend(h, "req q reset");
		h->qrnum = 1;
		h->qans = 0;
	}
	h->rstatus[h->qrnum] = WAIT;
	h->qreqs[h->qrnum] = r;
	if(verbosity == UP){
		print("\t\t%s fsread queued read %d on fid %ld count %d\n", h->name, h->qrnum, r->fid->fid, r->ifcall.count);
	}
	msgsend(h, "fsread");
	if(verbosity == UP){
		print("\t\t%s fsread complete h->qans %d h->qrnum %d\n", h->name, h->qans, h->qrnum);
	}
}

void
fswrite(Req *r)
{
	u32int count;
	Hub *h;

	h = r->fid->file->aux;
	count = r->ifcall.count;

	if((h->buckfull + count) >= BUCKSIZE){
		if(verbosity == UP){
			print("\n\n %s bucket wrap!! \n\n", h->name);
		}
		h->inbuckp = h->bucket;
		h->buckfull = 0;
	}

	memmove(h->inbuckp, r->ifcall.data, count);
	if(verbosity ==  UP){
		print("\t\t%s fswrite inbuckp at %d count is %d\n", h->name, (int)h->inbuckp, count);
	}

	h->inbuckp += count;
	h->buckfull += count;
	r->fid->file->length = h->buckfull;
	r->ofcall.count = count;

	respond(r, nil);
	msgsend(h, "fswrite");
	if(verbosity == UP){
		print("\t\t%s fswrite complete h->qans %d h->qrnum %d\n", h->name, h->qans, h->qrnum);
	}
}

void
fscreate(Req *r)
{
	Hub *h;
	File *f;

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

void
fsopen(Req *r)
{
	Hub *h;
	Msgq *q;

	h = r->fid->file->aux;
	q = (Msgq*)emalloc9p(sizeof(Msgq));
	memset(q, 0, sizeof(Msgq));
	q->myfid = r->fid->fid;
	q->nxt = h->bucket;
	q->bufuse = 0;
	r->fid->aux = q;
	if(h && (r->ifcall.mode&OTRUNC)){
		h->inbuckp = h->bucket;
		h->buckfull = 0;
		r->fid->file->length = 0;
	}
	if(verbosity == UP){
		print("\t\tfsopen %s myfid %ld h->inbuckp at %d q->nxt at %d\n", h->name, q->myfid, (int)h->inbuckp, (int)q->nxt);
	}
	respond(r, nil);
}

void
fsdestroyfile(File *f)
{
	Hub *h;

	h = f->aux;
	if(h){
		free(h);
	}
}

void
zerohub(Hub *h)
{
	memset(h, 0, sizeof(Hub));
	h->sleeptime = 20;
	h->inbuckp = &h->bucket[0];
	h->qrnum = 0;
	h->qans = 0;
	return;
}

void
usage(void)
{
	fprint(2, "usage: hubfs [-D -v] [-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;
	case 'v':
		verbosity = UP;
		print("vebosity UP\n");
		break;
	default:
		usage();
	}ARGEND;

	if(argc)
		usage();
	if(chatty9p)
		fprint(2, "hubsrv.nopipe %d srvname %s mtpt %s\n", fs.nopipe, 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);
}

