#include "cygwin.h"
#include "sPacket.h"
#include "cSocket.h"
#include "protocol.h"
#include "opcodes.h"

int	 sPacket::count = 0;

#define tReadOK	9
void sPacket::Proto (uint8_t p ) {{{
	ASSERT(data != NULL);
	*(reinterpret_cast<uint8_t *>(data + 0)) = proto = p;
	}}}
void sPacket::Paylen(size_t p ) {{{
	ASSERT(data != NULL);
	*(reinterpret_cast<uint32_t*>(data + 1)) = pay_len = p;
	}}}
void sPacket::Opcode(uint8_t p ) {{{
	ASSERT(data != NULL);
	*(reinterpret_cast<uint8_t *>(data + 5)) = opcode = p;
	}}}
sPacket            ::sPacket  () {{{
	size = 0;
	count++;	
	data = NULL;
	status  = 0;
	pos = 0;
	}}}
sPacket            ::~sPacket (void) {{{
	if (data != NULL) free (data);
	if (status != 103 && status >= 101) {
		printf("sPacket status %i\n", status);
		ASSERT(0);
		}
	count--;
	}}}
int         sPacket::Read     (class cSocket *Socket) {{{
	ASSERT (Socket != NULL);
	ASSERT(Socket->validSocket());
	ASSERT (status != -1);
	if (status <= 0) {{{ // Lese Header vorbereiten
		pos = 6;
		size = 6;

		if (data == NULL) {
		  data = reinterpret_cast<unsigned char*>(MALLOC (size));
		  data_length = size;
		}
		else {
		  if (data_length < size) {
		    data = reinterpret_cast<unsigned char*>(REALLOC (data, size));
		    data_length = size;
		  }
		}
		Proto (0);
		status = 1;
		}}}
	if (status == 1) {{{
		size_t ret = Socket->Read (data + 6 - pos, pos);	// never return - value
		pos -= ret;
		if (pos > 0) return 0;
		proto   = *(reinterpret_cast<uint8_t *>(data + 0));
		pay_len = *(reinterpret_cast<uint32_t*>(data + 1));
		opcode  = *(reinterpret_cast<uint8_t *>(data + 5));
		if (proto != OP_EDONKEYHEADER && proto != 0xD4 && proto != 0xC5) { 
			Socket->Close();
			status = -1;
			return -1;
			}
		status = (pay_len == 1)?tReadOK:2;
		if (status == tReadOK) return 0;
		size = 5 + pay_len;
		if (size > 30000) {{{ // Limit packet size
			printf("OVERSIZE PACKET\n");
			Socket->Close();
			status = 3;
			return 0;
			}}}
		ASSERT(data != NULL);
		if (data_length < size) {
		  data = reinterpret_cast<unsigned char*>(REALLOC (data, size));
		  data_length = size;
		}
	}}}
	if (status == 2) {{{ // Lese Daten
		size_t missing = pay_len - pos - 1;
		size_t ret = Socket->Read (data + 6 + pos, missing);	// never return - value
		if (ret == 0) return  0;
		pos += ret;
		if (pos == pay_len - 1) {
			pos = 0;
			status = tReadOK;
			return 1;
			}
		}}}
	return 0;				// Solte nie hier ankommen
	}}}
int         sPacket::Send     (class cSocket *Socket) {{{
	ASSERT (Full());	
	ASSERT (Socket != NULL);
	ASSERT (status == 101);
	ASSERT(Socket->validSocket());
	Socket->Write (data, size); // -2 (-1 Byte wegen array[1] und Code ist im Header)
	status = 103;
	return 1;
	}}}
bool        sPacket::GET_HASH (tHash &dst) {{{
	ASSERT (tReadOK == status);
	ASSERT (pay_len - pos >= sizeof(tHash));
	ASSERT (dst != NULL);
	memcpy (dst, data + 6 + pos, sizeof(tHash));
	pos += sizeof(tHash); 
	return true;
	}}}
float       sPacket::GET_F4   (void) {{{
	float    val = 0;
	ASSERT (tReadOK == status);
	ASSERT (pay_len - pos >= sizeof (val));
	memcpy (&val, data + 6 + pos, sizeof(val));
	pos += sizeof(val); 
	return val;
	}}}
uint32_t    sPacket::GET_U4   (void) {{{
	uint32_t val = 0;
	ASSERT (tReadOK == status);
	ASSERT (pay_len - pos >= sizeof (val));
	memcpy (&val, 6 + data + pos, sizeof(val));
	pos += sizeof(val); 
	return ctol32(val);
	}}}
uint16_t    sPacket::GET_U2   (void) {{{
	uint16_t val = 0;
	ASSERT (tReadOK == status);
	ASSERT (pay_len - pos >= sizeof (val));
	memcpy (&val, 6 + data + pos, sizeof(val));
	pos += sizeof(val); 
	return ctol16(val);
	}}}
uint8_t     sPacket::GET_U1   (void) {{{
	uint8_t  val = 0;
	ASSERT (tReadOK == status);
	ASSERT (pay_len - pos >= sizeof (val));
	memcpy (&val, 6 + data + pos, sizeof(val));
	pos += sizeof(val); 
	return val;
	}}}
char*       sPacket::GET_S2   (void) {{{
	uint16_t Size = GET_U2 ();
	ASSERT (tReadOK == status);
	if(Size == 0) return STRDUP ("");
	char *val = reinterpret_cast<char*>(MALLOC (Size + 1));
	memcpy (val, data + 6 + pos, Size);
	val[Size] = 0;
	pos += Size;
	return val;
	}}}
void        sPacket::GET_BLIND(unsigned len) {{{
	ASSERT (tReadOK == status);
	ASSERT (pay_len - pos >= len);
	pos += len;
	}}}
unsigned char *sPacket::DATA     (void) {{{
	return data + 6 + pos; 
	}}}
void        sPacket::Dump     (void) const {{{
	printf("\r\nprotokol:%02x\r\n", proto);
	printf("length    : %u\r\n"   , pay_len);
	printf("position  : %#08X\r\n", pos);
	unsigned tmp = opcode;
	printf("command   : 0x%02hx\r\n", tmp);
	for (uint32_t i=0; i < pay_len - 1; i++) {
		if(i%16==0) printf("\r\n%#08X ", i);
		tmp = data[6+i];
		printf ("%02hx ", tmp);
		}
	printf("\r\n");
	}}}
bool        sPacket::readReset(void) {{{
	if (status != tReadOK) {
		printf("Status %i\r\n", status);
		}
	ASSERT (status == tReadOK);
	if (pos != pay_len) {
//		printf("opcode 0x%02X\n", opcode);
//		printf("pos %i\n", pos);
//		printf("pay_len = %i\n", pay_len);
//		Dump();
//		abort();
		}
	status = 0;
	pos = 0;
	return true;
	}}}
/*
 * vim600: fdm=marker
 */

