//====== Copyright © 1996-2006, Valve Corporation, All rights reserved. =======//
//
// Purpose: Uploads gamestats via the SteamWorks API.  Server version.
//
//=============================================================================//

#ifndef STEAMWORKS_GAMESTATS_SERVER_H
#define STEAMWORKS_GAMESTATS_SERVER_H
#ifdef _WIN32
#pragma once
#endif

#include "steamworks_gamestats.h"

//used to drive most of the game stat event handlers as well as track basic stats under the hood of CBaseGameStats
class CSteamWorksGameStatsServer : public CSteamWorksGameStatsUploader
{
	DECLARE_CLASS( CSteamWorksGameStatsServer, CSteamWorksGameStatsUploader )
public:
	CSteamWorksGameStatsServer();

	void WriteSessionRow();

protected:
	virtual EGameStatsAccountType GetGameStatsAccountType();

	// called before a row is committed, allows derived classes to add sessionIDs, etc.
	virtual void AddSessionIDsToTable( int iTableID );
};

CSteamWorksGameStatsServer& GetSteamWorksGameStatsServer();


// Macros to ease the creation of SendData method for stats structs/classes
#define BEGIN_STAT_TABLE( tableName ) \
	static const char* GetStatTableName( void ) { return tableName; } \
	void BuildGamestatDataTable( KeyValues* pKV ) \
{ \
	pKV->SetName( GetStatTableName() ); 

#define REGISTER_STAT( varName ) \
	AddDataToKV(pKV, #varName, varName);

#define REGISTER_STAT_NAMED( varName, dbName ) \
	AddDataToKV(pKV, dbName, varName);

#define REGISTER_STAT_POSITION( varName ) \
	AddPositionDataToKV(pKV, #varName, varName);

#define REGISTER_STAT_POSITION_NAMED( varName, dbName ) \
	AddPositionDataToKV(pKV, dbName, varName);

#define REGISTER_STAT_ARRAY( varName ) \
	AddArrayDataToKV( pKV, #varName, varName, ARRAYSIZE( varName ) );

#define REGISTER_STAT_ARRAY_NAMED( varName, dbName ) \
	AddArrayDataToKV( pKV, dbName, varName, ARRAYSIZE( varName ) );

#define REGISTER_STAT_STRING( varName ) \
	AddStringDataToKV( pKV, #varName, varName );

#define REGISTER_STAT_STRING_NAMED( varName, dbName ) \
	AddStringDataToKV( pKV, dbName, varName );

#define AUTO_STAT_TABLE_KEY() \
	pKV->SetInt( "TimeSubmitted", GetUniqueIDForStatTable( *this ) );

#define END_STAT_TABLE() \
	pKV->SetUint64( ::BaseStatData::m_bUseGlobalData ? "TimeSubmitted" : "SessionTime", ::BaseStatData::TimeSubmitted ); \
	GetSteamWorksGameStatsServer().AddStatsForUpload( pKV ); \
}

//-----------------------------------------------------------------------------
// Purpose: Templatized class for getting unique ID's for stat tables that need
//			to be submitted multiple times per-session.
//-----------------------------------------------------------------------------
template < typename T >
class UniqueStatID_t
{
public:
	static unsigned GetNext( void )
	{
		return ++s_nLastID;
	}

	static void Reset( void )
	{
		s_nLastID = 0;
	}

private:
	static unsigned s_nLastID;
};

template < typename T >
unsigned UniqueStatID_t< T >::s_nLastID = 0;

template < typename T >
unsigned GetUniqueIDForStatTable( const T &table )
{
	return UniqueStatID_t< T >::GetNext();
}

//=============================================================================
//
// An interface for tracking gamestats.
//
class IGameStatTracker
{
public:

	//-----------------------------------------------------------------------------
	// Templatized methods to track a per-mission stat.
	// The stat is copied, then deleted after it's sent to the SQL server.
	//-----------------------------------------------------------------------------
	template < typename T >
	void SubmitStat( T& stat )
	{
		// Make a copy of the stat. All of the stat lists require pointers,
		// so we need to protect against a stat allocated on the stack
		T* pT = new T();
		if( !pT )
			return;

		*pT = stat;
		SubmitStat( pT );
	}

	//-----------------------------------------------------------------------------
	// Templatized methods to track a per-mission stat (by pointer)
	// The stat is deleted after it's sent to the SQL server
	//-----------------------------------------------------------------------------
	template < typename T >
	void SubmitStat( T* pStat )
	{
		// Get the static stat table for this type and add the stat to it
		GetStatTable<T>()->AddToTail( pStat );
	}

	//-----------------------------------------------------------------------------
	// Add all stats to an existing key value file for submit.
	//-----------------------------------------------------------------------------
	virtual void SubmitGameStats( KeyValues *pKV ) = 0;

	//-----------------------------------------------------------------------------
	// Prints the memory usage of all of the stats being tracked
	//-----------------------------------------------------------------------------
	void PrintGamestatMemoryUsage( void );

protected:
	//=============================================================================
	//
	// Used as a base interface to store a list of all templatized stat containers
	//
	class IStatContainer
	{
	public:
		virtual void SendData( KeyValues *pKV ) = 0;
		virtual void Clear( void ) = 0;
		virtual void PrintMemoryUsage( void ) = 0;
	};

	// Defines a list of stat containers.
	typedef CUtlVector< IStatContainer* > StatContainerList_t;

	//-----------------------------------------------------------------------------
	// Used to get a list of all stats containers being tracked by the deriving class
	//-----------------------------------------------------------------------------
	virtual StatContainerList_t* GetStatContainerList( void ) = 0;

private:

	//=============================================================================
	//
	// Templatized list of stats submitted
	//
	template < typename T >
	class CGameStatList : public IStatContainer, public CUtlVector< T* >
	{
	public:
		//-----------------------------------------------------------------------------
		// Get data ready to send to the SQL server
		//-----------------------------------------------------------------------------
		virtual void SendData( KeyValues *pKV )
		{
			//ASSERT( pKV != NULL );

			// Duplicate the master KeyValue for each stat instance
			for( int i=0; i < this->m_Size; ++i )
			{
				// Make a copy of the master key value and build the stat table
				KeyValues *pKVCopy = this->operator [](i)->m_bUseGlobalData ? pKV->MakeCopy() : new KeyValues( "" );
				this->operator [](i)->BuildGamestatDataTable( pKVCopy );
			}

			// Reset unique ID counter for the stat type
			UniqueStatID_t< T >::Reset();
		}

		//-----------------------------------------------------------------------------
		// Clear and delete every stat in this list
		//-----------------------------------------------------------------------------
		virtual void Clear( void )
		{
			this->Purge();
		}

		//-----------------------------------------------------------------------------
		// Print out details about this lists memory usage
		//-----------------------------------------------------------------------------
		virtual void PrintMemoryUsage( void )
		{
			if( this->m_Size == 0 )
				return;

			// Compute the memory used as the size of type times the list count
			unsigned uMemoryUsed = this->m_Size * ( sizeof( T ) );

			Msg( "	%d\tbytes used by %s table\n", uMemoryUsed, T::GetStatTableName() );
		}
	};

	//-----------------------------------------------------------------------------
	// Templatized method to get a single instance of a stat list per data type.
	//-----------------------------------------------------------------------------
	template < typename T >
	CGameStatList< T >* GetStatTable( void )
	{
		static CGameStatList< T > *s_vecOfType = 0;
		if( s_vecOfType == 0 )
		{
			s_vecOfType = new CGameStatList< T >();
			GetStatContainerList()->AddToTail( s_vecOfType );
		}
		return s_vecOfType;
	}

};

struct BaseStatData
{
	BaseStatData( bool bUseGlobalData = true ) : m_bUseGlobalData( bUseGlobalData )
	{
		TimeSubmitted = GetSteamWorksGameStatsServer().GetTimeSinceEpoch();
	}

	bool	m_bUseGlobalData;
	uint64	TimeSubmitted;

};

#endif // STEAMWORKS_GAMESTATS_SERVER_H