//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================
#ifndef TF_GAMESTATS_SHARED_H
#define TF_GAMESTATS_SHARED_H
#ifdef _WIN32
#pragma once
#endif
#include "cbase.h"
#include "tier1/utlvector.h"
#include "tier1/utldict.h"
#include "shareddefs.h"
#include "tf_shareddefs.h"

//=============================================================================
//
// TF Game Stats Enums
//
// NOTE: You may add to the end, but do not insert to this list!
//
enum TFStatType_t
{
	TFSTAT_UNDEFINED = 0,
	TFSTAT_SHOTS_HIT,
	TFSTAT_SHOTS_FIRED,
	TFSTAT_KILLS,
	TFSTAT_DEATHS,
	TFSTAT_DAMAGE,
	TFSTAT_CAPTURES,
	TFSTAT_DEFENSES,
	TFSTAT_DOMINATIONS,
	TFSTAT_REVENGE,
	TFSTAT_POINTSSCORED,
	TFSTAT_BUILDINGSDESTROYED,
	TFSTAT_HEADSHOTS,
	TFSTAT_PLAYTIME,
	TFSTAT_HEALING,
	TFSTAT_INVULNS,
	TFSTAT_KILLASSISTS,
	TFSTAT_BACKSTABS,
	TFSTAT_HEALTHLEACHED,
	TFSTAT_BUILDINGSBUILT,
	TFSTAT_MAXSENTRYKILLS,
	TFSTAT_TELEPORTS,
	TFSTAT_FIREDAMAGE,
	TFSTAT_BONUS_POINTS,
	TFSTAT_BLASTDAMAGE,
	TFSTAT_DAMAGETAKEN,
	TFSTAT_HEALTHKITS,
	TFSTAT_AMMOKITS,
	TFSTAT_CLASSCHANGES,
	TFSTAT_CRITS,
	TFSTAT_SUICIDES,
	TFSTAT_CURRENCY_COLLECTED,
	TFSTAT_DAMAGE_ASSIST,
	TFSTAT_HEALING_ASSIST,
	TFSTAT_DAMAGE_BOSS,
	TFSTAT_DAMAGE_BLOCKED,
	TFSTAT_DAMAGE_RANGED,
	TFSTAT_DAMAGE_RANGED_CRIT_RANDOM,
	TFSTAT_DAMAGE_RANGED_CRIT_BOOSTED,
	TFSTAT_REVIVED,
	TFSTAT_THROWABLEHIT,
	TFSTAT_THROWABLEKILL,
	TFSTAT_KILLSTREAK_MAX,
	TFSTAT_KILLS_RUNECARRIER,
	TFSTAT_FLAGRETURNS,
	TFSTAT_TOTAL
};

#define TFSTAT_FIRST (TFSTAT_UNDEFINED+1)
#define TFSTAT_LAST (TFSTAT_TOTAL-1)

extern const char *s_pStatStrings[ TFSTAT_TOTAL ];

enum TFMapStatType_t
{
	TFMAPSTAT_UNDEFINED = 0,
	TFMAPSTAT_PLAYTIME,
	TFMAPSTAT_TOTAL
};

#define TFMAPSTAT_FIRST (TFMAPSTAT_UNDEFINED+1)
#define TFMAPSTAT_LAST (TFMAPSTAT_TOTAL-1)

extern const char *s_pMapStatStrings[ TFMAPSTAT_TOTAL ];

enum TFRoundEndReason_t
{
	RE_ROUND_END,
	RE_CLIENT_DISCONNECT,
	RE_CLIENT_QUIT,
	RE_SERVER_MAP_CHANGE,
	RE_SERVER_SHUTDOWN,
	RE_TIME_LIMIT,
	RE_WIN_LIMIT,
	RE_WIN_DIFF_LIMIT,
	RE_ROUND_LIMIT,
	RE_NEXT_LEVEL_CVAR,
	MAX_ROUND_END_REASON
};

extern const char *g_aRoundEndReasons[MAX_ROUND_END_REASON];

//=============================================================================
//
// TF Player Round Stats
//
struct RoundStats_t
{
	int m_iStat[TFSTAT_TOTAL];

	RoundStats_t() { Reset(); };

	inline int Get( int i ) const
	{
		AssertMsg( i >= TFSTAT_UNDEFINED && i < TFSTAT_TOTAL, "Stat index out of range!" );
		return m_iStat[ i ];
	}

	inline void Set( int i, int nValue )
	{
		AssertMsg( i >= TFSTAT_UNDEFINED && i < TFSTAT_TOTAL, "Stat index out of range!" );
		m_iStat[ i ] = nValue;
	}

	void Reset()
	{
		for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
		{
			m_iStat[i] = 0;
		}
	};

	void AccumulateRound( const RoundStats_t &other )
	{
		for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
		{
			m_iStat[i] += other.m_iStat[i];
		}
	};
};

struct RoundMapStats_t
{
	int m_iStat[ TFMAPSTAT_TOTAL ];

	RoundMapStats_t() { Reset(); };

	inline int Get( int i ) const
	{
		AssertMsg( i >= TFMAPSTAT_UNDEFINED && i < TFMAPSTAT_TOTAL, "Map stat index out of range!" );
		return m_iStat[ i ];
	}

	inline void Set( int i, int nValue )
	{
		AssertMsg( i >= TFMAPSTAT_UNDEFINED && i < TFMAPSTAT_TOTAL, "Map stat index out of range!" );
		m_iStat[ i ] = nValue;
	}

	void Reset()
	{
		for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
		{
			m_iStat[i] = 0;
		}
	};

	void AccumulateRound( const RoundMapStats_t &other )
	{
		for ( int i = 0; i < ARRAYSIZE( m_iStat ); i++ )
		{
			m_iStat[i] += other.m_iStat[i];
		}
	};
};

enum TFGameStatsVersions_t
{
	TF_GAMESTATS_FILE_VERSION = 006,
	TF_GAMESTATS_MAGIC = 0xDEADBEEF
};

enum TFGameStatsLumpIds_t
{
	TFSTATS_LUMP_VERSION = 1,
	TFSTATS_LUMP_MAPHEADER,
	TFSTATS_LUMP_MAPDEATH,
	TFSTATS_LUMP_MAPDAMAGE,
	TFSTATS_LUMP_CLASS,	
	TFSTATS_LUMP_WEAPON,
	TFSTATS_LUMP_ENDTAG,
	MAX_LUMP_COUNT
};

struct TF_Gamestats_Version_t
{
	int m_iMagic;			// always TF_GAMESTATS_MAGIC
	int m_iVersion;
};

struct TF_Gamestats_ClassStats_t
{
	static const unsigned short LumpId = TFSTATS_LUMP_CLASS;	// Lump ids.
	int iSpawns;												// total # of spawns of this class
	int iTotalTime;												// aggregate player time in seconds in this class
	int iScore;													// total # of points scored by this class
	int iKills;													// total # of kills by this class
	int iDeaths;												// total # of deaths by this class
	int iAssists;												// total # of assists by this class
	int iCaptures;												// total # of captures by this class
	int iClassChanges;											// total # of times someone changed to this class

	void Accumulate( TF_Gamestats_ClassStats_t &other )
	{
		iSpawns += other.iSpawns;
		iTotalTime += other.iTotalTime;
		iScore += other.iScore;
		iKills += other.iKills;
		iDeaths += other.iDeaths;
		iAssists += other.iAssists;
		iCaptures += other.iCaptures;
		iClassChanges += other.iClassChanges;
	}
};

struct TF_Gamestats_WeaponStats_t
{
	static const unsigned short LumpId = TFSTATS_LUMP_WEAPON;	// Lump ids.
	int iShotsFired;
	int iCritShotsFired;
	int	iHits;
	int iTotalDamage;
	int iHitsWithKnownDistance;
	int64 iTotalDistance;

	void Accumulate( TF_Gamestats_WeaponStats_t &other )
	{
		iShotsFired += other.iShotsFired;
		iCritShotsFired += other.iCritShotsFired;
		iHits += other.iHits;
		iTotalDamage += other.iTotalDamage;
		iHitsWithKnownDistance += other.iHitsWithKnownDistance;
		iTotalDistance += other.iTotalDistance;
	}
};

//=============================================================================
//
// TF Game Level Stats Data
//
struct TF_Gamestats_LevelStats_t
{
public:

	TF_Gamestats_LevelStats_t();
	~TF_Gamestats_LevelStats_t();
	TF_Gamestats_LevelStats_t( const TF_Gamestats_LevelStats_t &stats );

	// Level start and end
	void Init( const char *pszMapName, int nMapVersion, int nIPAddr, short nPort, float flStartTime );
	void Shutdown( float flEndTime );

	void Accumulate( TF_Gamestats_LevelStats_t *pOther )
	{
		m_Header.Accumulate( pOther->m_Header );
		//m_aPlayerDeaths.AddVectorToTail( pOther->m_aPlayerDeaths );
		//m_aPlayerDamage.AddVectorToTail( pOther->m_aPlayerDamage );
		int i;
		for ( i = 0; i < ARRAYSIZE( m_aClassStats ); i++ )
		{
			m_aClassStats[i].Accumulate( pOther->m_aClassStats[i] );
		}
		for ( i = 0; i < ARRAYSIZE( m_aWeaponStats ); i++ )
		{
			m_aWeaponStats[i].Accumulate( pOther->m_aWeaponStats[i] );
		}

	}
public:

	// Level header data.
	struct LevelHeader_t
	{
		static const unsigned short LumpId = TFSTATS_LUMP_MAPHEADER;	// Lump ids.
		char			m_szMapName[64];							// Name of the map.
		int				m_nMapRevision;								// Version number for the map.
		unsigned int	m_nIPAddr;									// IP Address of the server - 4 bytes stored as an int.
		unsigned short	m_nPort;									// Port the server is using.	
		int				m_iRoundsPlayed;							// # of rounds played
		int				m_iTotalTime;								// total # of seconds of all rounds
		int				m_iBlueWins;								// # of blue team wins
		int				m_iRedWins;									// # of red team wins
		int				m_iStalemates;								// # of stalemates
		int				m_iBlueSuddenDeathWins;						// # of blue team wins during sudden death
		int				m_iRedSuddenDeathWins;						// # of red team wins during sudden death
		int				m_iLastCapChangedInRound[MAX_CONTROL_POINTS+1];		// # of times a round ended on each control point

		void Accumulate( LevelHeader_t &other )
		{
			m_iRoundsPlayed += other.m_iRoundsPlayed;
			m_iTotalTime += other.m_iTotalTime;
			m_iBlueWins += other.m_iBlueWins;
			m_iRedWins += other.m_iRedWins;
			m_iStalemates += other.m_iStalemates;
			m_iBlueSuddenDeathWins += other.m_iBlueSuddenDeathWins;
			m_iRedSuddenDeathWins += other.m_iRedSuddenDeathWins;
			for ( int i = 0; i <= MAX_CONTROL_POINTS; i++ )
			{
				m_iLastCapChangedInRound[i] += other.m_iLastCapChangedInRound[i]; 
			}
		}
	};

	// Player deaths.
	struct PlayerDeathsLump_t
	{
		static const unsigned short LumpId = TFSTATS_LUMP_MAPDEATH;	// Lump ids.
		short			nPosition[3];								// Position of death.
		short			iWeapon;									// Weapon that killed the player.
		unsigned short	iDistance;									// Distance the attacker was from the player.
		byte			iAttackClass;								// Class that killed the player.
		byte			iTargetClass;								// Class of the player killed.
	};

	// Player damage.
	struct PlayerDamageLump_t
	{
		static const unsigned short LumpId = TFSTATS_LUMP_MAPDAMAGE;	// Lump ids.
		float			fTime;										// Time of the damage event
		short			nTargetPosition[3];							// Position of target.
		short			nAttackerPosition[3];						// Position of attacker.
		short			iDamage;									// Total damage.
		short			iWeapon;									// Weapon used.
		byte			iAttackClass;								// Class of the attacker
		byte			iTargetClass;								// Class of the target
		byte			iCrit;										// was the shot a crit?
		byte			iKill;										// did the shot kill the target?
	};

	// Data.
	LevelHeader_t					m_Header;							// Level header.
	// Disabling These Fields
	//CUtlVector<PlayerDeathsLump_t>	m_aPlayerDeaths;					// Vector of player deaths.
	//CUtlVector<PlayerDamageLump_t>	m_aPlayerDamage;					// Vector of player damage.	
	bool							m_bIsRealServer;
	TF_Gamestats_ClassStats_t		m_aClassStats[TF_CLASS_COUNT_ALL];	// Vector of class data
	TF_Gamestats_WeaponStats_t		m_aWeaponStats[TF_WEAPON_COUNT];	// Vector of weapon data
	// Temporary data.
	bool							m_bInitialized;		// Has the map Map Stat Data been initialized.
	time_t							m_iMapStartTime;
	time_t							m_iRoundStartTime; // time_t version for steamworks stats
	float							m_flRoundStartTime;
	int								m_iPeakPlayerCount[TF_TEAM_COUNT];
};

struct TF_Gamestats_RoundStats_t
{
public:

	TF_Gamestats_RoundStats_t();
	~TF_Gamestats_RoundStats_t();

private:
	TF_Gamestats_RoundStats_t( const TF_Gamestats_RoundStats_t &stats ) {}

public:
	void Reset();
	void ResetSummary();

	struct RoundSummary_t
	{
		int iTeamQuit;
		int iPoints;
		int iBonusPoints;
		int iKills;
		int iDeaths;
		int	iSuicides;
		int	iAssists;
		int	iBuildingsBuilt;
		int	iBuildingsDestroyed;
		int	iHeadshots;
		int	iDominations;
		int	iRevenges;
		int	iInvulns;
		int	iTeleports;
		int	iDamageDone;
		int	iHealingDone;
		int	iCrits;
		int	iBackstabs;
		int iThrowableHits;
		int iThrowableKills;
	};

	RoundSummary_t				m_Summary;

	static time_t				m_iRoundStartTime;
	static int					m_iNumRounds;
};

struct TF_Gamestats_KillStats_t
{
public:
	TF_Gamestats_KillStats_t();
	~TF_Gamestats_KillStats_t();

private:
	TF_Gamestats_KillStats_t( const TF_Gamestats_KillStats_t &stats ) {}

public:
	void Reset();
};

// Old style killstats matrix.
struct KillStats_t
{
	KillStats_t() { Reset(); }

	void Reset()
	{
		Q_memset( iNumKilled, 0, sizeof( iNumKilled ) );
		Q_memset( iNumKilledBy, 0, sizeof( iNumKilledBy ) );
		Q_memset( iNumKilledByUnanswered, 0, sizeof( iNumKilledByUnanswered ) );
	}

	int iNumKilled[MAX_PLAYERS+1];					// how many times this player has killed every other player
	int iNumKilledBy[MAX_PLAYERS+1];				// how many times this player has been killed by every other player
	int iNumKilledByUnanswered[MAX_PLAYERS+1];		// how many unanswered kills this player has been dealt by every other player
};

//=============================================================================
// LoadoutStats
struct LoadoutStats_t
{
	LoadoutStats_t() { Reset(); }

	void Reset()
	{
		V_memset( iLoadoutItemDefIndices, INVALID_ITEM_DEF_INDEX, sizeof( iLoadoutItemDefIndices ) );
		V_memset( iLoadoutItemQualities, AE_UNDEFINED, sizeof( iLoadoutItemQualities ) );
		V_memset( iLoadoutItemStyles, 0, sizeof( iLoadoutItemStyles ) );
		
		flStartTime = 0;
		iClass = TF_CLASS_UNDEFINED;
	}

	void Set ( int iPlayerClass )
	{
		iClass = iPlayerClass;
		flStartTime = gpGlobals->curtime;
	}

	void SetItemDef ( int iSlot, itemid_t iItemDef, entityquality_t iItemQuality, style_index_t iStyle )
	{
		iLoadoutItemDefIndices[iSlot] = iItemDef;
		iLoadoutItemQualities[iSlot] = iItemQuality;
		iLoadoutItemStyles[iSlot] = iStyle;
	}

	item_definition_index_t iLoadoutItemDefIndices[CLASS_LOADOUT_POSITION_COUNT];
	entityquality_t iLoadoutItemQualities[CLASS_LOADOUT_POSITION_COUNT];
	style_index_t iLoadoutItemStyles[CLASS_LOADOUT_POSITION_COUNT];
	float flStartTime;
	int iClass;
};

//=============================================================================
//
// TF Player Stats 
//
struct PlayerStats_t
{
	PlayerStats_t()	
	{
		Reset();
	};

	void Reset()
	{
		statsCurrentLife.Reset();
		statsCurrentRound.Reset();
		statsAccumulated.Reset();
		mapStatsCurrentLife.Reset();
		mapStatsCurrentRound.Reset();
		mapStatsAccumulated.Reset();
		statsKills.Reset();
		loadoutStats.Reset();
		iConnectTime = 0;
		iDisconnectTime = 0;
	}

	PlayerStats_t( const PlayerStats_t &other )
	{
		statsCurrentLife	= other.statsCurrentLife;
		statsCurrentRound	= other.statsCurrentRound;
		statsAccumulated	= other.statsAccumulated;
		mapStatsCurrentLife	= other.mapStatsCurrentLife;
		mapStatsCurrentRound = other.mapStatsCurrentRound;
		mapStatsAccumulated	= other.mapStatsAccumulated;
		loadoutStats		= other.loadoutStats;
		iConnectTime		= other.iConnectTime;
		iDisconnectTime		= other.iDisconnectTime;
	}

	RoundStats_t	statsCurrentLife;
	RoundStats_t	statsCurrentRound;
	RoundStats_t	statsAccumulated;
	RoundMapStats_t	mapStatsCurrentLife;
	RoundMapStats_t	mapStatsCurrentRound;
	RoundMapStats_t	mapStatsAccumulated;
	KillStats_t		statsKills;
	LoadoutStats_t	loadoutStats;
	int				iConnectTime;
	int				iDisconnectTime;
};

// reported stats structure that contains all stats data uploaded from TF server to Steam.  Note that this
// code is shared between TF server and processgamestats, which cracks the data file on the back end
struct TFReportedStats_t
{
	TFReportedStats_t();
	~TFReportedStats_t();
	void Clear();
	TF_Gamestats_LevelStats_t	*FindOrAddMapStats( const char *szMapName );
#ifdef GAME_DLL
	void AppendCustomDataToSaveBuffer( CUtlBuffer &SaveBuffer );
	bool LoadCustomDataFromBuffer( CUtlBuffer &LoadBuffer );
#endif 

	bool													m_bValidData;
	TF_Gamestats_LevelStats_t								*m_pCurrentGame;
	CUtlDict<TF_Gamestats_LevelStats_t, unsigned short>		m_dictMapStats;
};

struct ClassStats_t
{
	int					iPlayerClass;		// which class these stats refer to
	int					iNumberOfRounds;	// how many times player has played this class
	RoundStats_t		accumulated;
	RoundStats_t		max;
	RoundStats_t		currentRound;

	RoundStats_t		accumulatedMVM;
	RoundStats_t		maxMVM;

	ClassStats_t()
	{
		iPlayerClass	= TF_CLASS_UNDEFINED;
		iNumberOfRounds = 0;
	}

	void AccumulateRound( const RoundStats_t &other )
	{
		iNumberOfRounds++;
		accumulated.AccumulateRound( other );
		currentRound = other;
	}

	void AccumulateMVMRound( const RoundStats_t &other )
	{
		iNumberOfRounds++;
		accumulatedMVM.AccumulateRound( other );
		currentRound = other;
	}
};

struct MapStats_t
{
	map_identifier_t	iMapID;				// which map these stats refer to
	int					iNumberOfRounds;	// how many times player has played this map
	RoundMapStats_t		accumulated;
	RoundMapStats_t		currentRound;

	MapStats_t()
	{
		iMapID = 0xFFFFFFFF;
		iNumberOfRounds = 0;
	}

	void AccumulateRound( const RoundMapStats_t &other )
	{
		iNumberOfRounds++;
		accumulated.AccumulateRound( other );
		currentRound = other;
	}
};

//=============================================================================
// Beta Map Stats
//=============================================================================

//=============================================================================
// Robot Destruction
struct RobotDestructionStats_t
{
	RobotDestructionStats_t();

	void	Clear();
	int		GetRobotInteractionCount();
	int		GetRobotCoreInteractionCount();
	int		GetFlagInteractionCount();

	// Robot Cores Collected
	int		iCoresCollectedByTeam[ TF_TEAM_COUNT ];

	// Collected By What Class
	int		iCoreCollectedByClass[ TF_CLASS_COUNT ];

	// Robots Killed By Type
	// eRobotType::NUM_ROBOT_TYPES
	int		iBlueRobotsKilledByType[ 3 ];
	int		iRedRobotsKilledByType[ 3 ];

	int		iRobotsDamageFromClass[ TF_CLASS_COUNT ];

	// Player Interaction
	int		iRobotInteraction[MAX_PLAYERS];
	int		iRobotCoreInteraction[MAX_PLAYERS];
	int		iFlagInteraction[MAX_PLAYERS];
};

//=============================================================================
// Cactus Canyon

//=============================================================================
// Passtime
struct PasstimeStats_t
{
	PasstimeStats_t() { Clear(); }
	void Clear();
	void AddBallFracSample( float f );
	void AddPassTravelDistSample( float f );

	// To get comprehensive class stats, we need an event log instead of a summary.
	// But for now this should cover what we need.
	// These class stats were specifically requested by Travis@br, in addition to
	// total kills by class. The total kills by class is tracked by TF already.
	struct Classes_t
	{
		int nTotalScores;
		int nTotalCarrySec;
	} classes[TF_CLASS_COUNT_ALL];

	struct RoundSummary_t
	{
		int nTotalPassesStarted;
		int nTotalPassesFailed;
		int nTotalPassesShotDown;
		int nTotalPassesCompleted;
		int nTotalPassesCompletedNearGoal;
		int nTotalPassesIntercepted;
		int nTotalPassesInterceptedNearGoal;
		int nTotalPassRequests;
		int nTotalTosses;
		int nTotalTossesCompleted;
		int nTotalTossesIntercepted;
		int nTotalTossesInterceptedNearGoal;
		int nTotalSteals;
		int nTotalStealsNearGoal; 
		int nTotalBallSpawnShots;
		int nTotalScores;
		int nTotalRecoveries;
		int nTotalCarrySec;
		int nTotalWinningTeamBallCarrySec;
		int nTotalLosingTeamBallCarrySec;
		int nTotalThrowCancels;
		int nTotalSpeedBoosts;
		int nTotalJumpPads;
		int nTotalCarrierSpeedBoosts;
		int nTotalCarrierJumpPads;
		int nTotalBallDeflects;
		int nBallNeutralSec;
		int nGoalType;
		int nRoundEndReason;
		int nRoundRemainingSec;
		int nRoundMaxSec;
		int nPlayersRedMax;
		int nPlayersBlueMax;
		int nScoreBlue;
		int nScoreRed;
		bool bStalemate;
		bool bSuddenDeath;
		bool bMeleeOnlySuddenDeath;

		// histogram used to create min/max/mean/med/mode/stdev stats
		uint32 nBallFracSampleCount;
		uint32 arrBallFracHist[ 256 ];
		uint32 nBallFracHistSum;

		// sample set used to create min/max/mean/med/stdev stats
		static const uint32 k_nMaxPassTravelDistSamples = 1024;
		uint32 nPassTravelDistSampleCount;
		uint16 arrPassTravelDistSamples[ k_nMaxPassTravelDistSamples ];
	} summary;
};

const char* GetGameTypeID();

#ifdef CLIENT_DLL
MapStats_t &GetMapStats( map_identifier_t iMapID );
#endif

#endif // TF_GAMESTATS_SHARED_H