#include <u.h>
#include <libc.h>
#include <venti.h>

Packet*
vtfcallpack(VtFcall *f)
{
	uchar buf[10];
	Packet *p;

	p = packetalloc();

	buf[0] = f->msgtype;
	buf[1] = f->tag;
	packetappend(p, buf, 2);

	switch(f->msgtype){
	default:
		werrstr("vtfcallpack: unknown packet type %d", f->msgtype);
		goto Err;

	case VtRerror:
		if(vtputstring(p, f->error) < 0)
			goto Err;
		break;

	case VtTping:
		break;

	case VtRping:
		break;

	case VtThello:
		if(vtputstring(p, f->version) < 0
		|| vtputstring(p, f->uid) < 0)
			goto Err;
		buf[0] = f->strength;
		buf[1] = f->ncrypto;
		packetappend(p, buf, 2);
		packetappend(p, f->crypto, f->ncrypto);
		buf[0] = f->ncodec;
		packetappend(p, buf, 1);
		packetappend(p, f->codec, f->ncodec);
		break;

	case VtRhello:
		if(vtputstring(p, f->sid) < 0)
			goto Err;
		buf[0] = f->rcrypto;
		buf[1] = f->rcodec;
		packetappend(p, buf, 2);
		break;

	case VtTgoodbye:
		break;

	case VtTread:
		packetappend(p, f->score, VtScoreSize);
		buf[0] = vttodisktype(f->blocktype);
		if(~buf[0] == 0)
			goto Err;
		buf[1] = 0;
		if(f->count >= (1<<16)) {
			buf[2] = f->count >> 24;
			buf[3] = f->count >> 16;
			buf[4] = f->count >> 8;
			buf[5] = f->count;
			packetappend(p, buf, 6);
		} else {
			buf[2] = f->count >> 8;
			buf[3] = f->count;
			packetappend(p, buf, 4);
		}
		break;

	case VtRread:
		packetconcat(p, f->data);
		break;

	case VtTwrite:
		buf[0] = vttodisktype(f->blocktype);
		if(~buf[0] == 0)
			goto Err;
		buf[1] = 0;
		buf[2] = 0;
		buf[3] = 0;
		packetappend(p, buf, 4);
		packetconcat(p, f->data);
		break;

	case VtRwrite:
		packetappend(p, f->score, VtScoreSize);
		break;

	case VtTsync:
		break;

	case VtRsync:
		break;
	}

	return p;

Err:
	packetfree(p);
	return nil;
}

int
vtfcallunpack(VtFcall *f, Packet *p)
{
	uchar buf[4];

	memset(f, 0, sizeof *f);

	if(packetconsume(p, buf, 2) < 0)
		return -1;

	f->msgtype = buf[0];
	f->tag = buf[1];

	switch(f->msgtype){
	default:
		werrstr("vtfcallunpack: unknown bad packet type %d", f->msgtype);
		vtfcallclear(f);
		return -1;

	case VtRerror:
		if(vtgetstring(p, &f->error) < 0)
			goto Err;
		break;

	case VtTping:
		break;

	case VtRping:
		break;

	case VtThello:
		if(vtgetstring(p, &f->version) < 0
		|| vtgetstring(p, &f->uid) < 0
		|| packetconsume(p, buf, 2) < 0)
			goto Err;
		f->strength = buf[0];
		f->ncrypto = buf[1];
		if(f->ncrypto){
			f->crypto = vtmalloc(f->ncrypto);
			if(packetconsume(p, buf, f->ncrypto) < 0)
				goto Err;
		}
		if(packetconsume(p, buf, 1) < 0)
			goto Err;
		f->ncodec = buf[0];
		if(f->ncodec){
			f->codec = vtmalloc(f->ncodec);
			if(packetconsume(p, buf, f->ncodec) < 0)
				goto Err;
		}
		break;

	case VtRhello:
		if(vtgetstring(p, &f->sid) < 0
		|| packetconsume(p, buf, 2) < 0)
			goto Err;
		f->rcrypto = buf[0];
		f->rcodec = buf[1];
		break;

	case VtTgoodbye:
		break;

	case VtTread:
		if(packetconsume(p, f->score, VtScoreSize) < 0
		|| packetconsume(p, buf, 2) < 0)
			goto Err;
		f->blocktype = vtfromdisktype(buf[0]);
		if(~f->blocktype == 0)
			goto Err;
		switch(packetsize(p)) {
		default:
			goto Err;
		case 2:
			if(packetconsume(p, buf, 2) < 0)
				goto Err;
			f->count = (buf[0] << 8) | buf[1];
			break;
		case 4:
			if(packetconsume(p, buf, 4) < 0)
				goto Err;
			f->count = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
			break;
		}
		break;

	case VtRread:
		f->data = packetalloc();
		packetconcat(f->data, p);
		break;

	case VtTwrite:
		if(packetconsume(p, buf, 4) < 0)
			goto Err;
		f->blocktype = vtfromdisktype(buf[0]);
		if(~f->blocktype == 0)
			goto Err;
		f->data = packetalloc();
		packetconcat(f->data, p);
		break;

	case VtRwrite:
		if(packetconsume(p, f->score, VtScoreSize) < 0)
			goto Err;
		break;

	case VtTsync:
		break;

	case VtRsync:
		break;
	}

	if(packetsize(p) != 0)
		goto Err;

	return 0;

Err:
	werrstr("bad packet");
	vtfcallclear(f);
	return -1;
}

void
vtfcallclear(VtFcall *f)
{
	vtfree(f->error);
	f->error = nil;
	vtfree(f->uid);
	f->uid = nil;
	vtfree(f->sid);
	f->sid = nil;
	vtfree(f->version);
	f->version = nil;
	vtfree(f->crypto);
	f->crypto = nil;
	vtfree(f->codec);
	f->codec = nil;
	vtfree(f->auth);
	f->auth = nil;
	packetfree(f->data);
	f->data = nil;
}
