/*
	Lan Wars.
	Copyright (c) 2009, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef GAME_PACKET_CENTER_H
#define GAME_PACKET_CENTER_H

//=====================================================================================================================
//=====================================================================================================================

#include "Game\GamePacket.h"
#include "Network\NetworkAddress.h"

//=====================================================================================================================
//=====================================================================================================================

namespace Game
{	
	//=================================================================================================================
	//=================================================================================================================
	
	class DatagramSocket;

	//=================================================================================================================
	/// @brief		This class serves as post-office style object whereby packets are dropped in for later sending,
	///				and where packets are put by for later receiving. This class automatically sends and receives 
	///				in it's own threads when the network is ready. All of the game's data passes through this class.
	/// @todo		Implement retransmission queue
	//=================================================================================================================

	class GamePacketCenter
	{
		public:
			
			//=========================================================================================================
			// Constants
			//=========================================================================================================
				
				/// Maximum number of incoming packets that can be buffered: older packets are dropped 
				
				static const unsigned MAX_INCOMING_PACKETS = 128;

				/// Maximum number of outgoing packets that can be buffered: older packets are dropped 
				
				static const unsigned MAX_OUTGOING_PACKETS = 128;
				
				#ifdef USE_DEBUG_CONSOLE
				
					/// Debug only: the length of the interval over which we monitor packet stats
					
					static const unsigned STAT_INTERVAL_MS = 1000;
					
				#endif
								
			//=========================================================================================================
			// Functions
			//=========================================================================================================

				/// Creates the packet center.
				
				GamePacketCenter( unsigned short port = 0 , unsigned timeout_ms	= 2500 );
				
				/// Destroys the packet center.
				
				~GamePacketCenter();
				
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// Resets the time since we last received a packet: this is used on the server side whilst we are 
				/// waiting for a connection, since there should be no timeouts when waiting for a connection.
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				
				inline void ResetTimeout(){ m_last_incoming_time = GetTickCount(); }
				
				/// Tells if a timeout has occured. This will also return true if the datagram socket is invalid.
				
				bool HasTimedOut();
				
				/// Gets the current number of outgoing packets
				
				unsigned GetOutgoingCount();
				
				/// Gets the current number of incoming packets
				
				unsigned GetIncomingCount();
				
				/// Writes one packet to the network
				
				void WritePacket( const GamePacket & packet );
				
				/// Reads one packet from the network
				
				void ReadPacket( GamePacket & packet );
				
				/// Gets the socket used by the packet center:
				
				DatagramSocket * GetSocket(){ return m_socket; }
				
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// Returns the timeout value in milliseconds. If a packet is not received within this period then the 
				/// connection is presumed to have been lost. This only applies to clients. Servers ignore timeouts
				/// and are always considered to be connected- even if they are completly isolated.
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				
				inline unsigned GetTimeout(){ return m_timeout_ms; }
				
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// Gets the time since the last packet was sent in milliseconds: we must send packets every so often 
				/// (even if we have no data) just to keep the connection alive. This applies to both server to client
				///	connections as well as client to server connections.
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				
				unsigned GetTimeSinceLastSend(){ return GetTickCount() - m_last_outgoing_time; }
				
		private:
		
			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				/// Thread entry function for the incoming packets thread
				
				static void IncomingPacketThread( void * packet_center );

				/// Thread entry function for the outgoing packets thread
				
				static void OutgoingPacketThread( void * packet_center );
				
				/// Debug only variables
				
				#ifdef USE_DEBUG_CONSOLE
				
					/// Console command. Sets the amount of constant jitter to add to packets.
					
					static int ConCmd_N_FakeJitter( int arg , bool arg_given );
					
					/// Console command. Sets the amount of constant lag to add to packets.
					
					static int ConCmd_N_FakeLag( int arg , bool arg_given );

					/// Console command. Enables/disables showing of network stats
					
					static bool ConCmd_N_Stats( bool arg , bool arg_given );
				
				#endif

			//=========================================================================================================
			// Variables
			//=========================================================================================================
			
				/// A pool for incoming packets
				
				Pool<GamePacket> * m_incoming_packet_pool;
				
				/// A pool for outgoing packets
				
				Pool<GamePacket> * m_outgoing_packet_pool;
			
				/// Our incoming packets buffer: these are used in the incoming packets pool
				
				GamePacket m_incoming_packet_buffer[MAX_INCOMING_PACKETS];
				
				/// Our outgoing packets buffer: these are used in the outgoing packets pool
				
				GamePacket m_outgoing_packet_buffer[MAX_INCOMING_PACKETS];
				
				/// Thread for receiving incomming packets
				
				Thread * m_incoming_thread;
				
				/// Thread for sending outgoing packets
				
				Thread * m_outgoing_thread;
				
				/// This sync condition is set to true when there are more packets to send
				
				Condition m_outgoing_packets_condition;
				
				/// A mutex for controlling access to the incoming packet pool
				
				Mutex m_incoming_lock;
				
				/// A mutex for controlling access to the outgoing packet pool
				
				Mutex m_outgoing_lock;
				
				/// Datagram socket used for sending and receiving
				
				DatagramSocket * m_socket;
				
				/// Number of milliseconds that are allowed without receiving data before the connection is considered to be timed out
				
				unsigned m_timeout_ms;
				
				/// MS timestamp for when a packet was last received
				
				unsigned m_last_incoming_time;
				
				/// MS timestamp for when a packet was last sent
				
				unsigned m_last_outgoing_time;
				
				/// If this is true then both the send and receive threads should stop sending and receiving packets
				
				bool m_exit_io_threads;
				
				/// This is the number we will assign to the next outgoing packet
				
				unsigned m_next_outgoing_packet_num;
				
				/// Debug only variables
				
				#ifdef USE_DEBUG_CONSOLE
				
					/// A constant amount of lag to add to all packets
					
					static int s_fake_lag_ms;
					
					/// A constant amount of random jitter to add to all packets.
					
					static int s_fake_jitter_ms;
					
					/// Debug flag that enables monitoring of network stats
					
					static bool s_show_stats;
					
					/// For data monitoring: the number of packets that have come in within the monitoring interval
					
					static int s_stats_incoming_packets;

					/// For data monitoring: the number of packets that have gone out within the monitoring interval
					
					static int s_stats_outgoing_packets;
					
					/// For data monitoring: the number of bytes that have come in within the monitoring interval
					
					static int s_stats_incoming_bytes;

					/// For data monitoring: the number of bytes that have gone out within the monitoring interval
					
					static int s_stats_outgoing_bytes;
					
					/// For data monitoring: the time at which the current monitoring interval began
					
					static int s_stats_interval_begin;
				
				#endif
	};
};

#endif
