#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/fcall.h>
#include <9pm/ns.h>

/* Qid is (2*fd + (file is ctl))+1 */

static int
dupqidgen(Qid *qid, int s)
{	
	Chan *c;

	if(s == DEVDOTDOT || s == DEVDOT)
		return 1;
	s--;
	if(waserror())
		return -1;
	c = fd2chan(s/2, -1);
	cclose(c);
	poperror();
	mkqid(qid, s+1, 0, QTFILE);
	return 1;	
}

static int
dupqidstat(Qid q, uchar *edir, int nedir)
{
	int fd;
	char buf[20];
	Chan *c;
	Dir d;
	static int perm[] = { 0400, 0200, 0600, 0 };

	devdir(&d);
	d.qid = q;
	switch((long)q.path){
	case 0:
		d.name = "/";
		d.mode = DMDIR|0555;
		break;
	default:
		fd = (q.path-1)/2;
		if((q.path-1)&1){
			d.mode = 0400;
			sprint(buf, "%dctl", fd);
		}else{
			if(!waserror()){
				c = fd2chan(fd, -1);
				cclose(c);
				poperror();
				d.mode = perm[c->omode];
			}else
				d.mode = 0600;
			sprint(buf, "%d", fd);
		}
		d.name = buf;
		break;
	}
	return convD2M(&d, edir, nedir);
}

static int
dupgen(Qid q, int i, uchar *edir, int nedir)
{
	switch(dupqidgen(&q, i)){
	case -1:
		return -1;
	case 0:
		return 0;
	default:
		return dupqidstat(q, edir, nedir);
	}
}


static Chan*
dupopen(Chan *c, Mnt *mnt, char *path, int omode)
{
	Chan *f;
	int fd, twicefd;

	c->qid = devqid(mnt, path);
	if(c->qid.type & QTDIR){
		if(omode != 0)
			error(Eisdir);
		c->omode = 0;
		return c;
	}
	if(c->qid.type & QTAUTH)
		error(Eperm);
	twicefd = c->qid.path - 1;
	fd = twicefd/2;
	if((twicefd & 1) == 0){
		/* fd file */
		f = fd2chan(fd, omode);
		cclose(c);
		c = f;
	}
	return c;
}

static int
procqidwidth(Chan *c)
{
	char buf[32];

	return sprint(buf, "%lud", c->qid.vers);
}

int
procfdprint(Chan *c, int fd, int w, char *s, int ns)
{
	int n;

	if(w == 0)
		w = procqidwidth(c);
	n = snprint(s, ns, "%3d %.2s %C %4ld (%.16llux %*lud %.2ux) %5ld %8lld %s\n",
		fd,
		&"r w rw"[(c->omode&3)<<1],
		c->mnt->dev->dc, c->mnt->id,
		c->qid.path, w, c->qid.vers, c->qid.type,
		c->iounit, c->offset, c->path);
	return n;
}

static long
duppread(Chan *c, void *va, long n, vlong offset)
{
	char *a = va;
	char buf[256];
	int fd, twicefd;

	if(c->qid.type == QTDIR)
		return devdirread(c, a, n, offset);
	twicefd = c->qid.path - 1;
	fd = twicefd/2;
	if(twicefd & 1){
		c = fd2chan(fd, -1);
		procfdprint(c, fd, 0, buf, sizeof buf);
		cclose(c);
		return readstr(offset, va, n, buf);
	}
	error("dupread");
	return 0;
}

static long
duppwrite(Chan *c, void *va, long n, vlong offset)
{
	USED(c);
	USED(va);
	USED(n);
	USED(offset);
	error("duppwrite");
	return -1;
}

Dev devdup = 
{
	(Rune)'d',

	devchdir,
	devclunk,
	devcreate,
	devfstat,
	devfwstat,
	dupopen,
	duppread,
	duppwrite,
	devread,
	devremove,
	devseek,
	devstat,
	devwrite,
	devwstat,

	devattach,
	devserves,
	dupgen,
};
