#include "vnc.h"

#define SHORT(p) (((p)[0]<<8)|((p)[1]))
#define LONG(p) ((SHORT(p)<<16)|SHORT(p+2))

uchar zero[64];

Vnc*
vncinit(int fd, int cfd, Vnc *v)
{
        if(v == nil)
		v = mallocz(sizeof(*v), 1);
	Binit(&v->in, fd, OREAD);
	Binit(&v->out, fd, OWRITE);
	v->datafd = fd;
	v->ctlfd = cfd;
	return v;
}

void
vncterm(Vnc *v)
{
	Bterm(&v->out);
	Bterm(&v->in);
}

void
vncflush(Vnc *v)
{
	if(Bflush(&v->out) < 0){
		if(verbose > 1)
			fprint(2, "hungup while sending flush: %r\n");
		vnchungup(v);
	}
}

uchar
vncrdchar(Vnc *v)
{
	uchar buf[1];

	vncrdbytes(v, buf, 1);
	return buf[0];
}

ushort
vncrdshort(Vnc *v)
{
	uchar buf[2];

	vncrdbytes(v, buf, 2);
	return SHORT(buf);
}

ulong
vncrdlong(Vnc *v)
{
	uchar buf[4];

	vncrdbytes(v, buf, 4);
	return LONG(buf);
}

Point
vncrdpoint(Vnc *v)
{
	Point p;

	p.x = vncrdshort(v);
	p.y = vncrdshort(v);
	return p;
}

Rectangle
vncrdrect(Vnc *v)
{
	Rectangle r;

	r.min.x = vncrdshort(v);
	r.min.y = vncrdshort(v);
	r.max.x = r.min.x + vncrdshort(v);
	r.max.y = r.min.y + vncrdshort(v);
	return r;
}

Rectangle
vncrdcorect(Vnc *v)
{
	Rectangle r;

	r.min.x = vncrdchar(v);
	r.min.y = vncrdchar(v);
	r.max.x = r.min.x + vncrdchar(v);
	r.max.y = r.min.y + vncrdchar(v);
	return r;
}

void
vncrdbytes(Vnc *v, void *a, int n)
{
	if(Bread(&v->in, a, n) != n){
		if(verbose > 1)
			fprint(2, "hungup while reading\n");
		vnchungup(v);
	}
}

Pixfmt
vncrdpixfmt(Vnc *v)
{
	Pixfmt fmt;
	uchar pad[3];

	fmt.bpp = vncrdchar(v);
	fmt.depth = vncrdchar(v);
	fmt.bigendian = vncrdchar(v);
	fmt.truecolor = vncrdchar(v);
	fmt.red.max = vncrdshort(v);
	fmt.green.max = vncrdshort(v);
	fmt.blue.max = vncrdshort(v);
	fmt.red.shift = vncrdchar(v);
	fmt.green.shift = vncrdchar(v);
	fmt.blue.shift = vncrdchar(v);
	vncrdbytes(v, pad, 3);
	return fmt;
}

char*
vncrdstring(Vnc *v)
{
	ulong len;
	char *s;

	len = vncrdlong(v);
	s = malloc(len+1);
	assert(s != nil);

	vncrdbytes(v, s, len);
	s[len] = '\0';
	return s;
}

/*
 * on the server side of the negotiation protocol, we read
 * the client response and then run the negotiated function.
 * in some cases (e.g., TLS) the negotiated function needs to
 * use v->datafd directly and be sure that no data has been
 * buffered away in the Bio.  since we know the client is waiting
 * for our response, it won't have sent any until we respond.
 * thus we read the response with vncrdstringx, which goes
 * behind bio's back.
 */
char*
vncrdstringx(Vnc *v)
{
	char tmp[4];
	char *s;
	ulong len;

	assert(Bbuffered(&v->in) == 0);
	if(readn(v->datafd, tmp, 4) != 4){
		fprint(2, "cannot rdstringx: %r");
		vnchungup(v);
	}
	len = LONG(tmp);
	s = malloc(len+1);
	assert(s != nil);
	if(readn(v->datafd, s, len) != len){
		fprint(2, "cannot rdstringx len %lud: %r", len);
		vnchungup(v);
	}
	s[len] = '\0';
	return s;
}

void
vncwrstring(Vnc *v, char *s)
{
	ulong len;

	len = strlen(s);
	vncwrlong(v, len);
	vncwrbytes(v, s, len);
}

void
vncwrbytes(Vnc *v, void *a, int n)
{
	if(Bwrite(&v->out, a, n) < 0){
		if(verbose > 1) 
			fprint(2, "hungup while writing bytes\n");
		vnchungup(v);
	}
}

void
vncwrlong(Vnc *v, ulong u)
{
	uchar buf[4];

	buf[0] = u>>24;
	buf[1] = u>>16;
	buf[2] = u>>8;
	buf[3] = u;
	vncwrbytes(v, buf, 4);
}

void
vncwrshort(Vnc *v, ushort u)
{
	uchar buf[2];

	buf[0] = u>>8;
	buf[1] = u;
	vncwrbytes(v, buf, 2);
}

void
vncwrchar(Vnc *v, uchar c)
{
	vncwrbytes(v, &c, 1);
}

void
vncwrpixfmt(Vnc *v, Pixfmt *fmt)
{
	vncwrchar(v, fmt->bpp);
	vncwrchar(v, fmt->depth);
	vncwrchar(v, fmt->bigendian);
	vncwrchar(v, fmt->truecolor);
	vncwrshort(v, fmt->red.max);
	vncwrshort(v, fmt->green.max);
	vncwrshort(v, fmt->blue.max);
	vncwrchar(v, fmt->red.shift);
	vncwrchar(v, fmt->green.shift);
	vncwrchar(v, fmt->blue.shift);
	vncwrbytes(v, zero, 3);
}

void
vncwrrect(Vnc *v, Rectangle r)
{
	vncwrshort(v, r.min.x);
	vncwrshort(v, r.min.y);
	vncwrshort(v, r.max.x-r.min.x);
	vncwrshort(v, r.max.y-r.min.y);
}

void
vncwrpoint(Vnc *v, Point p)
{
	vncwrshort(v, p.x);
	vncwrshort(v, p.y);
}

void
vnclock(Vnc *v)
{
	qlock(v);
}

void
vncunlock(Vnc *v)
{
	qunlock(v);
}

void
vncgobble(Vnc *v, long n)
{
	uchar buf[8192];
	long m;

	while(n > 0){
		m = n;
		if(m > sizeof(buf))
			m = sizeof(buf);
		vncrdbytes(v, buf, m);
		n -= m;
	}
}
