//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
//=============================================================================

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

#include "GameEventListener.h"
#include "hl2orange.spa.h"
#include "iachievementmgr.h"

class CAchievementMgr;

//
// Base class for achievements
//

class CBaseAchievement : public CGameEventListener, public IAchievement
{
	DECLARE_CLASS_NOBASE( CBaseAchievement );
public:
	CBaseAchievement();	
	virtual ~CBaseAchievement();
	virtual void Init() {}
	virtual void ListenForEvents() {};
	virtual void Event_EntityKilled( CBaseEntity *pVictim, CBaseEntity *pAttacker, CBaseEntity *pInflictor, IGameEvent *event );

	int GetAchievementID() { return m_iAchievementID; }
	void SetAchievementID( int iAchievementID ) { m_iAchievementID = iAchievementID; }
	void SetName( const char *pszName ) { m_pszName = pszName; }
	const char *GetName() { return m_pszName; }
	const char *GetStat() { return m_pszStat?m_pszStat:GetName(); }
	void SetFlags( int iFlags );
	int GetFlags() { return m_iFlags; }
	void SetGoal( int iGoal ) { m_iGoal = iGoal; }
	int GetGoal() { return m_iGoal; }
	void SetGameDirFilter( const char *pGameDir );
	bool HasComponents() { return ( m_iFlags & ACH_HAS_COMPONENTS ) > 0; }	
	void SetPointValue( int iPointValue ) { m_iPointValue = iPointValue; }
	int	GetPointValue() { return m_iPointValue; }
	bool ShouldHideUntilAchieved() { return m_bHideUntilAchieved; }
	void SetHideUntilAchieved( bool bHide ) { m_bHideUntilAchieved = bHide; }
	void SetStoreProgressInSteam( bool bStoreProgressInSteam ) { m_bStoreProgressInSteam = bStoreProgressInSteam; }
	bool StoreProgressInSteam() { return m_bStoreProgressInSteam; }
	virtual bool ShouldShowProgressNotification() { return true; }
	virtual void OnPlayerStatsUpdate() {}

	virtual bool ShouldSaveWithGame();
	bool ShouldSaveGlobal();
	virtual void PreRestoreSavedGame();
	virtual void PostRestoreSavedGame();
	void SetCount( int iCount ) { m_iCount = iCount; }
	int GetCount() { return m_iCount; }
	void SetProgressShown( int iProgressShown ) { m_iProgressShown = iProgressShown; }
	int GetProgressShown() { return m_iProgressShown; }
	virtual bool IsAchieved() { return m_bAchieved; }
	virtual bool IsActive();
	virtual bool LocalPlayerCanEarn( void ) { return true; }
	void SetAchieved( bool bAchieved ) { m_bAchieved = bAchieved; }
	virtual bool IsMetaAchievement() { return false; }
	virtual bool AlwaysListen() { return false; }
	virtual bool AlwaysEnabled() { return false; }

	//=============================================================================
	// HPE_BEGIN:
	// [pfreese] Notification method for derived classes
	//=============================================================================
	
	virtual void OnAchieved() {}
	uint32 GetUnlockTime() const { return m_uUnlockTime; }
	void SetUnlockTime( uint32 unlockTime ) { m_uUnlockTime = unlockTime; }
	
	//=============================================================================
	// HPE_END
	//=============================================================================

	uint64 GetComponentBits() { return m_iComponentBits; }
	void SetComponentBits( uint64 iComponentBits );
	void OnComponentEvent( const char *pchComponentName );
	void EnsureComponentBitSetAndEvaluate( int iBitNumber );
	void EvaluateIsAlreadyAchieved();
	virtual void OnMapEvent( const char *pEventName );
	virtual void PrintAdditionalStatus() {}		// for debugging, achievements may report additional status in achievement_status concmd
	virtual void OnSteamUserStatsStored() {}
	virtual void UpdateAchievement( int nData ) {}
	virtual bool ShouldShowOnHUD() { return m_bShowOnHUD; }
	virtual void SetShowOnHUD( bool bShow );

	//=============================================================================
	// HPE_BEGIN:
	// [pfreese] Serialization methods
	//=============================================================================
	
	virtual void GetSettings( KeyValues* pNodeOut );				// serialize
	virtual void ApplySettings( /* const */ KeyValues* pNodeIn );	// unserialize
	
	//=============================================================================
	// HPE_END
	//=============================================================================

	virtual void Think( void ) { return; }

	const char *GetMapNameFilter( void ){ return m_pMapNameFilter; }
	CAchievementMgr *GetAchievementMgr( void ){ return m_pAchievementMgr; }

protected:
	virtual void FireGameEvent( IGameEvent *event );
	virtual void FireGameEvent_Internal( IGameEvent *event ) {};
	void SetVictimFilter( const char *pClassName );
	void SetAttackerFilter( const char *pClassName );
	void SetInflictorFilter( const char *pClassName );
	void SetInflictorEntityNameFilter( const char *pEntityName );
	void SetMapNameFilter( const char *pMapName );
	void SetComponentPrefix( const char *pPrefix );
	void IncrementCount( int iOptIncrement = 0 );
	void EvaluateNewAchievement();
	void AwardAchievement();
	void ShowProgressNotification();
	void HandleProgressUpdate();
	virtual void CalcProgressMsgIncrement();
	void SetNextThink( float flThinkTime );
	void ClearThink( void );
	void SetStat( const char* pStatName ) { m_pszStat = pStatName; }

	const char *m_pszName;								// name of this achievement
	const char *m_pszStat;								// stat this achievement uses
	int m_iAchievementID;								// ID of this achievement
	int	m_iFlags;										// ACH_* flags for this achievement
	int	m_iGoal;										// goal # of steps to award this achievement
	int m_iProgressMsgIncrement;						// after how many steps show we show a progress notification
	int m_iProgressMsgMinimum;							// the minimum progress needed before showing progress notification
	int m_iPointValue;									// # of points this achievement is worth (currently only used for XBox Live)
	bool m_bHideUntilAchieved;							// should this achievement be hidden until achieved?
	bool m_bStoreProgressInSteam;						// should incremental progress be stored in Steam.  A counter with same name as achievement must be set up in Steam.
	const char *m_pInflictorClassNameFilter;			// if non-NULL, inflictor class name to filter with
	const char *m_pInflictorEntityNameFilter;			// if non-NULL, inflictor entity name to filter with
	const char *m_pVictimClassNameFilter;				// if non-NULL, victim class name to filter with
	const char *m_pAttackerClassNameFilter;				// if non-NULL, attacker class name to filter with
	const char *m_pMapNameFilter;						// if non-NULL, map name to filter with
	const char *m_pGameDirFilter;						// if non-NULL, game dir name to filter with

	const char **m_pszComponentNames;			
	int			m_iNumComponents;
	const char *m_pszComponentPrefix;
	int			m_iComponentPrefixLen;
	bool		m_bAchieved;							// is this achievement achieved
	uint32		m_uUnlockTime;							// time_t that this achievement was unlocked (0 if before Steamworks unlock time support)
	int			m_iCount;								// # of steps satisfied toward this achievement (only valid if not achieved)
	int			m_iProgressShown;						// # of progress msgs we've shown
	uint64		m_iComponentBits;						// bitfield of components achieved
	CAchievementMgr *m_pAchievementMgr;					// our achievement manager
	bool		m_bShowOnHUD;							// if set, the player wants this achievement pinned to the HUD

	friend class CAchievementMgr;
public:
	DECLARE_DATADESC();
};

class CFailableAchievement : public CBaseAchievement
{
	DECLARE_CLASS( CFailableAchievement, CBaseAchievement );
public:
	CFailableAchievement();
	void SetFailed();	

	virtual bool ShouldSaveWithGame();
	virtual void PreRestoreSavedGame();
	virtual void PostRestoreSavedGame();
	virtual bool IsAchieved() { return !m_bFailed && BaseClass::IsAchieved(); }
	virtual bool IsActive() { return m_bActivated && !m_bFailed && BaseClass::IsActive(); }
	bool IsFailed() { return m_bFailed; }

	virtual void OnMapEvent( const char *pEventName );
	virtual void OnActivationEvent() { Activate(); }
	virtual void OnEvaluationEvent();
	virtual const char *GetActivationEventName() =0;
	virtual const char *GetEvaluationEventName() =0;

protected:
	void Activate();

	bool	m_bActivated;		// are we activated? (If there is a map event that turns us on, has that happened)
	bool	m_bFailed;			// has this achievement failed
	
public:
	DECLARE_DATADESC();
};

class CMapAchievement : public CBaseAchievement
{
	virtual void Init()
	{
		SetFlags( ACH_LISTEN_MAP_EVENTS | ACH_SAVE_GLOBAL );
		SetGoal( 1 );
	}
};


//----------------------------------------------------------------------------------------------------------------
class CAchievement_AchievedCount : public CBaseAchievement
{
public:
	void Init();
	virtual void OnSteamUserStatsStored( void );
	virtual bool IsMetaAchievement() { return true; }

	int GetLowRange() { return m_iLowRange; }
	int GetHighRange() { return m_iHighRange; }
	int GetNumRequired() { return m_iNumRequired; }

protected:
	void SetAchievementsRequired( int iNumRequired, int iLowRange, int iHighRange );

private:
	int m_iNumRequired;
	int m_iLowRange;
	int m_iHighRange;
};

//
// Helper class for achievement creation
//
 
typedef CBaseAchievement* (*achievementCreateFunc) (void);
class CBaseAchievementHelper
{
public:
	CBaseAchievementHelper( achievementCreateFunc createFunc )
	{
		m_pfnCreate = createFunc;
		m_pNext = s_pFirst;
		s_pFirst = this;
	}
	achievementCreateFunc m_pfnCreate;
	CBaseAchievementHelper *m_pNext;
	static CBaseAchievementHelper *s_pFirst;
};

#define DECLARE_ACHIEVEMENT_( className, achievementID, achievementName, gameDirFilter, iPointValue, bHidden ) \
static CBaseAchievement *Create_##className( void )					\
{																		\
	CBaseAchievement *pAchievement = new className( );					\
	pAchievement->SetAchievementID( achievementID );					\
	pAchievement->SetName( achievementName );							\
	pAchievement->SetPointValue( iPointValue );							\
	pAchievement->SetHideUntilAchieved( bHidden );						\
	if ( gameDirFilter ) pAchievement->SetGameDirFilter( gameDirFilter ); \
	return pAchievement;												\
};																		\
static CBaseAchievementHelper g_##className##_Helper( Create_##className );

#define DECLARE_ACHIEVEMENT( className, achievementID, achievementName, iPointValue ) \
	DECLARE_ACHIEVEMENT_( className, achievementID, achievementName, NULL, iPointValue, false )

#define DECLARE_MAP_EVENT_ACHIEVEMENT_( achievementID, achievementName, gameDirFilter, iPointValue, bHidden ) \
class CAchievement##achievementID : public CMapAchievement {};		\
DECLARE_ACHIEVEMENT_( CAchievement##achievementID, achievementID, achievementName, gameDirFilter, iPointValue, bHidden )	\

#define DECLARE_MAP_EVENT_ACHIEVEMENT( achievementID, achievementName, iPointValue )	\
	DECLARE_MAP_EVENT_ACHIEVEMENT_( achievementID, achievementName, NULL, iPointValue, false )

#define DECLARE_MAP_EVENT_ACHIEVEMENT_HIDDEN( achievementID, achievementName, iPointValue )	\
	DECLARE_MAP_EVENT_ACHIEVEMENT_( achievementID, achievementName, NULL, iPointValue, true )

#endif // BASEACHIEVEMENT_H