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


#include "cbase.h"
#include "achievementmgr.h"
#include "baseachievement.h"

#ifdef GAME_DLL
#include "prop_portal.h"
#include "util.h"

CAchievementMgr g_AchievementMgrPortal;	// global achievement mgr for Portal

class CAchievementPortalInfiniteFall : public CBaseAchievement
{
	DECLARE_CLASS( CAchievementPortalInfiniteFall, CBaseAchievement );

public:
	void Init() 
	{
		SetFlags( ACH_SAVE_WITH_GAME );
		SetGameDirFilter( "portal" );
		SetGoal( 1 );
		m_fAccumulatedDistance = 0.0f;
		m_bIsFlinging = false;
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "portal_player_portaled" );
		ListenForGameEvent( "portal_player_touchedground" );
	}
	virtual void PreRestoreSavedGame()
	{
		m_fAccumulatedDistance = 0.0f;
		m_bIsFlinging = false;
		BaseClass::PreRestoreSavedGame();
	}

protected:
	virtual void FireGameEvent( IGameEvent *event )
	{
		const char *name = event->GetName();
		if ( 0 == Q_strcmp( name, "portal_player_portaled" ) )
		{
			bool bIsPortal2 = event->GetBool( "portal2", false );
			// Get the portals that they teleported through
			CProp_Portal *pInPortal = CProp_Portal::FindPortal( 0, bIsPortal2, false );
			CProp_Portal *pOutPortal = CProp_Portal::FindPortal( 0, !bIsPortal2, false );

			if ( pInPortal && pOutPortal )
			{
				if ( m_bIsFlinging )
				{
					// Add up how far we traveled since the last teleport
					m_fAccumulatedDistance += m_fZPortalPosition - pInPortal->GetAbsOrigin().z;

					if ( m_fAccumulatedDistance > 30000.0f * 12 )
						IncrementCount();
				}

				// Remember the Z position to get the distance when the teleport again or land
				m_fZPortalPosition = pOutPortal->GetAbsOrigin().z;
				m_bIsFlinging = true;
			}
		}
		else if ( 0 == Q_strcmp( name, "portal_player_touchedground" ) )
		{
			if ( m_bIsFlinging )
			{
				CBasePlayer *pLocalPlayer = UTIL_GetLocalPlayer();

				if ( pLocalPlayer )
				{
					m_fAccumulatedDistance += m_fZPortalPosition - pLocalPlayer->GetAbsOrigin().z;

					if ( m_fAccumulatedDistance > 30000.0f * 12 )
						IncrementCount();

					m_fAccumulatedDistance = 0.0f;
					m_bIsFlinging = false;
				}
			}
		}
	}

private:
	bool		m_bIsFlinging;
	float		m_fAccumulatedDistance;
	float		m_fZPortalPosition;
};
DECLARE_ACHIEVEMENT( CAchievementPortalInfiniteFall, ACHIEVEMENT_PORTAL_INFINITEFALL, "PORTAL_INFINITEFALL", 5 );

class CAchievementPortalLongJump : public CBaseAchievement
{
	DECLARE_CLASS( CAchievementPortalLongJump, CBaseAchievement );

public:
	void Init() 
	{
		SetFlags( ACH_SAVE_WITH_GAME );
		SetGameDirFilter( "portal" );
		SetGoal( 1 );
		m_fAccumulatedDistance = 0.0f;
		m_bIsFlinging = false;
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "portal_player_portaled" );
		ListenForGameEvent( "portal_player_touchedground" );
	}
	virtual void PreRestoreSavedGame()
	{
		m_fAccumulatedDistance = 0.0f;
		m_bIsFlinging = false;
		BaseClass::PreRestoreSavedGame();
	}

protected:
	virtual void FireGameEvent( IGameEvent *event )
	{
		const char *name = event->GetName();
		if ( 0 == Q_strcmp( name, "portal_player_portaled" ) )
		{
			bool bIsPortal2 = event->GetBool( "portal2", false );
			// Get the portals that they teleported through
			CProp_Portal *pInPortal = CProp_Portal::FindPortal( 0, bIsPortal2, false );
			CProp_Portal *pOutPortal = CProp_Portal::FindPortal( 0, !bIsPortal2, false );

			if ( pInPortal && pOutPortal )
			{
				if ( m_bIsFlinging )
				{
					// Add up how far we traveled since the last teleport
					float flDist = pInPortal->GetAbsOrigin().AsVector2D().DistTo( m_vec2DPortalPosition );
				
					// Ignore small distances that can be caused by microadjustments in infinite falls
					if ( flDist > 63.0f )
					{
						m_fAccumulatedDistance += flDist;
					}

					if ( m_fAccumulatedDistance > 300.0f * 12 )
						IncrementCount();
				}

				// Remember the 2D position to get the distance when the teleport again or land
				m_vec2DPortalPosition = pOutPortal->GetAbsOrigin().AsVector2D();
				m_bIsFlinging = true;
			}
		}
		else if ( 0 == Q_strcmp( name, "portal_player_touchedground" ) )
		{
			if ( m_bIsFlinging )
			{
				CBasePlayer *pLocalPlayer = UTIL_GetLocalPlayer();

				if ( pLocalPlayer )
				{
					float flDist = pLocalPlayer->GetAbsOrigin().AsVector2D().DistTo( m_vec2DPortalPosition );

					// Ignore small distances that can be caused by microadjustments in infinite falls
					if ( flDist > 63.0f )
					{
						m_fAccumulatedDistance += flDist;
					}

					if ( m_fAccumulatedDistance > 300.0f * 12 )
						IncrementCount();

					m_fAccumulatedDistance = 0.0f;
					m_bIsFlinging = false;
				}
			}
		}
	}

private:
	bool		m_bIsFlinging;
	float		m_fAccumulatedDistance;
	Vector2D	m_vec2DPortalPosition;
};
DECLARE_ACHIEVEMENT( CAchievementPortalLongJump, ACHIEVEMENT_PORTAL_LONGJUMP, "PORTAL_LONGJUMP", 5 );

class CAchievementPortalBeat2AdvancedMaps: public CBaseAchievement
{
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_GLOBAL );
		SetGoal( 2 );
		m_iProgressMsgMinimum = 0;
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "advanced_map_complete" );
	}

protected:
	virtual void FireGameEvent( IGameEvent* event )
	{
		if ( !Q_stricmp( event->GetName(), "advanced_map_complete" ) )
		{
			if ( !IsAchieved() )
			{
				int iNumAdvanced = event->GetInt( "numadvanced" );

				SetCount ( iNumAdvanced );
				if ( iNumAdvanced >= GetGoal() )
				{
					AwardAchievement();
				}
				else
				{
					HandleProgressUpdate();
				}
			}
		}
	}
	virtual void CalcProgressMsgIncrement()
	{
		// show progress every tick
		m_iProgressMsgIncrement = 1;
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalBeat2AdvancedMaps, ACHIEVEMENT_PORTAL_BEAT_2ADVANCEDMAPS, "PORTAL_BEAT_2ADVANCEDMAPS", 10 );

class CAchievementPortalBeat4AdvancedMaps : public CBaseAchievement
{
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_GLOBAL );
		SetGoal( 4 );
		m_iProgressMsgMinimum = 3;
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "advanced_map_complete" );
	}

protected:
	virtual void FireGameEvent( IGameEvent* event )
	{
		if ( !Q_stricmp( event->GetName(), "advanced_map_complete" ) )
		{
			if ( !IsAchieved() )
			{
				int iNumAdvanced = event->GetInt( "numadvanced" );

				SetCount ( iNumAdvanced );
				if ( iNumAdvanced >= GetGoal() )
				{
					AwardAchievement();
				}
				else
				{
					HandleProgressUpdate();
				}
			}
		}
	}
	virtual void CalcProgressMsgIncrement()
	{
		// show progress every tick
		m_iProgressMsgIncrement = 1;
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalBeat4AdvancedMaps, ACHIEVEMENT_PORTAL_BEAT_4ADVANCEDMAPS, "PORTAL_BEAT_4ADVANCEDMAPS", 20 );

class CAchievementPortalBeat6AdvancedMaps : public CBaseAchievement
{
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_GLOBAL );
		SetGoal( 6 );
		m_iProgressMsgMinimum = 5;
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "advanced_map_complete" );
	}

protected:
	virtual void FireGameEvent( IGameEvent* event )
	{
		if ( !Q_stricmp( event->GetName(), "advanced_map_complete" ) )
		{
			if ( !IsAchieved() )
			{
				int iNumAdvanced = event->GetInt( "numadvanced" );

				SetCount ( iNumAdvanced );
				if ( iNumAdvanced >= GetGoal() )
				{
					AwardAchievement();
				}
				else
				{
					HandleProgressUpdate();
				}
			}
		}
	}
	virtual void CalcProgressMsgIncrement()
	{
		// show progress every tick
		m_iProgressMsgIncrement = 1;
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalBeat6AdvancedMaps, ACHIEVEMENT_PORTAL_BEAT_6ADVANCEDMAPS, "PORTAL_BEAT_6ADVANCEDMAPS", 30 );

class CAchievementPortalGetAllBronze : public CBaseAchievement
{
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_GLOBAL );
		SetGoal( 18 );
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "challenge_map_complete" );
	}

protected:
	virtual void FireGameEvent( IGameEvent* event )
	{
		if ( !Q_stricmp( event->GetName(), "challenge_map_complete" ) )
		{
			if ( !IsAchieved() )
			{
				int iBronzeCount = event->GetInt( "numbronze" );

				SetCount ( iBronzeCount );
				if ( iBronzeCount >= GetGoal() )
				{
					AwardAchievement();
				}
				else
				{
					HandleProgressUpdate();
				}
			}
		}
	}
	virtual void CalcProgressMsgIncrement()
	{
		// show progress every tick
		m_iProgressMsgIncrement = 1;
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalGetAllBronze, ACHIEVEMENT_PORTAL_GET_ALLBRONZE, "PORTAL_GET_ALLBRONZE", 10 );

class CAchievementPortalGetAllSilver : public CBaseAchievement
{
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_GLOBAL );
		SetGoal( 18 );
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "challenge_map_complete" );
	}

protected:
	virtual void FireGameEvent( IGameEvent* event )
	{
		if ( !Q_stricmp( event->GetName(), "challenge_map_complete" ) )
		{
			if ( !IsAchieved() )
			{
				int iSilverCount = event->GetInt( "numsilver" );

				SetCount ( iSilverCount );
				if ( iSilverCount >= GetGoal() )
				{
					AwardAchievement();
				}
				else
				{
					HandleProgressUpdate();
				}
			}
		}
	}
	virtual void CalcProgressMsgIncrement()
	{
		// show progress every tick
		m_iProgressMsgIncrement = 1;
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalGetAllSilver, ACHIEVEMENT_PORTAL_GET_ALLSILVER, "PORTAL_GET_ALLSILVER", 20 );

class CAchievementPortalGetAllGold : public CBaseAchievement
{
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_GLOBAL );
		SetGoal( 18 );
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "challenge_map_complete" );
	}
	
protected:
	virtual void FireGameEvent( IGameEvent* event )
	{
		if ( !Q_stricmp( event->GetName(), "challenge_map_complete" ) )
		{
			if ( !IsAchieved() )
			{
				int iGoldCount = event->GetInt( "numgold" );

				SetCount ( iGoldCount );
				if ( iGoldCount >= GetGoal() )
				{
					AwardAchievement();
				}
				else
				{
					HandleProgressUpdate();
				}
			}
		}
	}
	virtual void CalcProgressMsgIncrement()
	{
		// show progress every tick
		m_iProgressMsgIncrement = 1;
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalGetAllGold, ACHIEVEMENT_PORTAL_GET_ALLGOLD, "PORTAL_GET_ALLGOLD", 40 );


class CAchievementPortalDetachAllCameras : public CBaseAchievement
{
protected:
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "security_camera_detached" );
	}

	void FireGameEvent_Internal( IGameEvent *event )
	{
		if ( 0 == Q_strcmp( event->GetName(), "security_camera_detached" ) )
		{
			IncrementCount();
		}
	}
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_WITH_GAME );
		SetGoal( 33 );
		ListenForGameEvent( "security_camera_detached" );
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalDetachAllCameras, ACHIEVEMENT_PORTAL_DETACH_ALL_CAMERAS, "PORTAL_DETACH_ALL_CAMERAS", 5 );


class CAchievementPortalHitTurretWithTurret : public CBaseAchievement
{
protected:
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "turret_hit_turret" );
	}

	void FireGameEvent_Internal( IGameEvent *event )
	{
		if ( 0 == Q_strcmp( event->GetName(), "turret_hit_turret" ) )
		{
			IncrementCount();
		}
	}
public:
	virtual void Init()
	{
		SetFlags( ACH_SAVE_GLOBAL );
		SetGoal( 1 );
		ListenForGameEvent( "turret_hit_turret" );
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalHitTurretWithTurret, ACHIEVEMENT_PORTAL_HIT_TURRET_WITH_TURRET, "PORTAL_HIT_TURRET_WITH_TURRET", 5 );


#ifndef _XBOX
class CAchievementPortalFindAllDinosaurs : public CBaseAchievement
{
	DECLARE_CLASS( CAchievementPortalFindAllDinosaurs, CBaseAchievement );
	void Init() 
	{
		SetFlags( ACH_HAS_COMPONENTS | ACH_SAVE_GLOBAL );
		m_iNumComponents = 26;
		SetStoreProgressInSteam( true );
		SetGoal( m_iNumComponents );
		BaseClass::Init();
		m_iProgressMsgMinimum = 1;
	}
	virtual void ListenForEvents()
	{
		ListenForGameEvent( "dinosaur_signal_found" );
	}
	virtual void FireGameEvent_Internal( IGameEvent *event )
	{
		if ( 0 == Q_strcmp( event->GetName(), "dinosaur_signal_found" ) )
		{
			int id = event->GetInt( "id", -1 );
			Assert( id >= 0 && id < m_iNumComponents );
			if ( id >= 0 && id < m_iNumComponents )
			{
				EnsureComponentBitSetAndEvaluate( id );
				
				// Update our Steam stat
				steamapicontext->SteamUserStats()->SetStat( "PORTAL_TRANSMISSION_RECEIVED_STAT", m_iCount );
			}
			else
			{
				Warning( "Failed to set achievement progress. Dinosaur ID(%d) out of range (0 to %d)\n", id, m_iNumComponents );
			}
		}
	}
	virtual void CalcProgressMsgIncrement()
	{
		// Show progress every tick
		m_iProgressMsgIncrement = 1;
	}
};
DECLARE_ACHIEVEMENT( CAchievementPortalFindAllDinosaurs, ACHIEVEMENT_PORTAL_TRANSMISSION_RECEIVED, "PORTAL_TRANSMISSION_RECEIVED", 0 );
#endif // _XBOX

// achievements which are won by a map event firing once
DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_GET_PORTALGUNS, "PORTAL_GET_PORTALGUNS", 5 );
DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_KILL_COMPANIONCUBE, "PORTAL_KILL_COMPANIONCUBE", 5 );
DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_ESCAPE_TESTCHAMBERS, "PORTAL_ESCAPE_TESTCHAMBERS", 5 );
DECLARE_MAP_EVENT_ACHIEVEMENT( ACHIEVEMENT_PORTAL_BEAT_GAME, "PORTAL_BEAT_GAME", 10 );

#endif // GAME_DLL