// NextBotManager.h
// Author: Michael Booth, May 2006
//========= Copyright Valve Corporation, All rights reserved. ============//

#ifndef _NEXT_BOT_MANAGER_H_
#define _NEXT_BOT_MANAGER_H_

#include "NextBotInterface.h"

class CTerrorPlayer;

//----------------------------------------------------------------------------------------------------------------
/**
 * The NextBotManager manager 
 */
class NextBotManager
{
public:
	NextBotManager( void );
	virtual ~NextBotManager();

	void Reset( void );								// reset to initial state
	virtual void Update( void );

	bool ShouldUpdate( INextBot *bot );
	void NotifyBeginUpdate( INextBot *bot );
	void NotifyEndUpdate( INextBot *bot );

	int GetNextBotCount( void ) const;				// How many nextbots are alive right now?


	/**
	 * Populate given vector with all bots in the system
	 */
	void CollectAllBots( CUtlVector< INextBot * > *botVector );


	/**
	 * DEPRECATED: Use CollectAllBots().
	 * Execute functor for each NextBot in the system.
	 * If a functor returns false, stop iteration early
	 * and return false.
	 */	
	template < typename Functor >
	bool ForEachBot( Functor &func )
	{
		for( int i=m_botList.Head(); i != m_botList.InvalidIndex(); i = m_botList.Next( i ) )
		{
			if ( !func( m_botList[i] ) )
			{
				return false;
			}
		}

		return true;
	}

	/**
	 * DEPRECATED: Use CollectAllBots().
	 * Execute functor for each NextBot in the system as 
	 * a CBaseCombatCharacter.
	 * If a functor returns false, stop iteration early
	 * and return false.
	 */	
	template < typename Functor >
	bool ForEachCombatCharacter( Functor &func )
	{
		for( int i=m_botList.Head(); i != m_botList.InvalidIndex(); i = m_botList.Next( i ) )
		{
			if ( !func( m_botList[i]->GetEntity() ) )
			{

				return false;
			}
		}

		return true;
	}

	/**
	 * Return closest bot to given point that passes the given filter
	 */	
	template < typename Filter >
	INextBot *GetClosestBot( const Vector &pos, Filter &filter )
	{
		INextBot *close = NULL;
		float closeRangeSq = FLT_MAX;

		for( int i=m_botList.Head(); i != m_botList.InvalidIndex(); i = m_botList.Next( i ) )
		{
			float rangeSq = ( m_botList[i]->GetEntity()->GetAbsOrigin() - pos ).LengthSqr();
			if ( rangeSq < closeRangeSq && filter( m_botList[i] ) )
			{
				closeRangeSq = rangeSq;
				close = m_botList[i];
			}
		}

		return close;
	}

	/**
	 * Event propagators
	 */
	virtual void OnMapLoaded( void );						// when the server has changed maps
	virtual void OnRoundRestart( void );					// when the scenario restarts
	virtual void OnBeginChangeLevel( void );				// when the server is about to change maps
	virtual void OnKilled( CBaseCombatCharacter *victim, const CTakeDamageInfo &info );	// when an actor is killed
	virtual void OnSound( CBaseEntity *source, const Vector &pos, KeyValues *keys );				// when an entity emits a sound
	virtual void OnSpokeConcept( CBaseCombatCharacter *who, AIConcept_t concept, AI_Response *response );	// when an Actor speaks a concept
	virtual void OnWeaponFired( CBaseCombatCharacter *whoFired, CBaseCombatWeapon *weapon );		// when someone fires a weapon

	/**
	 * Debugging
	 */
	bool IsDebugging( unsigned int type ) const;	// return true if debugging system is on for the given type(s)
	void SetDebugTypes( NextBotDebugType type );	// start displaying debug info of the given type(s)

	void DebugFilterAdd( int index );				// add given entindex to the debug filter
	void DebugFilterAdd( const char *name );		// add given name to the debug filter
	void DebugFilterRemove( int index );			// remove given entindex from the debug filter
	void DebugFilterRemove( const char *name );		// remove given name from the debug filter
	void DebugFilterClear( void );					// clear the debug filter (remove all entries)
	bool IsDebugFilterMatch( const INextBot *bot ) const;	// return true if the given bot matches the debug filter

	void Select( INextBot *bot );					// mark bot as selected for further operations
	void DeselectAll( void );
	INextBot *GetSelected( void ) const;

	INextBot *GetBotUnderCrosshair( CBasePlayer *picker );	// Get the bot under the given player's crosshair

	//
	// Put these in a derived class
	//
	void OnSurvivorVomitedUpon( CTerrorPlayer *victim );	// when a Survivor has been hit by Boomer Vomit

	static void SetInstance( NextBotManager *pInstance ) { sInstance = pInstance; };
	static NextBotManager* GetInstance() { return sInstance; }

protected:
	static NextBotManager* sInstance;

	friend class INextBot;

	int Register( INextBot *bot );
	void UnRegister( INextBot *bot );

	CUtlLinkedList< INextBot * > m_botList;				// list of all active NextBots

	int m_iUpdateTickrate;
	double m_CurUpdateStartTime;
	double m_SumFrameTime;

	unsigned int m_debugType;						// debug flags

	struct DebugFilter
	{
		int index;			// entindex
		enum { MAX_DEBUG_NAME_SIZE = 128 };
		char name[ MAX_DEBUG_NAME_SIZE ];
	};
	CUtlVector< DebugFilter > m_debugFilterList;

	INextBot *m_selectedBot;						// selected bot for further debug operations
};

inline int NextBotManager::GetNextBotCount( void ) const
{
	return m_botList.Count();
}

inline bool NextBotManager::IsDebugging( unsigned int type ) const
{
	if ( type & m_debugType )
	{
		return true;
	}

	return false;
}


inline void NextBotManager::SetDebugTypes( NextBotDebugType type )
{
	m_debugType = (unsigned int)type;
}


inline void NextBotManager::Select( INextBot *bot )
{
	m_selectedBot = bot;
}

inline void NextBotManager::DeselectAll( void )
{
	m_selectedBot = NULL;
}

inline INextBot *NextBotManager::GetSelected( void ) const
{
	return m_selectedBot;
}



// singleton accessor
extern NextBotManager &TheNextBots( void );


#endif // _NEXT_BOT_MANAGER_H_