// Common, group Encoding 
// Copyright Alexander Liss

#include "string.h"
#include "wirepack.h"
#include "porting.h"

using namespace Porting;

const int INT=4 ;
const int LONG=4 ;
const int SHORT=2 ;

static
void swap(unsigned char* d, unsigned char* z, int size)
{
	for(int i=size-1,s=size;i>=0;--i)
	{ 
		if(z[i]) break;
		--s;
	}

	for(i=0;i<s;++i)
		d[size-i]=z[i];
}


template<class T>
int local_put(T data,int t,CommBuffer* write_buffer)
{
	if(!write_buffer) return 1;

	unsigned char buf[LONG];
	memset(buf,0,LONG);

	unsigned char* z=(unsigned char*)&data;
	int s=sizeof(data);

	if(s>t)
	{
		for(int i=t-1;i<=s;++i)
			if(z[i]) return 2;

		s=t;
	}

	if(big_endian)
		memcpy(buf,z,s);
	else
		swap(buf,z,s);

	return write_buffer->append(buf,t);

}


template<class T>
int local_get(T& data,int t,const CommBuffer* read_buffer, ReadControl* read_control)
{
	if(!read_buffer || !read_control) return 1;
	if(read_buffer->data_left(*read_control)<t) return 2;

	data=0;

	unsigned char* z=read_buffer->read_start(*read_control);
	int s=sizeof(data);

	if(s>t)
	{
		for(int i=t-1;i<=s;++i)
			if(z[i]) return 3;

		s=t;
	}


	if(big_endian)
		memcpy(&data,z,s);
	else
		swap((unsigned char*)&data,z,s);

	read_control->offset+=t;

	return 0;
}


	// WirePacker

int WirePacker::put(const void * data,int size)
{
	if(!write_buffer) return 1;

	return write_buffer->append((const unsigned char*)data,size);
}

int WirePacker::get(void * data,int size)
{
	if(!read_buffer || !read_control) return 1;

	if(read_buffer->data_left(*read_control)<size) return 2;

	memcpy(data,read_buffer->read_start(*read_control),size);

	read_control->offset+=size;

	return 0;
}




int WirePacker::put(unsigned int data)
{
	return local_put(data,INT,write_buffer);
}

int WirePacker::get(unsigned int& data)
{
	return local_get(data,INT,read_buffer,read_control);
}



int WirePacker::put(unsigned long data)
{
	return local_put(data,LONG,write_buffer);
}

int WirePacker::get(unsigned long& data)
{
	return local_get(data,LONG,read_buffer,read_control);
}



int WirePacker::put(unsigned short data)
{
	return local_put(data,SHORT,write_buffer);
}

int WirePacker::get(unsigned short& data)
{
	return local_get(data,SHORT,read_buffer,read_control);
}



int WirePacker::put(unsigned char data)
{
	if(!write_buffer) return 1;

	return write_buffer->append(1,(char)data);

}

int WirePacker::get(unsigned char& data)
{
	if(!read_buffer || !read_control) return 1;

	if(read_buffer->data_left(*read_control)<1) return 2;

	data=*read_buffer->read_start(*read_control);

	read_control->offset++;

	return 0;
}


int WirePacker::put_tail(int data, int bytes)
{
	if(bytes>4 || bytes<1) return 1;

	return local_put(data,bytes,write_buffer);
}

int WirePacker::get_tail(int& data, int bytes)
{
	if(bytes>4 || bytes<1) return 1;

	return local_get(data,bytes,read_buffer,read_control);
}

int WirePacker::put(const CommBuffer& data, int bytes)
{
	if(!write_buffer) return 1;

	if(bytes>4 || bytes<1) return 2;

	int g=put_tail(data.data_size,bytes);

	if(!g) g=write_buffer->append(data.write_start(),data.data_size);

	return g;
}

int WirePacker::get(CommBuffer& data, int bytes)
{
	if(!read_buffer || !read_control) return 1;

	if(bytes>4 || bytes<1) return 2;

	int size=0;

	int g=get_tail(size,bytes);

	if(g) return 3;

	if(size>read_buffer->data_left(*read_control))
		return 4;

	g=data.append(read_buffer->read_start(*read_control),size);

	if(g) return 4;

	read_control->offset+=size;

	return 0;
}

	// functions



int put_message_size(CommBuffer& buf,unsigned int size)
{
	return WirePacker(buf).put(size);
}

int prepend_message_size(CommBuffer& d,unsigned int size)
{
	unsigned char buf[INT];
	memset(buf,0,INT);

	unsigned char* z=(unsigned char*)&size;
	int s=sizeof(size);

	if(s>INT)
	{
		for(int i=INT-1;i<=s;++i)
			if(z[i]) return 1;

		s=INT;
	}

	if(big_endian)
		memcpy(buf,z,s);
	else
		swap(buf,z,s);

	return d.prepend(buf,INT);
}


unsigned int get_message_size(const CommBuffer& buf,ReadControl& c)
{
	unsigned int size=0;

	WirePacker(buf,c).get(size);

	return size;
}