/*
	ZNetHost.cpp
	Author: Patrick Baggett	<ptbaggett@762studios.com>
	Created: 6/4/2013

	Purpose:

	ZNet host class, represents the local system

	License:

	Copyright 2013, 762 Studios
*/

#include <ZNet/ZNetHost.hpp>
#include <ZNet/ZNetPacket.hpp>
#include <ZNet/ZNetConsts.hpp>
#include <ZNet/ZNetPeer.hpp>
#include <SST/SST_Alloc.h> //SST_OS_Alloca()
#include "ZNetPrivate.hpp"

#include <SST/SST_OS.h>
#include <ZUtil/ZBinaryBufferReader.hpp>

#include <stdio.h> //HACKHACK

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

ZNetHost::ZNetHost()
{
	mtu = ZNET_MTU_IPV4SAFE;	//Safe, but small MTU as a default
	dropChance = 0;
}

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

ZNetPacket* ZNetHost::CreatePacket(const void* initData, uint32_t dataSize, uint32_t flags)
{
	ZNetPacket* packet;

	//Validate that ONLY legitimate bits are set
	if(flags != 0)
		return NULL;

	//Allocate packet structure + data
	packet = (ZNetPacket*)malloc(sizeof(ZNetPacket) + dataSize);
	if(packet == NULL)
		return NULL;

	packet->flags = flags;
	packet->dataSize = dataSize;
	packet->refCount = 1;

	if(initData != NULL)
		memcpy(packet->GetData(), initData, dataSize);

	return packet;
}

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

bool ZNetHost::GetNextEvent(ZNetEvent* eventReturn)
{
	if(!events.Empty())
	{
		*eventReturn = events.PopFront();
		return true;
	}

	return false;
}

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

bool ZNetHost::SendToPeer(ZNetPeer* peer)
{
	uint8_t* data = (uint8_t*)SST_OS_Alloca(GetMTU());
	bool unsent = false;

	//TODO: No bandwidth allocations done here
	//TODO: send in round-robin fashion across channels

	ZBinaryBufferWriter writer(data, GetMTU(), ZNET_BYTEORDER);
	ZNetPrivate::WriteWirePacketHeader(&writer);

	//OUTER LOOP: for each channel
	for(uint32_t i=0; i<peer->GetNumberChannels(); i++)
	{
		ZNetPacketChannel* channel = peer->GetPacketChannel(i);
		bool moreInChannel = true;
		uint32_t index = 0;
		uint32_t newIndex;
		uint32_t nrWritten;

		//INNER LOOP: fill buffer with channel data
		do 
		{
			uint32_t remain = mtu - (uint32_t)writer.GetOffset();
			
			if(!channel->FillBuffer((uint8_t*)writer.GetBufferWriteAddress(), remain, index, &newIndex, &nrWritten))//TODO: update to make use of ZBinaryBufferWriter interface
			{
				//Buffer is full, send now
				index = newIndex;
				writer.SeekForward(nrWritten);
				if(!ZNetPrivate::SendAll(peer->GetSocket(), peer->GetNetAddress(), data, (uint32_t)writer.GetOffset()))
				{
					printf("SendToPeer FAILED %u bytes\n", (uint32_t)writer.GetOffset());
					return false;
				}
				printf("SendToPeer -> Sent %u bytes\n", (uint32_t)writer.GetOffset());

				//Restart!
				writer.Rewind();
				ZNetPrivate::WriteWirePacketHeader(&writer);
				unsent = false;
			}
			else //Done with this channel
			{
				if(nrWritten > 0 && !unsent)
				{
					unsent = true;
					writer.SeekForward(nrWritten);
				}
				moreInChannel = false;
				
			}

		} while(moreInChannel);

	}

	//Any unsent data?
	if(unsent)
	{
		//Then send it!
		if(!ZNetPrivate::SendAll(peer->GetSocket(), peer->GetNetAddress(), data, (uint32_t)writer.GetOffset()))
		{
			printf("SendToPeer FAILED %u bytes\n", (uint32_t)writer.GetOffset());
			return false;
		}
		printf("SendToPeer -> Sent %u bytes\n", (uint32_t)writer.GetOffset());
	}

	return true;
}
