/* Copyright ©2007-2008 Kris Maglione <fbsdaemon@gmail.com>
 * See LICENSE file for license details.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ixp_local.h"

enum {
	SByte = 1,
	SWord = 2,
	SDWord = 4,
	SQWord = 8,
};

static void
ixp_puint(IxpMsg *msg, uint size, ulong *val) {
	uchar *pos;
	int v;

	if(msg->pos + size <= msg->end) {
		pos = (uchar*)msg->pos;
		switch(msg->mode) {
		case MsgPack:
			v = *val;
			switch(size) {
			case SDWord:
				pos[3] = v>>24;
				pos[2] = v>>16;
			case SWord:
				pos[1] = v>>8;
			case SByte:
				pos[0] = v;
				break;
			}
		case MsgUnpack:
			v = 0;
			switch(size) {
			case SDWord:
				v |= pos[3]<<24;
				v |= pos[2]<<16;
			case SWord:
				v |= pos[1]<<8;
			case SByte:
				v |= pos[0];
				break;
			}
			*val = v;
		}
	}
	msg->pos += size;
}

void
ixp_pu32(IxpMsg *msg, ulong *val) {
	ixp_puint(msg, SDWord, val);
}
void
ixp_pu8(IxpMsg *msg, uchar *val) {
	ulong v;

	v = *val;
	ixp_puint(msg, SByte, &v);
	*val = (uchar)v;
}
void
ixp_pu16(IxpMsg *msg, ushort *val) {
	ulong v;

	v = *val;
	ixp_puint(msg, SWord, &v);
	*val = (ushort)v;
}
void
ixp_pu64(IxpMsg *msg, uvlong *val) {
	ulong vl, vb;

	vl = (uint)*val;
	vb = (uint)(*val>>32);
	ixp_puint(msg, SDWord, &vl);
	ixp_puint(msg, SDWord, &vb);
	*val = vl | ((uvlong)vb<<32);
}

void
ixp_pstring(IxpMsg *msg, char **s) {
	ushort len;

	if(msg->mode == MsgPack)
		len = strlen(*s);
	ixp_pu16(msg, &len);

	if(msg->pos + len <= msg->end) {
		if(msg->mode == MsgUnpack) {
			*s = emalloc(len + 1);
			memcpy(*s, msg->pos, len);
			(*s)[len] = '\0';
		}else
			memcpy(msg->pos, *s, len);
	}
	msg->pos += len;
}

void
ixp_pstrings(IxpMsg *msg, ushort *num, char *strings[]) {
	char *s;
	uint i, size;
	ushort len;

	ixp_pu16(msg, num);
	if(*num > IXP_MAX_WELEM) {
		msg->pos = msg->end+1;
		return;
	}

	SET(s);
	if(msg->mode == MsgUnpack) {
		s = msg->pos;
		size = 0;
		for(i=0; i < *num; i++) {
			ixp_pu16(msg, &len);
			msg->pos += len;
			size += len;
			if(msg->pos > msg->end)
				return;
		}
		msg->pos = s;
		size += *num;
		s = emalloc(size);
	}

	for(i=0; i < *num; i++) {
		if(msg->mode == MsgPack)
			len = strlen(strings[i]);
		ixp_pu16(msg, &len);

		if(msg->mode == MsgUnpack) {
			memcpy(s, msg->pos, len);
			strings[i] = (char*)s;
			s += len;
			msg->pos += len;
			*s++ = '\0';
		}else
			ixp_pdata(msg, &strings[i], len);
	}
}

void
ixp_pdata(IxpMsg *msg, char **data, uint len) {
	if(msg->pos + len <= msg->end) {
		if(msg->mode == MsgUnpack) {
			*data = emalloc(len);
			memcpy(*data, msg->pos, len);
		}else
			memcpy(msg->pos, *data, len);
	}
	msg->pos += len;
}

void
ixp_pqid(IxpMsg *msg, Qid *qid) {
	ixp_pu8(msg, &qid->type);
	ixp_pu32(msg, &qid->version);
	ixp_pu64(msg, &qid->path);
}

void
ixp_pqids(IxpMsg *msg, ushort *num, Qid qid[]) {
	int i;

	ixp_pu16(msg, num);
	if(*num > IXP_MAX_WELEM) {
		msg->pos = msg->end+1;
		return;
	}

	for(i = 0; i < *num; i++)
		ixp_pqid(msg, &qid[i]);
}

void
ixp_pstat(IxpMsg *msg, Stat *stat) {
	ushort size;

	if(msg->mode == MsgPack)
		size = ixp_sizeof_stat(stat) - 2;

	ixp_pu16(msg, &size);
	ixp_pu16(msg, &stat->type);
	ixp_pu32(msg, &stat->dev);
	ixp_pqid(msg, &stat->qid);
	ixp_pu32(msg, &stat->mode);
	ixp_pu32(msg, &stat->atime);
	ixp_pu32(msg, &stat->mtime);
	ixp_pu64(msg, &stat->length);
	ixp_pstring(msg, &stat->name);
	ixp_pstring(msg, &stat->uid);
	ixp_pstring(msg, &stat->gid);
	ixp_pstring(msg, &stat->muid);
}
