// Asura_PC_Steamworks.h
// Thomas Young - 13.01.09
// Copyright (C) 2008 Rebellion, All Rights Reserved.

// F I L E   W R A P P E R ///////////////////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef ___ASURA_STEAMWORKS_H___
#define ___ASURA_STEAMWORKS_H___ 1

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_PC_SUPPORT_STEAMWORKS

#include "Core/Asura_List.h"

#include "Steam/Steam_API.h"

// D E F I N E S /////////////////////////////////////////////////////////////

#define ASURA_PC_STEAMWORKS_GAME_PORT					27015
#define ASURA_PC_STEAMWORKS_AUTH_PORT					8766
#define ASURA_PC_STEAMWORKS_UPDATER_PORT				27016

#define ASURA_PC_STEAMWORKS_FILTER_MAX_CHARS			64

#define ASURA_PC_STEAM_RETURN_VOID_IF_UNINITIALISED		if( !Asura_PC_Steamworks::IsInitialised() ) { Asura_Assert( false, "Steamworks not initialised" );	return; }
#define ASURA_PC_STEAM_RETURN_FALSE_IF_UNINITIALISED	if( !Asura_PC_Steamworks::IsInitialised() ) { Asura_Assert( false, "Steamworks not initialised" );	return false; }
#define ASURA_PC_STEAM_RETURN_ZERO_IF_UNINITIALISED		if( !Asura_PC_Steamworks::IsInitialised() ) { Asura_Assert( false, "Steamworks not initialised" );	return 0; }

// E N U M E R A T I O N S ///////////////////////////////////////////////////

enum ASURA_PC_STEAMWORKS_SERVER_TYPE
{
	ASURA_PC_STEAMWORKS_SERVER_LAN,
	ASURA_PC_STEAMWORKS_SERVER_INTERNET
};

enum ASURA_PC_STEAMWORKS_AUTH_LEVEL
{
	ASURA_PC_STEAMWORKS_NO_AUTHENTICATE,
	ASURA_PC_STEAMWORKS_AUTHENTICATE,
	ASURA_PC_STEAMWORKS_AUTHENTICATE_AND_VAC
};

enum ASURA_PC_STEAMWORKS_OVERLAY
{
	ASURA_PC_STEAMWORKS_OVERLAY_FRIENDS,
	ASURA_PC_STEAMWORKS_OVERLAY_COMMUNITY,
	ASURA_PC_STEAMWORKS_OVERLAY_PLAYERS,
	ASURA_PC_STEAMWORKS_OVERLAY_SETTINGS
};


enum ASURA_PC_STEAMWORKS_FILTER_OP
{
	ASURA_PC_STEAMWORKS_FILTER_OP_EQUAL,		// ==
	ASURA_PC_STEAMWORKS_FILTER_OP_LESS_EQUAL,	// <=
	ASURA_PC_STEAMWORKS_FILTER_OP_GREAT_EQUAL	// >=
};

enum ASURA_PC_STEAMWORKS_SEARCH_TYPE
{
	ASURA_PC_STEAMWORKS_SEARCH_TYPE_NONE,
	ASURA_PC_STEAMWORKS_SEARCH_TYPE_SERVER,
	ASURA_PC_STEAMWORKS_SEARCH_TYPE_LOBBY
};

// T Y P E D E F S ///////////////////////////////////////////////////////////

typedef void( *Asura_PC_Steamworks_Callback_SocketStatus ) ( SocketStatusCallback_t* pxCallbackData, void* pUserData );

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_PC_Steamworks_ServerList;
class Asura_MemHeap;

// C L A S S   D E C L A R A T I O N S ///////////////////////////////////////

// ____________________ Asura_PC_Steamworks_ServerFilter _________________________

class Asura_PC_Steamworks_KeyValuePair
{
	// This class defines a key value pair, which is used in a couple of places
	public:

		Asura_PC_Steamworks_KeyValuePair()
		{
			m_szKey   [0] = 0;
			m_szValue [0] = 0;
		}

		Asura_PC_Steamworks_KeyValuePair( const Asura_PC_Steamworks_KeyValuePair& xToCopy )
		{
			Asura_Mem::Copy( m_szKey, xToCopy.m_szKey, ASURA_PC_STEAMWORKS_FILTER_MAX_CHARS );
			Asura_Mem::Copy( m_szValue, xToCopy.m_szValue, ASURA_PC_STEAMWORKS_FILTER_MAX_CHARS );
		}

		int GetValueAsInteger () const
		{
			int iRet;
			if( Asura_StringUtil::GetIntValue( &m_szValue[0], &iRet ) )
			{
				return iRet;
			}
			return 0;
		}

		char m_szKey	[ ASURA_PC_STEAMWORKS_FILTER_MAX_CHARS];
		char m_szValue	[ ASURA_PC_STEAMWORKS_FILTER_MAX_CHARS ];
};

// ____________________ Asura_PC_SteamworksServer_Data _________________________

class Asura_PC_SteamServer_Data
{
	// This class holds data about a server we have found when searching for servers
	public:

		Asura_PC_SteamServer_Data()
		{
			m_uAddress = 0;
			m_usPort = 0;
			m_ucNumPlayers = 0;
			m_ucMaxPlayers = 0;
		}

		u_int			m_uAddress;
		unsigned short	m_usPort;
		unsigned char	m_ucNumPlayers;
		unsigned char	m_ucMaxPlayers;
};

// ____________________ Asura_PC_Steamworks_LobbyFilter _________________________

class Asura_PC_Steamworks_LobbyFilter
{
	// This class defines a filter for filtering lists of lobbies
	public:

		Asura_PC_Steamworks_LobbyFilter()
		{
			m_szKey[0] = 0;
			m_iValue = 0;
			m_eOperator = ASURA_PC_STEAMWORKS_FILTER_OP_EQUAL;
		}

		char							m_szKey		[ ASURA_PC_STEAMWORKS_FILTER_MAX_CHARS ];
		int								m_iValue;
		ASURA_PC_STEAMWORKS_FILTER_OP	m_eOperator;
};

// ____________________ Asura_PC_SteamworksLobby_Data _________________________

class Asura_PC_SteamworksLobby_Data
{
	// This class holds data about a lobby we have found when searching for lobbies
	public:

		Asura_PC_SteamworksLobby_Data()
		{
			m_xSteamID = CSteamID();
			m_bMetaDataReceived = false;
		}

		~Asura_PC_SteamworksLobby_Data()
		{
			while( !m_xMetaData.IsEmpty() )
			{
				delete m_xMetaData.PopHeadObject();
			}
		}

		CSteamID										m_xSteamID;
		bool											m_bMetaDataReceived;
		Asura_List<Asura_PC_Steamworks_KeyValuePair>	m_xMetaData;
};

// _______________________ Asura_PC_Steam_Callback ___________________________

template< class Param > class Asura_PC_Steam_Callback : public CCallbackBase
{
	// This class handles the fabulously awkward callbacks and async call results from the steam API

	public:

		Asura_PC_Steam_Callback ( void( *pfnVoidFunc )( const Param* const pParam ) )
		{
			m_pfnFunction = pfnVoidFunc;

			m_eType = TYPE_NOT_SET;
			m_xAPICall = 0;
		}

		~Asura_PC_Steam_Callback	() {};

		virtual void Run ( void* pxParam )
		{
			if( m_pfnFunction )
			{
				m_pfnFunction( reinterpret_cast<Param*>( pxParam ) );
			}
		}

		virtual void Run( void *pxParam, bool bIOFailure, SteamAPICall_t xSteamAPICall )
		{
			Asura_Assert( xSteamAPICall == m_xAPICall, "" );
			Asura_Assert( !bIOFailure, "Steam IO failure" );

			m_xAPICall = 0;

			if( m_pfnFunction )
			{
				m_pfnFunction( reinterpret_cast<Param*>( pxParam ) );
			}
		}

		virtual void Register( bool bGameServer = false )
		{
			Asura_Assert( m_eType == TYPE_NOT_SET, "Register or Set has already been called" );

			if ( m_nCallbackFlags & k_ECallbackFlagsRegistered )
			{
				Unregister();
			}

			if ( bGameServer )
			{
				m_nCallbackFlags |= k_ECallbackFlagsGameServer;
			}

			SteamAPI_RegisterCallback( this, Param::k_iCallback );

			m_eType = TYPE_CALLBACK;
		}

		virtual void Unregister	()
		{
			Asura_Assert( m_eType == TYPE_CALLBACK, "Should not be unregistering" );

			SteamAPI_UnregisterCallback( this );
		}

		virtual void Set( SteamAPICall_t xAPICall )
		{
			m_iCallback = Param::k_iCallback;

			if ( m_xAPICall )
			{
				SteamAPI_UnregisterCallResult( this, m_xAPICall );
			}

			if ( xAPICall )
			{
				SteamAPI_RegisterCallResult( this, xAPICall );
			}

			m_xAPICall = xAPICall;
			m_eType = TYPE_CALLRESULT;
		}

		virtual void UnSet( )
		{
			Asura_Assert( m_eType == TYPE_CALLRESULT, "Should not be unsetting" );

			if ( m_xAPICall )
			{
				SteamAPI_UnregisterCallResult( this, m_xAPICall );
			}
		}

		virtual int GetCallbackSizeBytes() 
		{ 
			return sizeof( Param );
		}

	private:

		enum STEAM_CALLBACK_TYPE
		{
			TYPE_NOT_SET		= 0,
			TYPE_CALLBACK		= 1,
			TYPE_CALLRESULT		= 2
		};

		void( *m_pfnFunction )( const Param* const pParam );

		SteamAPICall_t m_xAPICall;
		STEAM_CALLBACK_TYPE m_eType;

		Asura_PC_Steam_Callback		() {};
};


// _______________________ Asura_PC_Steamworks ___________________________

class Asura_PC_Steamworks
{
	friend class Asura_PC_Steamworks_ServerList;

	public:

		// ______________________________ Functions _________________________________

		static void		Initialise					( );
		static void 	Update						( );
		static void 	Shutdown					( );

		static bool		IsInitialised				( )		{ return s_bIsInitialised; }
		static bool		IsSteamworksEnabled			( )		{ return s_bIsInitialised; }

		static void		BeginServerSearch			( const ASURA_PC_STEAMWORKS_SERVER_TYPE eServerType
													, const Asura_PC_Steamworks_KeyValuePair* pxFilters = 0
													, const u_int uNumFilters = 0 );
		static void		BeginLobbySearch			( const Asura_PC_Steamworks_LobbyFilter* pxFilters = 0
													, const u_int uNumFilters = 0 
													, const Asura_List<Asura_String>* pxMetaData = 0 );

		static const Asura_PC_SteamServer_Data*		GetServerSearchResult	( const u_int uResult );
		static const Asura_PC_SteamworksLobby_Data* GetLobbySearchResult	( const u_int uResult );
		
		static u_int	GetNumSearchResults			( );
		static bool		IsSearchFinished			( );
		static void		ClearSearch					( );

		static void		RegisterConsoleCmds			( );

		static void		ShowSteamOverlay			( const ASURA_PC_STEAMWORKS_OVERLAY eOverlay );

		static u_int	GetLoopbackAddress			( )		{ return ( ( 1 ) + ( 0 << 8 ) + ( 0 << 16 ) + ( 127 << 24 ) ); }

	private:

		// ______________________________ Functions _________________________________

		Asura_PC_Steamworks		( );
		~Asura_PC_Steamworks	( );

		#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
		static bool ConsoleCmd_EnableSteamworks		( bool bHelp );
		static bool ConsoleCmd_DisableSteamworks	( bool bHelp );
		static bool ConsoleCmd_SteamworksStatus		( bool bHelp );
		static bool ConsoleCmd_StartVoiceRecording	( bool bHelp );
		static bool ConsoleCmd_StopVoiceRecording	( bool bHelp );
		#endif

		static void AudioVoiceBufferCallback		( void* pContext );
		
		static void	Callback_LobbyList				( const LobbyMatchList_t* const pxParam );
		static void Callback_LobbyData				( const LobbyDataUpdate_t* const pxParam );

		// ______________________________ Variables _________________________________

		static bool									s_bSteamworksEnabled;

		static bool									s_bIsInitialised;

		static bool									s_bSearching;
		static float								s_fSearchStartTime;
		static ASURA_PC_STEAMWORKS_SEARCH_TYPE		s_eSearchType;
		static u_int								s_uNumSearchResults;

		static Asura_PC_SteamworksLobby_Data*		s_pxLobbyList;
		static Asura_List<Asura_String>				s_xLobbyMetaData;
				
		static Asura_PC_SteamServer_Data*			s_pxServerList;
		static Asura_PC_Steamworks_ServerList		s_xServerListResponse;
		static ASURA_PC_STEAMWORKS_SERVER_TYPE		s_eSearchServerType;

		static Asura_PC_Steam_Callback<	LobbyMatchList_t >	s_xCallback_LobbyList;
		static Asura_PC_Steam_Callback<	LobbyDataUpdate_t > s_xCallback_LobbyData;
		

};

#endif	// ASURA_PC_SUPPORT_STEAMWORKS

#endif	//___ASURA_STEAMWORKS_H___

//eof
