#include	<u.h>
#include	<libc.h>
#include	<draw.h>
#define NODEFINE
#include	<9pm/u.h>
#include	<9pm/libc.h>
#include	<9pm/memdraw.h>
#include	<9pm/cursor.h>
#include	<9pm/ns.h>
#include	<9pm/thread.h>
#include	<9pm/screen.h>

static int mousefd;
static int cursorfd;
static void reshaped(void);

static Rectangle
screenrect(void)
{
	int fd;
	char buf[12*5];

	fd = open("/dev/screen", OREAD);
	if(fd == -1)
		fd = open("/mnt/term/dev/screen", OREAD);
	if(fd == -1)
		pm_sysfatal("can't open /dev/screen: %r\n");
	if(read(fd, buf, sizeof buf) != sizeof buf)
		pm_sysfatal("can't read /dev/screen: %r\n");
	close(fd);
	return Rect(atoi(buf+12), atoi(buf+24), atoi(buf+36), atoi(buf+48));
}

static int nresize;

static void
mouseproc(void*)
{
	char buf[50];
	char *f[5];
	Point p;

	if((mousefd = open("/dev/mouse", ORDWR)) < 0){
		fprint(2, "warning: no mouse\n");
		return;
	}

	if((cursorfd = open("/dev/cursor", ORDWR)) < 0){
		fprint(2, "warning: no cursor\n");
		return;
	}

	while(read(mousefd, buf, 49) == 49) {
		if(buf[0] == 'r'){
			nresize++;
			pm_resizescreenimage();
		}
		if(tokenize(buf, f, nelem(f)) < 5)
			continue;
		p.x = atoi(f[1]);
		p.y = atoi(f[2]);
		p = subpt(p, screen->r.min);
		pm_mousetrack(p, atoi(f[3]), atoi(f[4]));
	}
}

static void
kbdproc(void*)
{
	int fd, kfd, n, l;
	char buf[32], *p;
	Rune r;

	if((fd = open("/dev/consctl", OWRITE)) < 0)
		fprint(2, "warning: cannot set raw mode\n");
	else
		fprint(fd, "rawon");

	if((kfd = open("/dev/cons", OREAD)) < 0){
		fprint(2, "warning: cannot read /dev/cons\n");
		return;
	}

	while((n = read(kfd, buf, sizeof buf)) >= 0) {
		p = buf;
		while(n > 0) {
			l = chartorune(&r, p);
			p += l;
			n -= l;
			pm_kbdputc(r);
		}
	}
}

void
pm_cursorload(Cursor *c)
{
	char curs[2*4+2*2*16];

	if(c == nil)
		write(cursorfd, curs, 0);
	else{
		BPLONG(curs+0*4, c->offset.x);
		BPLONG(curs+1*4, c->offset.y);
		memmove(curs+2*4, c->clr, 2*2*16);
		write(cursorfd, curs, sizeof curs);
	}
}

int
pm_cursormove(Point p)
{
	char buf[30];

	p = addpt(p, screen->r.min);
	snprint(buf, sizeof buf, "m%d %d", p.x, p.y);
	if(write(mousefd, buf, strlen(buf)) != strlen(buf)){
		fprint(2, "write %d failed; %r\n", mousefd);
		abort();
	}
	return 0;	/* success? */
}

void
pm_mousectl(Cmdbuf *c)
{
	USED(c);
	pm_error("no mouse ctls supported");
}

Memimage*
pm_attachscreen(int *softscreen)
{
	if(initdraw(0, 0, "drawterm") < 0)
		sysfatal("plan9 initdraw: %r");

	*softscreen = 1;
	memimageinit();
	proccreate(mouseproc, nil, 8192);
	proccreate(kbdproc, nil, 8192);
	pm_gscreen = allocmemimage(Rect(0, 0, Dx(screen->r), Dy(screen->r)), screen->chan);
	if(pm_gscreen == nil)
		sysfatal("cannot allocate memimage: %r");
	pm_drawconsinit();
	return pm_gscreen;
}

void
pm_detachscreen(Memimage *m)
{
	USED(m);
}

Memimage*
pm_reattachscreen(Memimage *m, int *softscreen)
{
	*softscreen = 1;
	
	if(getwindow(display, Refnone) < 0){
		fprint(2,"can't reattach to window");
		return m;
	}
	m = allocmemimage(Rect(0, 0, Dx(screen->r), Dy(screen->r)), screen->chan);
	if(m == nil)
		sysfatal("cannot allocate memimage: %r");
	pm_gscreen = m;
	return m;
}

#define	CHUNK	7500

/*
 * Like loadimage, but doesn't require the source data to be just the
 * given rectangle.  Lines in data are assumed to be bpl bytes wide.
 */
static int
myloadimage(Image *i, Rectangle r, uchar *data, int ndata, int bpl)
{
	long dy;
	int n, subbpl, y;
	uchar *a;

	if(r.max.x > i->r.max.x)
		r.max.x = i->r.max.x;
	if(r.max.y > i->r.max.y)
		r.max.y = i->r.max.y;

	if(!rectinrect(r, i->r)){
		fprint(2, "myloadimage: bad rectangle\n");
		return -1;
	}
	subbpl = bytesperline(r, i->depth);
	n = bpl*(Dy(r)-1)+subbpl;
	if(n > ndata){
		fprint(2, "myloadimage: insufficient data\n");
		return -1;
	}
	ndata = 0;
	while(r.max.y > r.min.y){
		dy = r.max.y - r.min.y;
		if(dy*subbpl > CHUNK)
			dy = CHUNK/subbpl;
		if(dy <= 0){
			fprint(2, "myloadimage: image too wide for buffer (CHUNK=%d, subbpl=%d)\n",
				CHUNK, subbpl);
			return -1;
		}
		n = dy*subbpl;
		a = bufimage(i->display, 21+n);
		if(a == nil){
			fprint(2, "myloadimage: bufimage %d failed (dy=%d, subbpl=%d)", 21+n, dy, subbpl);
			return -1;
		}
		a[0] = 'y';
		BPLONG(a+1, i->id);
		BPLONG(a+5, r.min.x);
		BPLONG(a+9, r.min.y);
		BPLONG(a+13, r.max.x);
		BPLONG(a+17, r.min.y+dy);

		for(y=0; y<dy; y++){
			memmove(a+21+y*subbpl, data, subbpl);
			data += bpl;
			ndata += subbpl;
			r.min.y++;
		}
	}
	if(flushimage(i->display, 0) < 0)
		return -1;
	return ndata;
}

void
pm_flushmemscreen(Rectangle r)
{
	if(rectclip(&r, pm_gscreen->r) == 0)
		return;

	myloadimage(screen, rectaddpt(r, screen->r.min), byteaddr(pm_gscreen, r.min), 1<<30, sizeof(ulong)*pm_gscreen->width);
	flushimage(display, 1);
}

