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

#ifndef AI_BEHAVIOR_LEAD_H
#define AI_BEHAVIOR_LEAD_H

#include "simtimer.h"
#include "ai_behavior.h"

#if defined( _WIN32 )
#pragma once
#endif

typedef const char *AIConcept_t;

// Speak concepts
#define TLK_LEAD_START				"TLK_LEAD_START"
#define TLK_LEAD_ARRIVAL			"TLK_LEAD_ARRIVAL"
#define TLK_LEAD_SUCCESS			"TLK_LEAD_SUCCESS"
#define TLK_LEAD_FAILURE			"lead_fail"
#define TLK_LEAD_COMINGBACK			"TLK_LEAD_COMINGBACK"
#define TLK_LEAD_CATCHUP			"TLK_LEAD_CATCHUP"
#define TLK_LEAD_RETRIEVE			"TLK_LEAD_RETRIEVE"
#define TLK_LEAD_ATTRACTPLAYER		"TLK_LEAD_ATTRACTPLAYER"
#define TLK_LEAD_WAITOVER			"TLK_LEAD_WAITOVER"
#define TLK_LEAD_MISSINGWEAPON		"TLK_LEAD_MISSING_WEAPON"
#define TLK_LEAD_IDLE				"TLK_LEAD_IDLE"

//-----------------------------------------------------------------------------
// class CAI_LeadBehavior
//
// Purpose:
//
//-----------------------------------------------------------------------------

enum LeadBehaviorEvents_t
{
	LBE_ARRIVAL,
	LBE_ARRIVAL_DONE,
	LBE_SUCCESS,
	LBE_FAILURE,
	LBE_DONE,
};

//-------------------------------------
//
// Handler class interface to listen to and modify actions of the lead behavior.
// Could be an NPC, or another entity (like a goal entity)
//

class CAI_LeadBehaviorHandler
{
public:
	virtual void OnEvent( int event ) {}
	virtual const char *GetConceptModifiers( const char *pszConcept )	{ return NULL; }
};

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

enum AI_LeadFlags_t
{
	AILF_NO_DEF_SUCCESS	 = 0x01,
	AILF_NO_DEF_FAILURE	 = 0x02,
	AILF_USE_GOAL_FACING = 0x04,
};

struct AI_LeadArgs_t
{
	const char *pszGoal;
	const char *pszWaitPoint;
	unsigned 	flags;
	float		flWaitDistance;
	float		flLeadDistance;
	float		flRetrieveDistance;
	float		flSuccessDistance;
	bool		bRun;
	int			iRetrievePlayer;
	int			iRetrieveWaitForSpeak;
	int			iComingBackWaitForSpeak;
	bool		bStopScenesWhenPlayerLost;
	bool		bDontSpeakStart;
	bool		bLeadDuringCombat;
	bool		bGagLeader;

	DECLARE_SIMPLE_DATADESC();
};


class CAI_LeadBehavior : public CAI_SimpleBehavior
{
	DECLARE_CLASS( CAI_LeadBehavior, CAI_SimpleBehavior );
public:
	CAI_LeadBehavior()
	 :	m_pSink(NULL),
		m_LostTimer( 3.0, 4.0 ),
		m_LostLOSTimer( 2.0, 3.0 )
	{
		memset( &m_args, 0, sizeof(m_args) );
		ClearGoal();
	}
	
	virtual void OnRestore();

	virtual const char *GetName() {	return "Lead"; }

	virtual int	DrawDebugTextOverlays( int text_offset );
	virtual bool IsNavigationUrgent();

	void LeadPlayer( const AI_LeadArgs_t &leadArgs, CAI_LeadBehaviorHandler *pSink = NULL );
	void StopLeading( void );

	virtual bool CanSelectSchedule();
	void BeginScheduleSelection();

	virtual bool IsCurTaskContinuousMove();

	bool SetGoal( const AI_LeadArgs_t &args );
	void ClearGoal()										{ m_goal = vec3_origin; m_waitpoint = vec3_origin; m_pSink = NULL; m_weaponname = NULL_STRING; }
	bool HasGoal() const 									{ return (m_goal != vec3_origin); }
	bool HasWaitPoint() const 								{ return (m_waitpoint != vec3_origin); }

	bool Connect( CAI_LeadBehaviorHandler *);
	bool Disconnect( CAI_LeadBehaviorHandler *);

#ifdef MAPBASE
	void SetWaitForWeapon( string_t iszWeaponName, float flTimeout = 60 ) { m_weaponname = iszWeaponName; m_flWeaponSafetyTimeOut = gpGlobals->curtime + flTimeout; }
#else
	void SetWaitForWeapon( string_t iszWeaponName ) { m_weaponname = iszWeaponName; m_flWeaponSafetyTimeOut = gpGlobals->curtime + 60; }
#endif

	enum
	{
		// Schedules
		SCHED_LEAD_PLAYER = BaseClass::NEXT_SCHEDULE,
		SCHED_LEAD_PAUSE,
		SCHED_LEAD_PAUSE_COMBAT,
		SCHED_LEAD_RETRIEVE,
		SCHED_LEAD_RETRIEVE_WAIT,
		SCHED_LEAD_SUCCEED,
		SCHED_LEAD_AWAIT_SUCCESS,
		SCHED_LEAD_WAITFORPLAYER,
		SCHED_LEAD_WAITFORPLAYERIDLE,
		SCHED_LEAD_PLAYERNEEDSWEAPON,
		SCHED_LEAD_SPEAK_START,
		SCHED_LEAD_SPEAK_THEN_RETRIEVE_PLAYER,
		SCHED_LEAD_SPEAK_THEN_LEAD_PLAYER,
		NEXT_SCHEDULE,
		
		// Tasks
		TASK_GET_PATH_TO_LEAD_GOAL = BaseClass::NEXT_TASK,
		TASK_STOP_LEADING,
		TASK_LEAD_FACE_GOAL,
		TASK_LEAD_ARRIVE,
		TASK_LEAD_SUCCEED,
		TASK_LEAD_GET_PATH_TO_WAITPOINT,
		TASK_LEAD_WAVE_TO_PLAYER,
		TASK_LEAD_PLAYER_NEEDS_WEAPON,
		TASK_LEAD_SPEAK_START,
		TASK_LEAD_MOVE_TO_RANGE,
		TASK_LEAD_RETRIEVE_WAIT,
		TASK_LEAD_WALK_PATH,
		NEXT_TASK,
		
		// Conditions
		COND_LEAD_FOLLOWER_LOST = BaseClass::NEXT_CONDITION,
		COND_LEAD_FOLLOWER_LAGGING,
		COND_LEAD_FOLLOWER_NOT_LAGGING,
		COND_LEAD_FOLLOWER_VERY_CLOSE,
		COND_LEAD_SUCCESS,
		COND_LEAD_HAVE_FOLLOWER_LOS,
		COND_LEAD_FOLLOWER_MOVED_FROM_MARK,
		COND_LEAD_FOLLOWER_MOVING_TOWARDS_ME,
		NEXT_CONDITION

	};
	
private:

	void GatherConditions();
	virtual int SelectSchedule();
	virtual int TranslateSchedule( int scheduleType );
	virtual void StartTask( const Task_t *pTask );
	virtual void RunTask( const Task_t *pTask );

	bool GetClosestPointOnRoute( const Vector &targetPos, Vector *pVecClosestPoint );
	bool PlayerIsAheadOfMe( bool bForce = false );

	bool Speak( AIConcept_t concept );
	bool IsSpeaking();

	// --------------------------------
	//
	// Sink notifiers. Isolated to limit exposure to actual sink storage,
	// provide debugging pinch pount, and allow for class-local logic
	// in addition to sink logic
	//
	void NotifyEvent( int event )								{ if ( m_pSink ) m_pSink->OnEvent( event ) ; }
	const char * GetConceptModifiers( const char *pszConcept )	{ return ( m_pSink ) ? m_pSink->GetConceptModifiers( pszConcept ) : NULL; }
	
	// --------------------------------

	AI_LeadArgs_t			m_args;
	CAI_LeadBehaviorHandler *m_pSink;
	EHANDLE					m_hSinkImplementor;
	
	// --------------------------------
	
	Vector		m_goal;
	float		m_goalyaw;
	Vector		m_waitpoint;
	float		m_waitdistance;
	float		m_leaddistance;
	float		m_retrievedistance;
	float		m_successdistance;
	string_t	m_weaponname;
	bool		m_run;
	bool		m_gagleader;
	bool		m_hasspokenstart;
	bool		m_hasspokenarrival;
	bool		m_hasPausedScenes;
	float		m_flSpeakNextNagTime;
	float		m_flWeaponSafetyTimeOut;
	float		m_flNextLeadIdle;
	bool		m_bInitialAheadTest;
	CAI_MoveMonitor m_MoveMonitor;
	
	CRandStopwatch	m_LostTimer;
	CRandStopwatch  m_LostLOSTimer;

	DEFINE_CUSTOM_SCHEDULE_PROVIDER;

	DECLARE_DATADESC();
};

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

#endif // AI_BEHAVIOR_LEAD_H
