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

#ifndef TALKNPC_H
#define TALKNPC_H

#ifdef POSIX
#undef time
#include <time.h>
#endif

#ifndef _XBOX
#undef min
#undef max
#pragma warning(push)
#include <set>
#pragma warning(pop)
#endif

#ifdef _WIN32
#pragma once
#endif

// the include <set> monkey's with the MAX() define, unbreak it
#undef MINMAX_H
#include "minmax.h"

#include "ai_playerally.h"

#include "soundflags.h"

#include "ai_task.h"
#include "ai_schedule.h"
#include "ai_default.h"
#include "ai_speech.h"
#include "ai_basenpc.h"
#include "ai_behavior.h"
#include "ai_behavior_follow.h"

#include "tier0/memdbgon.h"

//=========================================================
// Talking NPC base class
// Used for scientists and barneys
//=========================================================

#define TLK_CFRIENDS		4

//=============================================================================
// >> CNPCSimpleTalker
//=============================================================================

#define MONOLOGNAME_LEN	16				// sentence names passed as monolog may be no longer than this.
#define AI_SP_START_MONOLOG 	'~'
#define AI_SP_MONOLOG_LINE  	'@'

class CNPCSimpleTalker;

class CNPCSimpleTalkerExpresser : public CAI_ComponentWithOuter<CNPCSimpleTalker, CAI_Expresser>
{
public:
	CNPCSimpleTalkerExpresser( CNPCSimpleTalker *pOuter )
		: CAI_ComponentWithOuter<CNPCSimpleTalker, CAI_Expresser>( pOuter )
	{
		EndMonolog();
	}

	virtual int SpeakRawSentence( const char *pszSentence, float delay, float volume = VOL_NORM, soundlevel_t soundlevel = SNDLVL_TALKING, CBaseEntity *pListener = NULL );

	// --------------------------------
	//
	// Monologue operations
	//
	
	bool HasMonolog( void ) { return m_iMonologIndex != -1; };
	void BeginMonolog( char *pszSentenceName, CBaseEntity *pListener );
	void EndMonolog( void );
	void SpeakMonolog( void );

	void SuspendMonolog( float flInterval );
	void ResumeMonolog( void );

	CBaseEntity *GetMonologueTarget()						{ return m_hMonologTalkTarget.Get(); }

	// --------------------------------
	//
	// Monologue data
	//
	char		m_szMonologSentence[MONOLOGNAME_LEN];	// The name of the sentence group for the monolog I'm speaking.
	int			m_iMonologIndex;						// Which sentence from the group I should be speaking.
	bool		m_fMonologSuspended;
	EHANDLE		m_hMonologTalkTarget;					// Who I'm trying to deliver my monolog to. 

	DECLARE_SIMPLE_DATADESC();
};

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

class CNPCSimpleTalker : public CAI_PlayerAlly
{
	DECLARE_CLASS( CNPCSimpleTalker, CAI_PlayerAlly );
public:
	void			Precache( void );
	virtual bool	KeyValue( const char *szKeyName, const char *szValue );

	virtual CAI_Expresser *CreateExpresser() { return new CNPCSimpleTalkerExpresser(this); }
	
	virtual void			StartFollowing( CBaseEntity *pLeader ) { m_FollowBehavior.SetFollowTarget( pLeader ); DeferSchedulingToBehavior( &m_FollowBehavior ); }
	virtual void			StopFollowing( ) { m_FollowBehavior.SetFollowTarget( NULL ); DeferSchedulingToBehavior( NULL ); }
	CBaseEntity		*GetFollowTarget( void ) { return m_FollowBehavior.GetFollowTarget(); }

	virtual void	OnChangeRunningBehavior( CAI_BehaviorBase *pOldBehavior,  CAI_BehaviorBase *pNewBehavior );
	bool			OnBehaviorChangeStatus( CAI_BehaviorBase *pBehavior, bool fCanFinishSchedule );

	int				PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener );
	virtual void 	FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
	void			Event_Killed( const CTakeDamageInfo &info );
	int				OnTakeDamage_Alive( const CTakeDamageInfo &info );

	bool CreateBehaviors()
	{
		AddBehavior( &m_FollowBehavior );
		return BaseClass::CreateBehaviors();
	}

	void		BuildScheduleTestBits( void );
	void			PrescheduleThink( void );
	virtual int		SelectSchedule( void );
	virtual int		SelectNonCombatSpeechSchedule();
	void			StartTask( const Task_t *pTask );
	void			RunTask( const Task_t *pTask );
	void			HandleAnimEvent( animevent_t *pEvent );
	Activity	NPC_TranslateActivity( Activity newActivity );

	virtual void	OnStartingFollow( CBaseEntity *pTarget );
	virtual void	OnStoppingFollow( CBaseEntity *pTarget );

	virtual void			DeferAllIdleSpeech( float flDelay, CAI_BaseNPC *pIgnore = NULL );
	bool		ShouldSpeakRandom( int iChance, float flModifier );

	// For following
	virtual void	DeclineFollowing( void ) {}
	void			LimitFollowers( CBaseEntity *pPlayer, int maxFollowers );

	float			GetUseTime() const { return m_useTime; }

	//=========================================================
	// TalkNPC schedules
	//=========================================================
	enum
	{
		SCHED_TALKER_IDLE_RESPONSE = BaseClass::NEXT_SCHEDULE,
		SCHED_TALKER_IDLE_SPEAK,
		SCHED_TALKER_IDLE_HELLO,
		SCHED_TALKER_IDLE_STOP_SHOOTING,
		SCHED_TALKER_IDLE_WATCH_CLIENT,
		SCHED_TALKER_IDLE_WATCH_CLIENT_STARE,
		SCHED_TALKER_IDLE_EYE_CONTACT,
		SCHED_TALKER_BETRAYED,

		// !ALWAYS LAST!
		NEXT_SCHEDULE,	
	};

	//=========================================================
	// TalkNPC tasks
	//=========================================================
	enum
	{
		TASK_TALKER_RESPOND = BaseClass::NEXT_TASK,		// say my response
		TASK_TALKER_SPEAK,			// question or remark
		TASK_TALKER_HELLO,			// Try to say hello to player
		TASK_TALKER_BETRAYED,		// Player killed an ally
		TASK_TALKER_HEADRESET,		// reset head position
		TASK_TALKER_STOPSHOOTING,	// tell player to stop shooting friend
		TASK_TALKER_STARE,			// let the player know I know he's staring at me.
		TASK_TALKER_LOOK_AT_CLIENT,// faces player if not moving and not talking and in idle.
		TASK_TALKER_CLIENT_STARE,	// same as look at client, but says something if the player stares.
		TASK_TALKER_EYECONTACT,	// maintain eyecontact with person who I'm talking to
		TASK_TALKER_IDEALYAW,		// set ideal yaw to face who I'm talking to
		TASK_FIND_LOCK_HINTNODE_HEALTH, // Find & lock a nearby healthkit hintnode to heal myself at
		TASK_TALKER_WAIT_FOR_SEMAPHORE,

		// !ALWAYS LAST!
		NEXT_TASK,		
	};

//private:
	virtual bool IsValidSpeechTarget( int flags, CBaseEntity *pEntity );
	
	CBaseEntity		*FindNearestFriend(bool fPlayer);

	bool IsOkToSpeak( void );

	void SayHelloToPlayer( CBaseEntity *pPlayer );
	virtual bool CanSayHello( void );
	virtual int	 FIdleHello( void );

	// Inputs
	void InputIdleRespond( inputdata_t &inputdata );

	// Conversations / communication
	void			IdleRespond( void );
	int				FIdleSpeak( void );
	void			FIdleSpeakWhileMoving( void );
	int				FIdleStare( void );
	bool			SpeakQuestionFriend( CBaseEntity *pFriend );
	bool			SpeakAnswerFriend( CBaseEntity *pFriend );
	void			TrySmellTalk( void );

	virtual void	SetAnswerQuestion( CNPCSimpleTalker *pSpeaker );
	
	bool ShouldSuspendMonolog( void );
	bool ShouldResumeMonolog( void );
	void OnResumeMonolog() 		{	Speak( TLK_RESUME ); }
	
	int			m_nSpeak;						// number of times initiated talking
	float		m_flNextIdleSpeechTime;

	static char *m_szFriends[TLK_CFRIENDS];		// array of friend names
	CBaseEntity		*EnumFriends( CBaseEntity *pentPrevious, int listNumber, bool bTrace );

	virtual int		FriendNumber( int arrayNumber )	{ return arrayNumber; }
	void			ShutUpFriends( void );
	void			AlertFriends( CBaseEntity *pKiller );

	string_t	m_iszUse;						// Custom +USE sentence group (follow)
	string_t	m_iszUnUse;						// Custom +USE sentence group (stop following)

protected:
	CAI_FollowBehavior m_FollowBehavior;
	float		m_useTime;						// Don't allow +USE until this time

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

	DECLARE_DATADESC();
#ifndef _XBOX
	DEFINE_CUSTOM_AI;
#else
public:
	DEFINE_CUSTOM_AI;
private:
#endif
};

#include "tier0/memdbgoff.h"

#endif		//TALKNPC_H