// $Id: SerializationFormat.cc,v 1.2 2003/09/02 04:16:06 vern Exp $

#include <ctype.h>

#include "net_util.h"
#include "SerializationFormat.h"

SerializationFormat::SerializationFormat()
	{
	own = false;
	buffer = 0;
	}

SerializationFormat::~SerializationFormat()
	{
	if ( own && buffer )
		delete [] buffer;
	}

void SerializationFormat::StartRead(char* data, uint32 arg_len)
	{
	buffer = data;
	len = arg_len;
	pos = 0;
	own = false;
	}

void SerializationFormat::EndRead()
	{
	buffer = 0;
	}

void SerializationFormat::StartWrite()
	{
	if ( buffer && own )
		delete [] buffer;

	buffer = new char[INITIAL_SIZE];
	len = INITIAL_SIZE;
	pos = 0;
	own = true;
	}

uint32 SerializationFormat::EndWrite(char** data)
	{
	*data = buffer;
	own = false;
	return pos;
	}

bool SerializationFormat::ReadData(void* b, size_t count)
	{
	if ( pos + count > len )
		{
		error("data underflow during read in binary Format");
		return false;
		}

	memcpy(b, buffer + pos, count);
	pos += count;
	return true;
	}

void SerializationFormat::WriteData(const void* b, size_t count)
	{
	// Increase buffer if necessary.
	while ( pos + count > len )
		{
		len = pos + count + INITIAL_SIZE;
		char* tmp = new char[len];
		memcpy(tmp, buffer, pos);
		delete [] buffer;
		buffer = tmp;
		}

	memcpy(buffer + pos, b, count);
	pos += count;
	}


BinarySerializationFormat::BinarySerializationFormat()
	{
	}

BinarySerializationFormat::~BinarySerializationFormat()
	{
	}

bool BinarySerializationFormat::Read(int* v)
	{
	uint32 tmp;
	if ( ! ReadData(&tmp, sizeof(tmp)) )
		return false;

	*v = (int) ntohl(tmp);
	return true;
	}

bool BinarySerializationFormat::Read(uint32* v)
	{
	if ( ! ReadData(v, sizeof(*v)) )
		return false;

	*v = ntohl(*v);
	return true;
	}

bool BinarySerializationFormat::Read(bool* v)
	{
	char c;
	if ( ! ReadData(&c, 1) )
		return false;

	*v = c == 'T' ? true : false;
	return true;
	}

bool BinarySerializationFormat::Read(double* d)
	{
	if ( ! ReadData(d, sizeof(*d)) )
		return false;

	*d = ntohd(*d);
	return true;
	}

bool BinarySerializationFormat::Read(char* v)
	{
	return ReadData(v, 1);
	}

bool BinarySerializationFormat::Read(char** str, int* len)
	{
	int l;
	if ( ! ReadData(&l, sizeof(l)) )
		return false;

	l = ntohl(l);
	*str = new char[l + 1];

	if ( ! ReadData(*str, l) )
		{
		delete [] str;
		*str = 0;
		return false;
		}

	if ( len )
		*len = l;
	else
		{
		// If len isn't given, make sure that the string
		// doesn't contain any nulls.
		for ( int i = 0; i < l; i++ )
			if ( ! (*str)[i] )
				{
				error("binary Format: string contains null; replaced by '_'");
				(*str)[i] = '_';
				}
		}

	(*str)[l] = '\0';

	return true;
	}

void BinarySerializationFormat::Write(char v, const char* tag)
	{
	return WriteData(&v, 1);
	}

void BinarySerializationFormat::Write(uint32 v, const char* tag)
	{
	v = htonl(v);
	return WriteData(&v, sizeof(v));
	}

void BinarySerializationFormat::Write(int v, const char* tag)
	{
	uint32 tmp = htonl((uint32) v);
	return WriteData(&tmp, sizeof(tmp));
	}

void BinarySerializationFormat::Write(double d, const char* tag)
	{
	d = htond(d);
	return WriteData(&d, sizeof(d));
	}

void BinarySerializationFormat::Write(bool v, const char* tag)
	{
	char c = v ? 'T' : 'F';
	return WriteData(&c, 1);
	}

void BinarySerializationFormat::Write(const char* s, const char* tag)
	{
	return Write(s, strlen(s), tag);
	}

void BinarySerializationFormat::WriteOpenTag(const char* tag)
	{
	}

void BinarySerializationFormat::WriteCloseTag(const char* tag)
	{
	}

void BinarySerializationFormat::WriteSeparator()
	{
	}

void BinarySerializationFormat::Write(const char* buf, int len, const char* tag)
	{
	uint32 l = htonl(len);
	WriteData(&l, sizeof(l));
	WriteData(buf, len);
	}

XMLSerializationFormat::XMLSerializationFormat()
	{
	}

XMLSerializationFormat::~XMLSerializationFormat()
	{
	}

bool XMLSerializationFormat::Read(int* v)
	{
	internal_error("no reading of xml");
	return false;
	}

bool XMLSerializationFormat::Read(uint32* v)
	{
	internal_error("no reading of xml");
	return false;
	}

bool XMLSerializationFormat::Read(bool* v)
	{
	internal_error("no reading of xml");
	return false;
	}

bool XMLSerializationFormat::Read(double* d)
	{
	internal_error("no reading of xml");
	return false;
	}

bool XMLSerializationFormat::Read(char* v)
	{
	internal_error("no reading of xml");
	return false;
	}

bool XMLSerializationFormat::Read(char** str, int* len)
	{
	internal_error("no reading of xml");
	return false;
	}

void XMLSerializationFormat::Write(char v, const char* tag)
	{
	WriteElem(tag, "char", &v, 1);
	}

void XMLSerializationFormat::Write(uint32 v, const char* tag)
	{
	const char* tmp = fmt("%u", v);
	WriteElem(tag, "uint32", tmp, strlen(tmp));
	}

void XMLSerializationFormat::Write(int v, const char* tag)
	{
	const char* tmp = fmt("%d", v);
	WriteElem(tag, "int", tmp, strlen(tmp));
	}

void XMLSerializationFormat::Write(double d, const char* tag)
	{
	const char* tmp = fmt("%f", d);
	WriteElem(tag, "double", tmp, strlen(tmp));
	}

void XMLSerializationFormat::Write(bool v, const char* tag)
	{
	if ( v )
		WriteElem(tag, "bool", "true", 4);
	else
		WriteElem(tag, "bool", "false", 5);
	}

void XMLSerializationFormat::Write(const char* s, const char* tag)
	{
	WriteElem(tag, "string", s, strlen(s));
	}

void XMLSerializationFormat::WriteOpenTag(const char* tag)
	{
	WriteData("<", 1);
	WriteData(tag, strlen(tag));
	WriteData(">", 1);
	}

void XMLSerializationFormat::WriteCloseTag(const char* tag)
	{
	WriteData("</", 2);
	WriteData(tag, strlen(tag));
	WriteData(">", 1);
	}

void XMLSerializationFormat::WriteSeparator()
	{
	WriteData("\n", 1);
	}

void XMLSerializationFormat::Write(const char* buf, int len, const char* tag)
	{
	WriteElem(tag, "string", buf, len);
	}

void XMLSerializationFormat::WriteEncodedString(const char* s, int len)
	{
	while ( len-- )
		{
		if ( ! isprint(*s) )
			{
			WriteData("&#x", 3);
			const char* tmp = fmt("%.4x", (int)* s++);
			WriteData(tmp, 4);
			WriteData(";", 1);
			}
		else
			{
			switch ( *s ) {
			case '"':
				WriteData("&quot;", 6);
				break;
			case '&':
				WriteData("&amp;", 5);
				break;
			case '<':
				WriteData("&lt;", 4);
				break;
			case '>':
				WriteData("&gt;", 4);
				break;
			default:
				WriteData(s, 1);
			}

			++s;
			}
		}
	}

void XMLSerializationFormat::WriteElem(const char* tag, const char* type,
					const char* content, int len)
	{
	if ( ! tag )
		return;

	WriteData("<", 1);
	WriteData(tag, strlen(tag));
#if 0
	WriteData(" type=\"", 7);
	WriteData(type, strlen(type));
	WriteData("\"", 1);
#endif
	WriteData(">", 1);
	WriteEncodedString(content, len);
	WriteData("</", 2);
	WriteData(tag, strlen(tag));
	WriteData(">", 1);
	}
