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

#ifndef AI_SCRIPTCONDITIONS_H
#define AI_SCRIPTCONDITIONS_H

#include "baseentity.h"
#include "entityoutput.h"
#include "simtimer.h"
#include "ai_npcstate.h"

#if defined( _WIN32 )
#pragma once
#endif

//-----------------------------------------------------------------------------

class CAI_ProxTester
{
public:
	CAI_ProxTester()
		: m_distSq( 0 ),
		m_fInside( false )
	{
	}

	void Init( float dist )
	{
		m_fInside = ( dist > 0 );
		m_distSq = dist * dist;
	}

	bool Check( CBaseEntity *pEntity1, CBaseEntity *pEntity2 )
	{
		if ( m_distSq != 0 )
		{
			float distSq = ( pEntity1->GetAbsOrigin() - pEntity2->GetAbsOrigin() ).LengthSqr();
			bool fInside = ( distSq < m_distSq );

			return ( m_fInside == fInside );
		}
		return true;
	}

	DECLARE_SIMPLE_DATADESC();

private:

	float m_distSq;
	bool  m_fInside;
};

//-----------------------------------------------------------------------------
class CAI_ScriptConditionsElement
{
public:

	DECLARE_SIMPLE_DATADESC();

	void			SetActor( CBaseEntity *pEntity ) { m_hActor = pEntity; }
	CBaseEntity		*GetActor( void ){ return m_hActor.Get(); }

	void			SetTimer( CSimTimer timer ) { m_Timer = timer;	}
	CSimTimer		*GetTimer( void ) { return &m_Timer;	}
	
	void			SetTimeOut( CSimTimer timeout) { m_Timeout = timeout;	}
	CSimTimer		*GetTimeOut( void ) { return &m_Timeout;	}

private:
	EHANDLE			m_hActor;
	CSimTimer		m_Timer;
	CSimTimer		m_Timeout;
};

//-----------------------------------------------------------------------------
// class CAI_ScriptConditions
//
// Purpose: Watches a set of conditions relative to a given NPC, and when they
//			are all satisfied, fires the relevant output
//-----------------------------------------------------------------------------

class CAI_ScriptConditions : public CBaseEntity, public IEntityListener
{
	DECLARE_CLASS( CAI_ScriptConditions, CBaseEntity );

public:
	CAI_ScriptConditions()
		:	m_fDisabled( true ),
		m_flRequiredTime( 0 ),
		m_fMinState( NPC_STATE_IDLE ),
		m_fMaxState( NPC_STATE_IDLE ),
		m_fScriptStatus( TRS_NONE ),
		m_fActorSeePlayer( TRS_NONE ),
		m_flPlayerActorProximity( 0 ),
		m_flPlayerActorFOV( -1 ),
		m_fPlayerActorLOS( TRS_NONE ),
		m_fActorSeeTarget( TRS_NONE ),
		m_flActorTargetProximity( 0 ),
		m_flPlayerTargetProximity( 0 ),
		m_flPlayerTargetFOV( 0 ),
		m_fPlayerTargetLOS( TRS_NONE ),
		m_fPlayerBlockingActor( TRS_NONE ),
		m_flMinTimeout( 0 ),
		m_flMaxTimeout( 0 ),
		m_fActorInPVS( TRS_NONE ),
		m_fActorInVehicle( TRS_NONE ),
		m_fPlayerInVehicle( TRS_NONE )
	{
#ifndef HL2_EPISODIC
		m_hActor = NULL;
#endif
	}

private:
	void Spawn();
	void Activate();

	void EvaluationThink();

	void Enable();
	void Disable();

	void SetThinkTime()			{ SetNextThink( gpGlobals->curtime + 0.250 ); }

	// Evaluators
	struct EvalArgs_t
	{
		CBaseEntity *pActor; 
		CBasePlayer *pPlayer; 
		CBaseEntity *pTarget;
	};

	bool EvalState( const EvalArgs_t &args );
	bool EvalActorSeePlayer( const EvalArgs_t &args );
	bool EvalPlayerActorLook( const EvalArgs_t &args );
	bool EvalPlayerTargetLook( const EvalArgs_t &args );
	bool EvalPlayerActorProximity( const EvalArgs_t &args );
	bool EvalPlayerTargetProximity( const EvalArgs_t &args );
	bool EvalActorTargetProximity( const EvalArgs_t &args );
	bool EvalActorSeeTarget( const EvalArgs_t &args );
	bool EvalPlayerActorLOS( const EvalArgs_t &args );
	bool EvalPlayerTargetLOS( const EvalArgs_t &args );
	bool EvalPlayerBlockingActor( const EvalArgs_t &args );
	bool EvalActorInPVS( const EvalArgs_t &args );
	bool EvalPlayerInVehicle( const EvalArgs_t &args );
	bool EvalActorInVehicle( const EvalArgs_t &args );

	void OnEntitySpawned( CBaseEntity *pEntity );

	int AddNewElement( CBaseEntity *pActor );

	bool ActorInList( CBaseEntity *pActor );
	void UpdateOnRemove( void );

	// Input handlers
	void InputEnable( inputdata_t &inputdata );
	void InputDisable( inputdata_t &inputdata );

	// Output handlers
	COutputEvent	m_OnConditionsSatisfied;
	COutputEvent	m_OnConditionsTimeout;
	COutputEvent	m_NoValidActors;

	//---------------------------------

#ifndef HL2_EPISODIC
	CBaseEntity *GetActor()		{ return m_hActor.Get();			}
#endif
	CBasePlayer *GetPlayer()	{ return UTIL_GetLocalPlayer();	}

	//---------------------------------

	// @Note (toml 07-17-02): At some point, it may be desireable to switch to using function objects instead of functions. Probably
	// if support for NPCs addiing custom conditions becomes necessary
	typedef bool (CAI_ScriptConditions::*EvaluationFunc_t)( const EvalArgs_t &args );

	struct EvaluatorInfo_t
	{
		EvaluationFunc_t	pfnEvaluator;
		const char			*pszName;
	};

	static EvaluatorInfo_t gm_Evaluators[];

	//---------------------------------
	// Evaluation helpers

	static bool IsInFOV( CBaseEntity *pViewer, CBaseEntity *pViewed, float fov, bool bTrueCone );
	static bool PlayerHasLineOfSight( CBaseEntity *pViewer, CBaseEntity *pViewed, bool fNot );
	static bool ActorInPlayersPVS( CBaseEntity *pActor, bool bNot );

	virtual void OnRestore( void );

	//---------------------------------
	// General conditions info

	bool			m_fDisabled;
	bool			m_bLeaveAsleep;
	EHANDLE			m_hTarget;

	float			m_flRequiredTime;	// How long should the conditions me true

#ifndef HL2_EPISODIC
	EHANDLE 		m_hActor;
	CSimTimer		m_Timer; 			// @TODO (toml 07-16-02): save/load of timer once Jay has save/load of contained objects
	CSimTimer		m_Timeout;
#endif

	//---------------------------------
	// Specific conditions data
	NPC_STATE		m_fMinState;
	NPC_STATE		m_fMaxState;
	ThreeState_t 	m_fScriptStatus;
	ThreeState_t 	m_fActorSeePlayer;
	string_t		m_Actor;

	float 			m_flPlayerActorProximity;
	CAI_ProxTester	m_PlayerActorProxTester;

	float			m_flPlayerActorFOV;
	bool			m_bPlayerActorFOVTrueCone;
	ThreeState_t	m_fPlayerActorLOS;
	ThreeState_t 	m_fActorSeeTarget;

	float 			m_flActorTargetProximity;
	CAI_ProxTester	m_ActorTargetProxTester;

	float 			m_flPlayerTargetProximity;
	CAI_ProxTester	m_PlayerTargetProxTester;

	float 			m_flPlayerTargetFOV;
	bool			m_bPlayerTargetFOVTrueCone;
	ThreeState_t	m_fPlayerTargetLOS;
	ThreeState_t	m_fPlayerBlockingActor;
	ThreeState_t	m_fActorInPVS;

	float			m_flMinTimeout;
	float			m_flMaxTimeout;

	ThreeState_t	m_fActorInVehicle;
	ThreeState_t	m_fPlayerInVehicle;

	CUtlVector< CAI_ScriptConditionsElement > m_ElementList;

	//---------------------------------

	DECLARE_DATADESC();
};

//=============================================================================

#endif // AI_SCRIPTCONDITIONS_H