//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The CS game stats header
//
// $NoKeywords: $
//=============================================================================//

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

#include "cs_blackmarket.h"
#include "gamestats.h"
#include "cs_gamestats_shared.h"
#include "GameEventListener.h"
#include "steamworks_gamestats.h"
#include "weapon_csbase.h"

// forward declares
class CBreakableProp;

#define CS_STATS_BLOB_VERSION 3

const float cDisseminationTimeHigh = 0.25f;		// Time interval for high priority stats sent to the player
const float cDisseminationTimeLow = 2.5f;	// Time interval for medium priority stats sent to the player

int GetCSLevelIndex( const char *pLevelName );

typedef struct
{
	char szGameName[8];
	byte iVersion;
	char szMapName[32];
	char ipAddr[4];
	short port;
	int serverid;
} gamestats_header_t;

typedef struct
{
	gamestats_header_t	header;
	short	iMinutesPlayed;

	short	iTerroristVictories[CS_NUM_LEVELS];
	short	iCounterTVictories[CS_NUM_LEVELS];
	short	iBlackMarketPurchases[WEAPON_MAX];

	short	iAutoBuyPurchases;
	short	iReBuyPurchases;
	short	iAutoBuyM4A1Purchases;
	short	iAutoBuyAK47Purchases;
	short	iAutoBuyFamasPurchases;
	short	iAutoBuyGalilPurchases;
	short	iAutoBuyVestHelmPurchases;
	short	iAutoBuyVestPurchases;

} cs_gamestats_t;

extern short	g_iWeaponPurchases[WEAPON_MAX];
extern float	g_flGameStatsUpdateTime;
extern short	g_iTerroristVictories[CS_NUM_LEVELS];
extern short	g_iCounterTVictories[CS_NUM_LEVELS];
extern short	g_iWeaponPurchases[WEAPON_MAX];

extern short	g_iAutoBuyPurchases;
extern short	g_iReBuyPurchases;
extern short	g_iAutoBuyM4A1Purchases;
extern short	g_iAutoBuyAK47Purchases;
extern short	g_iAutoBuyFamasPurchases;
extern short	g_iAutoBuyGalilPurchases;
extern short	g_iAutoBuyVestHelmPurchases;
extern short	g_iAutoBuyVestPurchases;


struct sHappyCamperSnipePosition
{
	sHappyCamperSnipePosition( int userid, Vector pos ) : m_iUserID(userid), m_vPos(pos) {}

	int		m_iUserID;
	Vector	m_vPos;
};

struct SMarketPurchases : public BaseStatData 
{
	SMarketPurchases( uint64 ulPlayerID, int iPrice, const char *pName ) : m_nPlayerID(ulPlayerID), ItemCost(iPrice)
	{
		if ( pName )
		{
			Q_strncpy( ItemID, pName, ARRAYSIZE(ItemID) );
		}
		else
		{
			Q_strncpy( ItemID, "unknown", ARRAYSIZE(ItemID) );
		}
	}
	uint64	m_nPlayerID;
	int		ItemCost;
	char	ItemID[64];

	BEGIN_STAT_TABLE( "CSSMarketPurchase" )
		REGISTER_STAT( m_nPlayerID )
		REGISTER_STAT( ItemCost )
		REGISTER_STAT_STRING( ItemID )
	END_STAT_TABLE()
};
typedef CUtlVector< SMarketPurchases* > VectorMarketPurchaseData;

struct WeaponStats
{
	int shots;
	int hits;
	int kills;
	int damage;
};

struct SCSSWeaponData : public BaseStatData 
{
	SCSSWeaponData( const char *pWeaponName, const WeaponStats &wpnData )
	{
		if ( pWeaponName )
		{
			Q_strncpy( WeaponID, pWeaponName, ARRAYSIZE(WeaponID) );
		}
		else
		{
			Q_strncpy( WeaponID, "unknown", ARRAYSIZE(WeaponID) );
		}

		Shots = wpnData.shots;
		Hits = wpnData.hits;
		Kills = wpnData.kills;
		Damage = wpnData.damage;
	}
	
	char	WeaponID[64];
	int		Shots;
	int		Hits;
	int		Kills;
	int		Damage;

	BEGIN_STAT_TABLE( "CSSWeaponData" )
		REGISTER_STAT_STRING( WeaponID )
		REGISTER_STAT( Shots )
		REGISTER_STAT( Hits )
		REGISTER_STAT( Kills )
		REGISTER_STAT( Damage )
	END_STAT_TABLE()
};
typedef CUtlVector< SCSSWeaponData* > CSSWeaponData;

struct SCSSDeathData : public BaseStatData
{
	SCSSDeathData( CBasePlayer *pVictim, const CTakeDamageInfo &info )
	{
		m_bUseGlobalData = false;

		m_DeathPos = info.GetDamagePosition();
		m_iVictimTeam = pVictim->GetTeamNumber();
		
		CCSPlayer *pCSPlayer = ToCSPlayer( info.GetAttacker() );
		if ( pCSPlayer )
		{
			m_iKillerTeam = pCSPlayer->GetTeamNumber();
		}
		else
		{
			m_iKillerTeam = -1;
		}

		const char *pszWeaponName = info.GetInflictor() ? info.GetInflictor()->GetClassname() : "unknown";
		
		if ( pszWeaponName )
		{
			if ( V_strcmp(pszWeaponName, "player") == 0 )
			{
				// get the player's weapon
				if ( pCSPlayer && pCSPlayer->GetActiveCSWeapon() )
				{
					pszWeaponName = pCSPlayer->GetActiveCSWeapon()->GetClassname();
				}
			}
		}


		m_uiDeathParam = WEAPON_NONE;
		if ( (m_uiDeathParam = AliasToWeaponID( pszWeaponName )) == WEAPON_NONE )
		{
			m_uiDeathParam = AliasToWeaponID( pszWeaponName );
		}

		const char *pszMapName = gpGlobals->mapname.ToCStr() ? gpGlobals->mapname.ToCStr() : "unknown";
		Q_strncpy( m_szMapName, pszMapName, ARRAYSIZE(m_szMapName) );
	}
	Vector	m_DeathPos;
	int		m_iVictimTeam;
	int		m_iKillerTeam;
	int		m_iDamageType;
	uint64	m_uiDeathParam;
	char	m_szMapName[64];
	
	BEGIN_STAT_TABLE( "Deaths" )
		REGISTER_STAT_NAMED( m_DeathPos.x, "XCoord" )
		REGISTER_STAT_NAMED( m_DeathPos.y, "YCoord" )
		REGISTER_STAT_NAMED( m_DeathPos.z, "ZCoord" )
		REGISTER_STAT_NAMED( m_iVictimTeam, "Team" )
		REGISTER_STAT_NAMED( m_iKillerTeam, "DeathCause" )
		REGISTER_STAT_NAMED( m_uiDeathParam, "DeathParam" )
		REGISTER_STAT_NAMED( m_szMapName, "DeathMap" )
	END_STAT_TABLE()
};
typedef CUtlVector< SCSSDeathData* > CSSDeathData;

//=============================================================================
//
// CS Game Stats Class
//
class CCSGameStats : public CBaseGameStats, public CGameEventListener, public CAutoGameSystemPerFrame, public IGameStatTracker
{
public:

	// Constructor/Destructor.
	CCSGameStats( void );
	~CCSGameStats( void );

	virtual void Clear( void );
	virtual bool Init();
	virtual void PreClientUpdate();
	virtual void PostInit();
	virtual void LevelShutdownPreClearSteamAPIContext();

	void UploadRoundStats( void );
	// Overridden events
	virtual void Event_LevelInit( void );
	virtual void Event_LevelShutdown( float flElapsed );
	virtual void Event_ShotFired( CBasePlayer *pPlayer, CBaseCombatWeapon* pWeapon );
	virtual void Event_ShotHit( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
	virtual void Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
	virtual void Event_PlayerKilled_PreWeaponDrop( CBasePlayer *pPlayer, const CTakeDamageInfo &info );
	void UpdatePlayerRoundStats(int winner);
	virtual void Event_PlayerConnected( CBasePlayer *pPlayer );
	virtual void Event_PlayerDisconnected( CBasePlayer *pPlayer );
	virtual void Event_WindowShattered( CBasePlayer *pPlayer );
	virtual void Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info );
			

	// CSS specific events
    void Event_BombPlanted( CCSPlayer *pPlayer );
    void Event_BombDefused( CCSPlayer *pPlayer );
	void Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info );
	void Event_BombExploded( CCSPlayer *pPlayer );
	void Event_MoneyEarned( CCSPlayer *pPlayer, int moneyEarned );
	void Event_MoneySpent( CCSPlayer *pPlayer, int moneySpent, const char *pItemName );
    void Event_HostageRescued( CCSPlayer *pPlayer );
    void Event_PlayerSprayedDecal( CCSPlayer*pPlayer );
	void Event_AllHostagesRescued();
	void Event_BreakProp( CCSPlayer *pPlayer, CBreakableProp *pProp );
    void Event_PlayerDonatedWeapon (CCSPlayer* pPlayer);
    void Event_PlayerDominatedOther( CCSPlayer* pAttacker, CCSPlayer* pVictim);
    void Event_PlayerRevenge( CCSPlayer* pAttacker );
    void Event_PlayerAvengedTeammate( CCSPlayer* pAttacker, CCSPlayer* pAvengedPlayer );
	void Event_MVPEarned( CCSPlayer* pPlayer );	
	void Event_KnifeUse( CCSPlayer* pPlayer, bool bStab, int iDamage );

	virtual void FireGameEvent( IGameEvent *event );

	void DumpMatchWeaponMetrics();

	const PlayerStats_t&		FindPlayerStats( CBasePlayer *pPlayer ) const;
	void						ResetPlayerStats( CBasePlayer *pPlayer );
	void						ResetKillHistory( CBasePlayer *pPlayer );
	void						ResetRoundStats();
	void						ResetPlayerClassMatchStats();

	const StatsCollection_t&	GetTeamStats( int iTeamIndex ) const;
	void						ResetAllTeamStats();
    void						ResetAllStats();
	void						ResetWeaponStats();
 	void						IncrementTeamStat( int iTeamIndex, int iStatIndex, int iAmount );
	void                        CalcDominationAndRevenge( CCSPlayer *pAttacker, CCSPlayer *pVictim, int *piDeathFlags );
    void                        CalculateOverkill( CCSPlayer* pAttacker, CCSPlayer* pVictim );
	void						PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info );

	void						IncrementStat( CCSPlayer* pPlayer, CSStatType_t statId, int iValue, bool bPlayerOnly = false );
	// Steamworks Gamestats 
	virtual void SubmitGameStats( KeyValues *pKV );

	virtual StatContainerList_t* GetStatContainerList( void )
	{
		return s_StatLists;
	}

protected:	
	void						SetStat( CCSPlayer *pPlayer, CSStatType_t statId, int iValue );
    void						TrackKillStats( CCSPlayer *pAttacker, CCSPlayer *pVictim );
    void                        ComputeRollingStatAverages();
	void                        ComputeDirectStatAverages();
    void                        SendRollingStatsAveragesToAllPlayers();
	void                        SendDirectStatsAveragesToAllPlayers();
	void						SendStatsToPlayer( CCSPlayer * pPlayer, int iMinStatPriority );

private:
	PlayerStats_t				m_aPlayerStats[MAX_PLAYERS+1];	// List of stats for each player for current life - reset after each death    
	StatsCollection_t			m_aTeamStats[TEAM_MAXCOUNT - FIRST_GAME_TEAM];

    RoundStatsRollingAverage_t  m_rollingCTStatAverages;	
    RoundStatsRollingAverage_t  m_rollingTStatAverages;	
    RoundStatsRollingAverage_t	m_rollingPlayerStatAverages;	

	RoundStatsDirectAverage_t	m_directCTStatAverages;
	RoundStatsDirectAverage_t	m_directTStatAverages;
	RoundStatsDirectAverage_t	m_directPlayerStatAverages;

	float						m_fDisseminationTimerLow;		// how long since last medium priority stat update
	float						m_fDisseminationTimerHigh;		// how long since last high priority stat update

	int							m_numberOfRoundsForDirectAverages;
	int							m_numberOfTerroristEntriesForDirectAverages;
	int							m_numberOfCounterTerroristEntriesForDirectAverages;

	CUtlDict< CSStatType_t, short >	m_PropStatTable;

	CUtlLinkedList<sHappyCamperSnipePosition, int>		m_PlayerSnipedPosition;
	WeaponStats					m_weaponStats[WEAPON_MAX][2];
	
	// Steamworks Gamestats
	VectorMarketPurchaseData	m_MarketPurchases;
	CSSWeaponData				m_WeaponData;
	CSSDeathData				m_DeathData;

	// A static list of all the stat containers, one for each data structure being tracked
	static StatContainerList_t * s_StatLists;

	bool						m_bInRound;
};

extern CCSGameStats CCS_GameStats;

#endif // CS_GAMESTATS_H