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

	Purpose:

	ZNet base class for shared client/server data

	License:
	
	Copyright 2013, 762 Studios
*/

#pragma once

#ifndef _ZNETHOST_HPP
#define _ZNETHOST_HPP

#include <ZNet/ZNetBandwidthMeter.hpp>
#include <ZNet/ZNetEvent.hpp>
#include <ZSTL/ZList.hpp>

struct ZNetPacket;

class ZNetHost
{
	public:
		ZNetHost();
		virtual ~ZNetHost() { }
	
		/*
			ZNetHost::Update()
			
			Fetches and sends data as necessary to perform the given role. See the
			documentation for the specific class. Generally, you need to call this
			each frame.

			@return (int) - Less than 0: error. 0: OK
		*/
		virtual int Update() = 0;

		/*
			ZNetHost::CreatePacket()

			Creates a packet and copies the given initialization data (if any).
			
			@param initData - Data to copy to the new packet, or NULL for uninitialized packet.
			@param dataSize - The size of the packet's payload
			@param flags - The flags. No flags defined, so must be 0
		*/
		ZNetPacket* CreatePacket(const void* initData, uint32_t dataSize, uint32_t flags);
		
		/*
			ZNetHost::HasEvent()

			Returns true if there are queued events. The next call to GetNextEvent()
			is guaranteed to succeed.
			
			@return (bool) - True if at least one event is pending
		*/
		bool HasEvent() { return events.Size() > 0; }

		/*
			ZNetHost::GetNextEvent()

			Attempt to fetch the next event form the queue and returns
			whether one was fetched or not.

			@param eventReturn - Pointer to ZNetEvent structure to receive event data
			@return (bool) - True if an event was returned, false if none was available (and eventReturn is not modified)
		*/
		bool GetNextEvent(ZNetEvent* eventReturn);

		//======================================================================
		// TRIVIAL GETTER / SETTER
		//======================================================================
	
		/*
			ZNetHost::SetIncomingBandwidth()
			
			Sets the desired incoming bandwidth cap.
			
			Note that this is a request made of the remote host; malicious hosts can still
			attempt DoS attacks by sending far above this limit.
			
			@param bwIn - The target incoming bandwidth, measured in bytes per second.
		*/
		void SetIncomingBandwidth(uint32_t bwIn) { inBW.SetLimit(bwIn); }
		
		/*
			ZNetHost::SetOutgoingBandwidth()
			
			Sets the desired outgoing bandwidth cap.
			
			@param bwOut - The target outgoing bandwidth, measured in bytes per second.
		*/
		void SetOutgoingBandwidth(uint32_t bwOut) { outBW.SetLimit(bwOut); }
		
		/*
			ZNetHost::SetDropChance()
			
			** DEBUG ONLY **
			
			Sets the chance that a packet will be intentionally dropped. This is used to
			simulate high packet loss networks; it should not be used in production. As
			such, the percent chance defaults to 0. Values over 100 are treated as 100%.
			
			@param _dropChance - The percent chance to drop. The value should be 0-99.
		*/
		void SetDropChance(uint32_t _dropChance) { dropChance = _dropChance; }
		
		/*
			ZNetHost::SetMTU()
			
			Sets the MTU used by ZNet. ZNet will not send raw packets larger than this;
			they will be fragmented into multiple calls into libsst-net. This typically less
			the MTU on the adapter, since the "path MTU" is minimum of all nodes between the
			two endpoints. The minimum MTU is clamped to 256 bytes.
		*/
		void SetMTU(uint32_t _mtu) { mtu = _mtu; if(mtu<256) mtu = 256; }
		
		/*
			ZNetHost::GetIncomingBandwidth()
			
			Gets the incoming bandwidth, in bytes per second.
			
			@return (uint32_t) - The incoming bandwidth
		*/
		uint32_t GetIncomingBandwidth() const { return inBW.GetLimit(); }
		
		/*
			ZNetHost::GetOutgoingBandwidth()
			
			Gets the outgoing bandwidth, in bytes per second.
			
			@return (uint32_t) - The outgoing bandwidth
		*/		
		uint32_t GetOutgoingBandwidth() const { return outBW.GetLimit(); }
		
		/*
			ZNetHost::GetDropChance()
			
			** DEBUG ONLY **
			
			Gets the chance to drop intentionally drop a packet. This should
			be zero unless testing network code.
			
			@return (uint32_t) - The chance to drop a packet.
		*/		
		uint32_t GetDropChance() const { return dropChance; }

		/*
			ZNetHost::GetMTU()
			
			Gets the ZNet maximum transmission unit.
		*/
		uint32_t GetMTU() const { return mtu; }
		
	
	protected: //These are used by ZNetServer/ZNetClient
	
		void Reset(uint64_t time);

		void AddEvent(const ZNetEvent* newEvent) { events.PushBack(*newEvent); }

		//Send all channel data to peer
		bool SendToPeer(ZNetPeer* peer);
		
		void TrySendPing(bool isReply, uint32_t givenToken, ZNetPeer* peer);
		void TrySendConnResp(uint32_t flags, uint32_t code);

		ZNetBandwidthMeter inBW;
		ZNetBandwidthMeter outBW;
		
	private:
		ZList<ZNetEvent> events;
		uint32_t mtu;			//< Maximum (wire) size packet
		uint32_t dropChance;	//< Chance to drop a packet

};

#endif

