/*
	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_GAME_SESSION_H
#define GAME_GAME_SESSION_H

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

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

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

namespace Game
{	
	//=================================================================================================================
	//=====================================================================================================================
	
	class GamePeer;
	class GamePacketCenter;
	class GamePacket;
	class GameObjectBase;

	//=================================================================================================================
	/// @brief		This class handles the network session for the game. It sets up a connection for this computer as
	///				either a client or a server.
	//=================================================================================================================

	class GameSession : public ObjectBase
	{
		public:
		
			//=========================================================================================================
			// Friends
			//=========================================================================================================
			
				// This helper class handles the main work for the client and directs client traffic. Needs private access.
				
				friend class GameSessionDirector_Client;
				
				// This helper class handles the main work for the server and directs server traffic. Needs private access.
				
				friend class GameSessionDirector_Server;
		
			//=========================================================================================================
			// Constants
			//=========================================================================================================
			
				/// Default port that the server listens on
				
				static const unsigned short DEFAULT_SERVER_PORT = 33323;
				
				/// This is the maximum number of participants for a game:
				
				static const unsigned MAX_CLIENTS = 8;
		
			//=========================================================================================================
			// Enums
			//=========================================================================================================		
		
				/// An enumerating representing the session type
				
				enum SessionType
				{
					/// We are a server
					
					SESSION_SERVER , 
					
					/// We are a client
					
					SESSION_CLIENT
				};
				
				/// An enumeration representing the current state of the session
				
				enum SessionState
				{						
					/// Client: awaiting connection to the server
					
					STATE_CONNECTING ,
					
					/// Client/Server: Playing the game normally.
					
					STATE_PLAY ,			
					
					/// Client/Server: disconnected from the game.
					
					STATE_DISCONNECTED
				};			
				
			//=========================================================================================================
			// Functions
			//=========================================================================================================
			
				/// Creates the session object that is initially disconnected
				
				GameSession();
				
				/// Destroys the session object and disconnects if not already done so
				
				~GameSession();
				
				/// Tells the object to announce it's type name: this is the type name without the Game:: namespace

				virtual const char * GetTypeName() const;
				
				//=====================================================================================================
				/// @brief		Returns a singleton reference to this object.			
				/// @returns	The single instance of this object.
				/// @pre		This object must still exist
				//=====================================================================================================
				
				static GameSession & GetSingleton()
				{ 
					// Make sure ok:
					
					ENSURE( s_this != 0 , "This object must still exist" )
					
					// Return us:
					
					return * s_this; 
				}
			
				/// Starts a new session. This will automatically tear down any existing session.
				
				void Connect( SessionType type , const NetworkAddress & host = NetworkAddress::InvalidAddress() );
			
				/// Disconnects from this session: this will destroy all objects in the game.
				
				void Disconnect();
				
				/// Updates the game session: does network sends and receives
				
				void Update();
				
				//=====================================================================================================
				/// @brief		Returns the type of session this is
				/// @pre		We must be still connected. This has no meaning when we are disconnected.
				//=====================================================================================================
				
				inline SessionType GetType() const 
				{ 
					// Make sure ok:
					
					ENSURE( IsConnected() , "We must be still connected. This has no meaning when we are disconnected." )				
				
					// return if true
				
					return m_session_type; 
				}
				
				/// Retrieves the current state of the session
				
				inline SessionState GetState() const { return m_session_state; }
				
				/// Tells if this machine is connected. This only returns false for the STATE_DISCONNECTED state
				
				inline bool IsConnected() const { return m_session_state != STATE_DISCONNECTED; }
				
				//=====================================================================================================
				/// @brief		Tells if we are a client
				/// @pre		We must be still connected. This has no meaning when we are disconnected.
				//=====================================================================================================
				
				inline bool IsClient() const 
				{ 
					// Make sure ok:
					
					ENSURE( IsConnected() , "We must be still connected. This has no meaning when we are disconnected." )
				
					// return if true
				
					return m_session_type == SESSION_CLIENT; 
				}
				
				//=====================================================================================================
				/// @brief		Tells if we are a server
				/// @pre		We must be still connected. This has no meaning when we are disconnected.
				//=====================================================================================================
				
				inline bool IsServer() const 
				{ 
					// Make sure ok:
					
					ENSURE( IsConnected() , "We must be still connected. This has no meaning when we are disconnected." )
				
					// return if true								
				
					return m_session_type == SESSION_SERVER; 
				}
				
				//=====================================================================================================
				/// @brief		Returns the peer id of us within the game's network. A peer ID of 0 means we are a server.
				///				An ID of 0xffffffff means we are a client connecting to the server, and we have not got
				///				a valid id from the server yet.
				/// @returns	The peer id of us.
				/// @pre		We must be connected. This function means nothing when we are disconnected.
				//=====================================================================================================
				
				inline unsigned GetPeerID() const 
				{ 
					// Make sure ok:
					
					ENSURE( IsConnected() , "We must be connected. This function means nothing when we are disconnected." )
					
					// Return the peer id:
				
					return m_peer_id; 
				}
				
				//=====================================================================================================
				/// @brief		Returns the settings for this session				
				/// @returns	Configuration settings for the session
				//=====================================================================================================
				
				const GameSession_Settings & GetSettings() const { return m_settings; }	
				
				//=====================================================================================================
				/// @brief		Client only function: returns the object we are controlling
				/// @returns	The object we are currently controlling
				/// @pre		We must be connected
				/// @pre		We must be a client
				//=====================================================================================================
				
				inline GameObjectBase * GetControlObject()
				{ 
					// Make sure ok
					
					ENSURE( IsConnected()	, "We must be connected"	)
					ENSURE( IsClient()		, "We must be a client"		)
					
					// Return it:
					
					return m_control_object;
				}

				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// Notifies all clients that an object has been created and puts the event into a queue to be 
				/// transmitted to all clients later. This is called by GameObjects on the server when they are 
				/// added into the game. N.B there must be no duplicate requests to create!
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

				void ClientCreateNofify( GameObjectBase & object );

				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// Notifies all clients that an object has been delete and puts the event into a queue to be 
				/// transmitted to all clients later. This is called by GameObjects on the server when they are 
				/// removed into the game. N.B there must be no duplicate requests to delete!
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

				void ClientDeleteNofify( GameObjectBase & object );
			
		private:
		
			//=========================================================================================================
			// Functions
			//=========================================================================================================				
				
				/// Checks for connections that need keepalive messages on either the client or the server
				
				void CheckKeepalives();
				
				/// Checks for connections that have timed out on either the client or server
				
				void CheckTimeouts();
				
				/// Server only function: tells if the given address is from one of our connected clients
				
				bool IsClientAddress( const NetworkAddress & addr ) const;
				
				/// Client only function: tells if the given address is the address of our server
				
				bool IsServerAddress( const NetworkAddress & addr ) const;
				
				/// Returns the list of peers in the game:
				
				const std::list<GamePeer*> & GetPeers(){ return m_peers; }
				
				/// Returns the peer with the given id or null on failure

				GamePeer * GetPeer( unsigned id );
				
				/// Server only function: Returns the peer that sent a packet from the given network address
				
				GamePeer * GetSendingClient( const NetworkAddress & addr );
				
				/// Client only function: returns the address of the server
				
				NetworkAddress GetServerAddress() const;
				
				/// Client only function: returns the server peer
				
				GamePeer & GetServerPeer();
				
				/// Client only function: sets the object we are controlling
				
				void SetControlObject( GameObjectBase * object );

				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// Adds a given client to the server. Also makes up pending create object requests so that the client
				/// will receive all of the current game objects in the world at a later stage.
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

				GamePeer * AddClient( const NetworkAddress & client_address );

				/// Removes a given client from the game on the server
				
				void RemoveClient( GamePeer & client );
				
			//=========================================================================================================
			// Variables
			//=========================================================================================================
			
				/// Settings for this session. Include timeout setting as well as information control mode
				
				GameSession_Settings m_settings;
				
				/// What type of session this is
				
				SessionType m_session_type;
				
				/// The current state of the session
				
				SessionState m_session_state;
				
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				/// This is a list of clients we are connected to if we are the server. If we are the client then this
				/// is the address of the server.
				//'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
				
				std::list<GamePeer*> m_peers;
				
				/// This is the id to next GamePeer to add. This is used only on the server. A peer ID of 0 is the server itself.
				
				unsigned m_next_peer_id;
				
				/// This is the peer id of this peer. This is always 0 for the server. It means nothing when we are disconnected.
				
				unsigned m_peer_id; 
				
				/// This is the packet center used for sending and receiving packets. It should be null if we are disconnected.
				
				GamePacketCenter * m_packet_center;
				
				/// This is a singleton pointer to this object
				
				static GameSession * s_this;
				
				/// Server only helper object: handles sending and receiving of data on the server
				
				GameSessionDirector_Server * m_server_director;

				/// Client only helper object: handles sending and receiving of data on the server
				
				GameSessionDirector_Client * m_client_director;
				
				/// Client only: this is the game object we are controlling
				
				GameObjectBase * m_control_object;		
	};
};

#endif
