/*
	ZNetPacketChannel.hpp
	Author: Patrick Baggett	<ptbaggett@762studios.com>
	Created: 6/14/2013

	Purpose:

	** NOT PART OF PUBLIC SDK **
	This class is not part of the public SDK; its fields and methods are not present
	in the documentation and cannot be guaranteed in future revisions.
	** NOT PART OF PUBLIC SDK **

	Queue of incoming and outgoing packets.

	License:

	Copyright 2013, 762 Studios
*/

#pragma once

#ifndef _ZNETPACKETCHANNEL_HPP
#define _ZNETPACKETCHANNEL_HPP

#include <ZSTL/ZList.hpp>
#include <ZNet/ZNetConsts.hpp>

struct ZNetPacket;
class ZNetHost;

namespace ZNetPrivate { struct ZNetMessageContainer; }	//urgh

class ZNetPacketChannel
{
	public:
		struct ZNetQueuedPacket
		{
			uint64_t timeSent;		//< Time this packet was sent (or 0 for never)
			ZNetPacket* packet;		//< Logical packet data (payload)
			uint32_t subsequence;	//< For data packets, this is the number of bytes successfully acked on the remote end
			uint32_t command;		//< Logical packet command such as ZNETCMD_CONNECT, ZNETCMD_DATA, ...
			uint16_t sequence;		//< Logical packet sequence
		};

		ZNetPacketChannel() :
		packetCount(0), overflowLimit(1024), localAck(0), remoteAck(0), nextSequenceNumber(1), critical(false)
		{

		}

		void SetOverflowThreshold(uint32_t _overflowLimit) { overflowLimit = _overflowLimit; }


		/*
			ZNetPacketChannel::QueueForSending()

			Attempts to queue a new packet to the channel. If the overflow limit is reached,
			this returns false and the client should be disconnected.
		*/
		bool QueueForSending(ZNetPacket* packet, uint32_t command);

		bool QueueForReceiving();


		/*
			ZNetPacketChannel::SetChannelId()

			Sets the channel's ID value for use in outgoing packets.
		*/
		void SetChannelId(uint32_t chId) { channelId = chId; }

		/*
			ZNetPacketChannel::FillBuffer()

			Fills a buffer with as many packets as the buffer can hold. If all packets
			have been added, this returns true. If there was not enough space to hold
			all of the packets, this returns false and the index to resume at is provided.

			@param buffer - Input buffer pointer
			@param bufferSize - The size of the buffer pointed to by buffer[]
			@param packetStartIndex - The starting index. Should start at 0, and use the value returned in restartIndexReturn to resume
			@param restartIndexReturn - The index to resume packet storage.
			@param bytesWritten - The number of bytes written
		*/
		bool FillBuffer(uint8_t* buffer, uint32_t bufferSize, uint32_t packetStartIndex, uint32_t* restartIndexReturn, uint32_t* bytesWritten);

		/*
			ZNetPacketChannel::Deinitialize()
		*/
		void Deinitialize();


		/*
			ZNetPacketChannel::UpdateRemoteAck()

			Updates the remote ack counter and dequeues packets as necessary.

			@param newHighest - The new highest sequence number
			@param pingAdjust - Set to the current ping value, this value is adjusted by the RTT on a per-packet basis
		*/
		void UpdateRemoteAck(uint16_t newHighest, int32_t* pingAdjust);

		/*
			ZNetPacketChannel::UpdateLocalAck()

			Updates the local sequence counter for this time period.
		*/
		void UpdateLocalAck(uint16_t seq);

		/*
			ZNetPacketChannel::QueueLocally()

			Queue a received packet for local consumption. Since packets have an ordering and they may be received out of 
			order (e.g. send 1, 2, 3 may be processed as 2, 1, 3), this allows ZNet to reorder them and generate the
			appropriate sequence of events.
		*/
		bool QueueLocally(const ZNetPacketChannel::ZNetQueuedPacket* container);

		/*
			ZNetPacketChannel::QueueData()

			Queue a received data packet. This has one of three effects:
			1) Full data packet received (e.g. short packet), so it just acts like QueueLocally()
			2) Fragmented data packet, did not exist before now -- creates a reassembly buffer.
			3) Fragmented data packet, did exist -- adds to reassembly and check if reassembly is complete. If it is, QueueLocally().
		*/		
		bool QueueData(ZNetPrivate::ZNetMessageContainer* data);

		void ProcessLocalAcks();

		void SetHost(ZNetHost* _host) { host = _host; }

		ZNetHost* GetHost() const { return host; }

		uint16_t GetLocalAck() { return localAck; }

		//Return the highest sequence number for this channel. This is one less than the "next", intuitively.
		uint16_t GetHighestSent() { return nextSequenceNumber == 0 ? UINT16_MAX : nextSequenceNumber; }
	private:

		struct ZNetDataReassemblyPacket
		{
			ZNetPacket* packet;		//< Logical packet data (not yet fully assembled)
			uint32_t subsequence;	//< Highest contiguous amount of data received
			uint16_t sequence;		//< The sequence number of this packet
		};

		ZList<ZNetQueuedPacket> packets;	//List of packets we've sent (or will send) but have not yet been acknowledged
		ZList<ZNetDataReassemblyPacket> reassembly;
		ZList<ZNetQueuedPacket> assembled;
		ZList<uint16_t> sequencesFound;	//The sequence numbers found this time around (sorted)
		ZNetHost* host;					//Owner (used for memory allocations)
		uint32_t packetCount;			//ZList::Size() is O(n), keep track manually
		uint32_t overflowLimit;			//When this many outgoing packets are unacknowledged,
		uint32_t channelId;				//This channel's ID value for outgoing packets
		uint16_t localAck;				//Local acknowledge counter (i.e. highest number of incoming packets we've confirmed)
		uint16_t remoteAck;				//Remote acknowledge counter (i.e. highest number the remote host has told us it has confirmed)
		uint16_t nextSequenceNumber;	//Next outgoing sequence number

		bool critical;					//Is the remote host in a critical state?

};

#endif

