/*
	ZNetPrivate.cpp
	Author: Patrick Baggett	<ptbaggett@762studios.com>
	Created: 7/19/2013

	Purpose:

	ZNet private functions and definitions

	License:

	Copyright 2013, 762 Studios
*/

#include "ZNetPrivate.hpp"
#include <ZNet/ZNetPeer.hpp>
#include <SST/SST_Assert.h>

/*************************************************************************/

bool ZNetPrivate::ParseWirePacketHeader(ZBinaryBufferReader* reader)
{
	//Packet is absolutely too small to have any useful data (i.e. size < minimum overhead)
	if(reader->GetLength() < ZNET_WIRESIZE_PACKET_MINIMUM)
		return false;

	if(reader->ReadU8() != ZNET_MAGIC)
		return false;

	return true;
}

/*************************************************************************/

void ZNetPrivate::WriteWirePacketHeader(ZBinaryBufferWriter* writer)
{
	writer->WriteU8(ZNET_MAGIC);
}

/*************************************************************************/

void ZNetPrivate::WriteWireMessageHeader(ZBinaryBufferWriter* writer, uint32_t channel, uint32_t flags, uint32_t command, uint16_t sequenceNumber)
{
	uint8_t merged = (uint8_t)(((flags & 0x0F) << 4) | (command & 0x0F));

	writer->WriteU8((uint8_t)channel);
	writer->WriteU8(merged);
	writer->WriteU16(sequenceNumber);
}

/*************************************************************************/

bool ZNetPrivate::SendAll(SST_Socket s, const SST_NetAddress* addr, uint8_t* data, uint32_t length)
{
	size_t totalSent;
	if(SST_Net_SendTo(s, data, length, 0, addr, &totalSent) == SSTNETRESULT_SUCCESS)
	{
		//Return true if and only if all data was sent
		return ((uint32_t)totalSent == length);
	}

	//Failed to send at all... :(
	return false;
}

/*************************************************************************/

int ZNetPrivate::ReadNextMessage(ZBinaryBufferReader* reader, ZNetPrivate::ZNetMessageContainer* msgReturn)
{
	if(reader->GetLength() - reader->GetOffset() == 0)
		return 0;
	if(!reader->CanRead(ZNET_WIRESIZE_MESSAGE_HEADER))
		return -1; //error in stream

	//Read channel
	msgReturn->channel = reader->ReadU8();
	
	//Read command+flags (merged 8-bit value)
	uint8_t cmdAndFlags = reader->ReadU8();

	msgReturn->command = (cmdAndFlags & 0x0F) >> 0;
	msgReturn->flags = (cmdAndFlags & 0xF0) >> 4;

	msgReturn->sequence = reader->ReadU16();

	//Validate the command is OK
	if(!ZNetPrivate::IsValidCommand(msgReturn->command))
		return -1;

	//TODO: validate flags for each command.

	//Ensure we can read at least the minimum number of bytes required for the command
	if(!reader->CanRead(ZNetPrivate::MinimumSizeForCommand(msgReturn->command)))
		return -1;

	switch(msgReturn->command)
	{
		case ZNETCMD_CONNECT:
		case ZNETCMD_DISCONNECT:
		case ZNETCMD_CONNRESP:
		{
			msgReturn->parsed.connect.userdata = reader->ReadU32();
			break;
		}	
		

		case ZNETCMD_DATA:
		{
			if(msgReturn->flags & ZNETCMDFLAGS_LEN8)
			{
				msgReturn->parsed.data.offset = reader->ReadU8();
				msgReturn->parsed.data.maxSize = reader->ReadU8();
				msgReturn->parsed.data.length = reader->ReadU8();
			}
			else if(msgReturn->flags & ZNETCMDFLAGS_LEN16)
			{
				msgReturn->parsed.data.offset = reader->ReadU16();
				msgReturn->parsed.data.maxSize = reader->ReadU16();
				msgReturn->parsed.data.length = reader->ReadU16();
			}
			else if(msgReturn->flags & ZNETCMDFLAGS_LEN32)
			{
				msgReturn->parsed.data.offset = reader->ReadU32();
				msgReturn->parsed.data.maxSize = reader->ReadU32();
				msgReturn->parsed.data.length = reader->ReadU32();
			}
			msgReturn->parsed.data.data = (uint8_t*)reader->GetBufferReadAddress();

			//Verify access
			if(!reader->CanRead(msgReturn->parsed.data.length))
				return -1;
			break;
		}

		case ZNETCMD_ACK:
		{
			msgReturn->parsed.ack.highestReceived = reader->ReadU16();
			msgReturn->parsed.ack.highestSent = reader->ReadU16();

			//Read 8-bit, 16-bit, or 32-bit subsequence
			if(msgReturn->flags & ZNETCMDFLAGS_LEN8)
				msgReturn->parsed.ack.subsequence = reader->ReadU8();
			else if(msgReturn->flags & ZNETCMDFLAGS_LEN16)
				msgReturn->parsed.ack.subsequence = reader->ReadU16();
			else if(msgReturn->flags & ZNETCMDFLAGS_LEN32)
				msgReturn->parsed.ack.subsequence = reader->ReadU32();
			break;
		}
	}

	//Read a full message
	return 1;
}

/*************************************************************************/

uint32_t ZNetPrivate::MinimumSizeForCommand(uint32_t command)
{
	uint32_t size;
	switch(command)
	{
		case ZNETCMD_CONNECT: size = sizeof(uint32_t); break;
		case ZNETCMD_DATA: size = sizeof(uint8_t)*3; break;
		case ZNETCMD_DISCONNECT: size = sizeof(uint32_t); break;
		case ZNETCMD_ACK: size = 2*sizeof(uint16_t); break;
		case ZNETCMD_CONNRESP: size = sizeof(uint32_t); break;
		default:
			SST_OS_DebugError("Should not reach here");
			size = UINT32_MAX;
			break;
	}

	return size;
}
