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

	Purpose:

	ZNet private functions and definitions

	License:

	Copyright 2013, 762 Studios
*/

#pragma once

#ifndef _ZNETPRIVATE_HPP
#define _ZNETPRIVATE_HPP

#include <ZUtil/ZBinaryBufferReader.hpp>
#include <ZUtil/ZBinaryBufferWriter.hpp>
#include <SST/SST_Net.h>

//========================================
// Wire format constants
//========================================

#define ZNET_BYTEORDER	SST_BIG_ENDIAN	//< Global byte order for all of ZNet


#define ZNET_MAGIC			0x5A	//'Z' in ASCII (i.e. uppercase zed)

#define ZNET_SYSCHANNEL		0x00u	//System channel

#define ZNETCMD_CONNECT		0x00u	//Connect request, sent from client to server
#define ZNETCMD_DATA		0x01u	//Data packet (or fragment of one)
#define ZNETCMD_DISCONNECT	0x02u	//Disconnect request
#define ZNETCMD_ACK			0x03u	//Acknowledge a range of packets, possibly including a subsequence value
#define ZNETCMD_CONNRESP	0x04u	//Connection response, send from server to client
#define ZNETCMD_MAX			0x05u	//First invalid value

//For ZNETCMD_ACK/DATA, the "flag" field will be set to one of these values.
#define ZNETCMDFLAGS_LEN8	0x00u
#define ZNETCMDFLAGS_LEN16	0x01u
#define ZNETCMDFLAGS_LEN32	0x02u

#define ZNETCMDFLAGS_FULL	0x01u

/*
	Wire Format

	RAW PACKET {
		u8 ZNET_MAGIC
		[ messages ]
	}


	MESSAGE {
		u8 channel
		u4 flags
		u4 cmd
		u16 seq
		[ message data ]
	}

	CONNECT MESSAGE {
		u32 userdata
	}

	DATA MESSAGE {
		[if flags == ZNETCMDFLAGS_LEN8]
			u8 offset
			u8 max
			u8 length
		[if flags == ZNETCMDFLAGS_LEN16]
			u16 offset
			u16 max
			u16 length			
		[if flags == ZNETCMDFLAGS_LEN32]
			u32 offset
			u32 max
			u32 length
	
		u8 data[ length ]
	}

	DISCONNECT MESSAGE {
		u32 userdata
	}
	
	ACK MESSAGE {
		u16 highestAcked		: I have received up to this point
		u16 localSequence		: I have sent up to this point

		[if flags == ZNETCMDFLAGS_LEN8]
			u8 subsequence8
		[if flags == ZNETCMDFLAGS_LEN16]
			u16 subsequence16
		[if flags == ZNETCMDFLAGS_LEN32]
			u32 subsequence32

	}


*/

#define ZNET_WIRESIZE_PACKET_HEADER		(sizeof(uint8_t))						//< Size of a packet header (overhead)
#define ZNET_WIRESIZE_MESSAGE_HEADER	(2*sizeof(uint8_t) + sizeof(uint16_t))	//< Size of a message header (overhead)

#define ZNET_WIRESIZE_PACKET_MINIMUM	ZNET_WIRESIZE_PACKET_HEADER + ZNET_WIRESIZE_MESSAGE_HEADER

class ZNetPeer;

namespace ZNetPrivate
{

	//Used for CONNECT, DISCONNECT, CONNRESP
	struct ZNetMessageConnect
	{
		uint32_t userdata;
	};

	//Used for ACK, ACKSUBSEQ
	struct ZNetMessageAck
	{
		uint32_t subsequence;
		uint16_t highestReceived;	//Highest contiguous sequence remote host has received
		uint16_t highestSent;		//Highest local sequence remote host has sent
	};

	struct ZNetMessagePing
	{
		uint32_t token;
	};

	//Used for DATA8, DATA16, DATA32
	struct ZNetMessageData
	{
		uint8_t* data;		//Pointer within the packet
		uint32_t offset;	//Offset into reassembly buffer
		uint32_t maxSize;	//Maximum size of reassembled packet
		uint32_t length;	//Length of this packet
	};

	union ZNetMessage
	{
		ZNetMessageConnect connect;
		ZNetMessageAck ack;
		ZNetMessageData data;
		ZNetMessagePing ping;
	};

	struct ZNetMessageContainer
	{
		uint32_t channel;
		uint32_t command;
		uint32_t flags;
		uint16_t sequence;
		ZNetMessage parsed;
	};

	/*
		ZNetPrivate::ParseWirePacketHeader()

		Parses the wire format packet header and returns true/false if successful.

		@param reader - The reader set to the first byte of the packet in the proper byte order
		@return (bool) - True if the header is valid, false if not.
	*/
	bool ParseWirePacketHeader(ZBinaryBufferReader* reader);

	void WriteWirePacketHeader(ZBinaryBufferWriter* writer);

	void WriteWireMessageHeader(ZBinaryBufferWriter* writer, uint32_t channel, uint32_t flags, uint32_t command, uint16_t sequenceNumber);

	bool SendAll(SST_Socket s, const SST_NetAddress* addr, uint8_t* data, uint32_t length);

	/*
		ZNetPrivate::ReadNextMessage()

		Attempt to parse the next message from a stream.

		@param reader - The binary reader
		@param msgReturn - The parsed message (only when returning > 0)
		@return (int) - < 0: error in stream. 0: end of stream. > 0 successfully parsed message
	*/
	int ReadNextMessage(ZBinaryBufferReader* reader, ZNetPrivate::ZNetMessageContainer* msgReturn);

	/*
		ZNetPrivate::MinimumSizeForCommand()

		Gets the minimum size (in bytes) of a command's payload.

		@return (uint32_t) - The number of bytes
	*/
	uint32_t MinimumSizeForCommand(uint32_t command);

	/*
		ZNetPrivate::WireSizeForCommand()

		Gets the approximate size for a simple command. This means packet header + message header + payload

		@return (uint32_t) - The number of bytes
	*/
	inline uint32_t WireSizeForSimpleCommand(uint32_t command) { return MinimumSizeForCommand(command) + ZNET_WIRESIZE_PACKET_MINIMUM; }

	/*
		ZNetPrivate::SequenceAfter()

		Checks if seq1 comes after seq2, taking into account sequence number wraps.

		@param seq1 - The sequence number to check
		@param seq2 - The existing sequence number
		@return (bool) - True if seq1 comes after seq2, false if seq2 comes first or equals seq1
	*/
	inline bool SequenceAfter(uint16_t seq1, uint16_t seq2)
	{
		return ((seq2 > seq1) && (seq2 - seq1 > UINT16_MAX/2)) || ((seq1 > seq2) && (seq1 - seq2 <= UINT16_MAX/2));
	}

	inline uint32_t PackedIntegerSize(uint32_t value)
	{
		if(value <= UINT8_MAX) return sizeof(uint8_t);
		if(value <= UINT16_MAX) return sizeof(uint16_t);

		return sizeof(uint32_t);
	}

	void SendPingCommand(bool isReply, uint32_t givenToken, ZNetPeer* peer);

	inline bool IsValidCommand(uint32_t command) { return (command < ZNETCMD_MAX); }

	inline uint32_t ZNetByteOrder32(uint32_t v)
	{
		#if (SST_BYTEORDER_HOST != ZNET_BYTEORDER)
		v = SST_OS_ByteSwap32(v);
		#endif
		return v;
	}
}

#endif

