//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
// nav_mesh.h
// The Navigation Mesh interface
// Author: Michael S. Booth (mike@turtlerockstudios.com), January 2003

//
// Author: Michael S. Booth (mike@turtlerockstudios.com), 2003
//
// NOTE: The Navigation code uses Doxygen-style comments. If you run Doxygen over this code, it will 
// auto-generate documentation.  Visit www.doxygen.org to download the system for free.
//

#ifndef _NAV_MESH_H_
#define _NAV_MESH_H_

#include "utlbuffer.h"
#include "filesystem.h"
#include "GameEventListener.h"

#include "nav.h"
#include "nav_area.h"
#include "nav_colors.h"


class CNavArea;
class CBaseEntity; 
class CBreakable;

extern ConVar nav_edit;
extern ConVar nav_quicksave;
extern ConVar nav_show_approach_points;
extern ConVar nav_show_danger;

//--------------------------------------------------------------------------------------------------------
class NavAreaCollector
{
	bool m_checkForDuplicates;
public:
	NavAreaCollector( bool checkForDuplicates = false )
	{
		m_checkForDuplicates = checkForDuplicates;
	}

	bool operator() ( CNavArea *area )
	{
		if ( m_checkForDuplicates && m_area.HasElement( area ) )
			return true;

		m_area.AddToTail( area );
		return true;
	}
	CUtlVector< CNavArea * > m_area;
};


//--------------------------------------------------------------------------------------------------------
class EditDestroyNotification
{
	CNavArea *m_deadArea;

public:
	EditDestroyNotification( CNavArea *deadArea )
	{
		m_deadArea = deadArea;
	}

	bool operator()( CBaseCombatCharacter *actor )
	{
		actor->OnNavAreaRemoved( m_deadArea );
		return true;
	}
};


//--------------------------------------------------------------------------------------------------------
class NavAttributeClearer
{
public:
	NavAttributeClearer( NavAttributeType attribute )
	{
		m_attribute = attribute;
	}

	bool operator() ( CNavArea *area )
	{
		area->SetAttributes( area->GetAttributes() & (~m_attribute) );

		return true;
	}

	NavAttributeType m_attribute;
};


//--------------------------------------------------------------------------------------------------------
class NavAttributeSetter
{
public:
	NavAttributeSetter( NavAttributeType attribute )
	{
		m_attribute = attribute;
	}

	bool operator() ( CNavArea *area )
	{
		area->SetAttributes( area->GetAttributes() | m_attribute );

		return true;
	}

	NavAttributeType m_attribute;
};


//--------------------------------------------------------------------------------------------------------
class NavAttributeToggler
{
public:
	NavAttributeToggler( NavAttributeType attribute )
	{
		m_attribute = attribute;
	}

	bool operator() ( CNavArea *area );

	NavAttributeType m_attribute;
};


//--------------------------------------------------------------------------------------------------------
struct NavAttributeLookup
{
	const char *name;
	NavAttributeType attribute;	
};

extern NavAttributeLookup TheNavAttributeTable[];

//--------------------------------------------------------------------------------------------------------
class SelectOverlappingAreas
{
public:
	bool operator()( CNavArea *area );
};

//--------------------------------------------------------------------------------------------------------
abstract_class INavAvoidanceObstacle
{
public:
	virtual bool IsPotentiallyAbleToObstructNavAreas( void ) const = 0;	// could we at some future time obstruct nav?
	virtual float GetNavObstructionHeight( void ) const = 0;			// height at which to obstruct nav areas
	virtual bool CanObstructNavAreas( void ) const = 0;					// can we obstruct nav right this instant?
	virtual CBaseEntity *GetObstructingEntity( void ) = 0;
	virtual void OnNavMeshLoaded( void ) = 0;
};

//--------------------------------------------------------------------------------------------------------
enum GetNavAreaFlags_t
{
	GETNAVAREA_CHECK_LOS			= 0x1,
	GETNAVAREA_ALLOW_BLOCKED_AREAS	= 0x2,
	GETNAVAREA_CHECK_GROUND			= 0x4,
};


//--------------------------------------------------------------------------------------------------------
// for nav mesh visibilty computation
struct NavVisPair_t
{
	void SetPair( CNavArea *pArea1, CNavArea *pArea2 )
	{
		int iArea1 = (int)( pArea1 > pArea2 );
		int iArea2 = ( iArea1 + 1 ) % 2;
		pAreas[iArea1] = pArea1;
		pAreas[iArea2] = pArea2;
	}

	CNavArea *pAreas[2];
};


// for nav mesh visibilty computation
class CVisPairHashFuncs
{
public:
	CVisPairHashFuncs( int ) {}

	bool operator()( const NavVisPair_t &lhs, const NavVisPair_t &rhs ) const
	{
		return ( lhs.pAreas[0] == rhs.pAreas[0] && lhs.pAreas[1] == rhs.pAreas[1] );
	}

	unsigned int operator()( const NavVisPair_t &item ) const
	{
		COMPILE_TIME_ASSERT( sizeof(CNavArea *) == 4 );
		int key[2] = { (int)item.pAreas[0] + item.pAreas[1]->GetID(), (int)item.pAreas[1] + item.pAreas[0]->GetID() };
		return Hash8( key );	
	}
};


//--------------------------------------------------------------------------------------------------------------
//
// The 'place directory' is used to save and load places from
// nav files in a size-efficient manner that also allows for the 
// order of the place ID's to change without invalidating the
// nav files.
//
// The place directory is stored in the nav file as a list of 
// place name strings.  Each nav area then contains an index
// into that directory, or zero if no place has been assigned to 
// that area.
//
class PlaceDirectory
{
public:
	typedef unsigned short IndexType;	// Loaded/Saved as UnsignedShort.  Change this and you'll have to version.

	PlaceDirectory( void );
	void Reset( void );
	bool IsKnown( Place place ) const;						/// return true if this place is already in the directory
	IndexType GetIndex( Place place ) const;				/// return the directory index corresponding to this Place (0 = no entry)
	void AddPlace( Place place );							/// add the place to the directory if not already known
	Place IndexToPlace( IndexType entry ) const;			/// given an index, return the Place
	void Save( CUtlBuffer &fileBuffer );					/// store the directory
	void Load( CUtlBuffer &fileBuffer, int version );		/// load the directory
	const CUtlVector< Place > *GetPlaces( void ) const
	{
		return &m_directory;
	}

	bool HasUnnamedPlaces( void ) const 
	{
		return m_hasUnnamedAreas;
	}


private:
	CUtlVector< Place > m_directory;
	bool m_hasUnnamedAreas;
};

extern PlaceDirectory placeDirectory;



//--------------------------------------------------------------------------------------------------------
/**
 * The CNavMesh is the global interface to the Navigation Mesh.
 * @todo Make this an abstract base class interface, and derive mod-specific implementations.
 */
class CNavMesh : public CGameEventListener
{
public:
	CNavMesh( void );
	virtual ~CNavMesh();
	
	virtual void PreLoadAreas( int nAreas ) {}
	virtual CNavArea *CreateArea( void ) const;							// CNavArea factory
	virtual void DestroyArea( CNavArea * ) const;
	virtual HidingSpot *CreateHidingSpot( void ) const;					// Hiding Spot factory

	virtual void Reset( void );											// destroy Navigation Mesh data and revert to initial state
	virtual void Update( void );										// invoked on each game frame

	virtual void FireGameEvent( IGameEvent *event );					// incoming event processing

	virtual NavErrorType Load( void );									// load navigation data from a file
	virtual NavErrorType PostLoad( unsigned int version );				// (EXTEND) invoked after all areas have been loaded - for pointer binding, etc
	bool IsLoaded( void ) const		{ return m_isLoaded; }				// return true if a Navigation Mesh has been loaded
	bool IsAnalyzed( void ) const	{ return m_isAnalyzed; }			// return true if a Navigation Mesh has been analyzed

	/**
	 * Return true if nav mesh can be trusted for all climbing/jumping decisions because game environment is fairly simple.
	 * Authoritative meshes mean path followers can skip CPU intensive realtime scanning of unpredictable geometry.
	 */
	virtual bool IsAuthoritative( void ) const { return false; }		

	const CUtlVector< Place > *GetPlacesFromNavFile( bool *hasUnnamedPlaces );	// Reads the used place names from the nav file (can be used to selectively precache before the nav is loaded)

	virtual bool Save( void ) const;									// store Navigation Mesh to a file
	bool IsOutOfDate( void ) const	{ return m_isOutOfDate; }			// return true if the Navigation Mesh is older than the current map version

	virtual unsigned int GetSubVersionNumber( void ) const;										// returns sub-version number of data format used by derived classes
	virtual void SaveCustomData( CUtlBuffer &fileBuffer ) const { }								// store custom mesh data for derived classes
	virtual void LoadCustomData( CUtlBuffer &fileBuffer, unsigned int subVersion ) { }			// load custom mesh data for derived classes
	virtual void SaveCustomDataPreArea( CUtlBuffer &fileBuffer ) const { }						// store custom mesh data for derived classes that needs to be loaded before areas are read in
	virtual void LoadCustomDataPreArea( CUtlBuffer &fileBuffer, unsigned int subVersion ) { }	// load custom mesh data for derived classes that needs to be loaded before areas are read in

	// events
	virtual void OnServerActivate( void );								// (EXTEND) invoked when server loads a new map
	virtual void OnRoundRestart( void );								// invoked when a game round restarts
	virtual void OnRoundRestartPreEntity( void );						// invoked when a game round restarts, but before entities are deleted and recreated
	virtual void OnBreakableCreated( CBaseEntity *breakable ) { }		// invoked when a breakable is created
	virtual void OnBreakableBroken( CBaseEntity *broken ) { }			// invoked when a breakable is broken
	virtual void OnAreaBlocked( CNavArea *area );						// invoked when the area becomes blocked
	virtual void OnAreaUnblocked( CNavArea *area );						// invoked when the area becomes un-blocked
	virtual void OnAvoidanceObstacleEnteredArea( CNavArea *area );					// invoked when the area becomes obstructed
	virtual void OnAvoidanceObstacleLeftArea( CNavArea *area );					// invoked when the area becomes un-obstructed

	virtual void OnEditCreateNotify( CNavArea *newArea );				// invoked when given area has just been added to the mesh in edit mode
	virtual void OnEditDestroyNotify( CNavArea *deadArea );				// invoked when given area has just been deleted from the mesh in edit mode
	virtual void OnEditDestroyNotify( CNavLadder *deadLadder );			// invoked when given ladder has just been deleted from the mesh in edit mode
	virtual void OnNodeAdded( CNavNode *node ) {};						

	// Obstructions
	void RegisterAvoidanceObstacle( INavAvoidanceObstacle *obstruction );
	void UnregisterAvoidanceObstacle( INavAvoidanceObstacle *obstruction );
	const CUtlVector< INavAvoidanceObstacle * > &GetObstructions( void ) const { return m_avoidanceObstacles; }

	unsigned int GetNavAreaCount( void ) const	{ return m_areaCount; }	// return total number of nav areas

	// See GetNavAreaFlags_t for flags
	CNavArea *GetNavArea( const Vector &pos, float beneathLimt = 120.0f ) const;	// given a position, return the nav area that IsOverlapping and is *immediately* beneath it
	CNavArea *GetNavArea( CBaseEntity *pEntity, int nGetNavAreaFlags, float flBeneathLimit = 120.0f ) const;
	CNavArea *GetNavAreaByID( unsigned int id ) const;
	CNavArea *GetNearestNavArea( const Vector &pos, bool anyZ = false, float maxDist = 10000.0f, bool checkLOS = false, bool checkGround = true, int team = TEAM_ANY ) const;
	CNavArea *GetNearestNavArea( CBaseEntity *pEntity, int nGetNavAreaFlags = GETNAVAREA_CHECK_GROUND, float maxDist = 10000.0f ) const;

	Place GetPlace( const Vector &pos ) const;							// return Place at given coordinate
	const char *PlaceToName( Place place ) const;						// given a place, return its name
	Place NameToPlace( const char *name ) const;						// given a place name, return a place ID or zero if no place is defined
	Place PartialNameToPlace( const char *name ) const;					// given the first part of a place name, return a place ID or zero if no place is defined, or the partial match is ambiguous
	void PrintAllPlaces( void ) const;									// output a list of names to the console
	int PlaceNameAutocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] );	// Given a partial place name, fill in possible place names for ConCommand autocomplete

	bool GetGroundHeight( const Vector &pos, float *height, Vector *normal = NULL ) const;		// get the Z coordinate of the topmost ground level below the given point
	bool GetSimpleGroundHeight( const Vector &pos, float *height, Vector *normal = NULL ) const;// get the Z coordinate of the ground level directly below the given point


	/// increase "danger" weights in the given nav area and nearby ones
	void IncreaseDangerNearby( int teamID, float amount, CNavArea *area, const Vector &pos, float maxRadius, float dangerLimit = -1.0f );
	void DrawDanger( void ) const;										// draw the current danger levels
	void DrawPlayerCounts( void ) const;								// draw the current player counts for each area
	void DrawFuncNavAvoid( void ) const;								// draw bot avoidance areas from func_nav_avoid entities
	void DrawFuncNavPrefer( void ) const;								// draw bot preference areas from func_nav_prefer entities
#ifdef NEXT_BOT
	void DrawFuncNavPrerequisite( void ) const;							// draw bot prerequisite areas from func_nav_prerequisite entities
#endif
	//-------------------------------------------------------------------------------------
	// Auto-generation
	//
	#define INCREMENTAL_GENERATION true
	void BeginGeneration( bool incremental = false );					// initiate the generation process
	void BeginAnalysis( bool quitWhenFinished = false );						// re-analyze an existing Mesh.  Determine Hiding Spots, Encounter Spots, etc.

	bool IsGenerating( void ) const		{ return m_generationMode != GENERATE_NONE; }	// return true while a Navigation Mesh is being generated
	const char *GetPlayerSpawnName( void ) const;						// return name of player spawn entity
	void SetPlayerSpawnName( const char *name );						// define the name of player spawn entities
	void AddWalkableSeed( const Vector &pos, const Vector &normal );	// add given walkable position to list of seed positions for map sampling
	virtual void AddWalkableSeeds( void );								// adds walkable positions for any/all positions a mod specifies
	void ClearWalkableSeeds( void )		{ m_walkableSeeds.RemoveAll(); }	// erase all walkable seed positions
	void MarkStairAreas( void );

	virtual unsigned int GetGenerationTraceMask( void ) const;			// return the mask used by traces when generating the mesh


	//-------------------------------------------------------------------------------------
	// Edit mode
	//
	unsigned int GetNavPlace( void ) const			{ return m_navPlace; }
	void SetNavPlace( unsigned int place )			{ m_navPlace = place; }

	// Edit callbacks from ConCommands
	void CommandNavDelete( void );										// delete current area
	void CommandNavDeleteMarked( void );								// delete current marked area

	virtual void CommandNavFloodSelect( const CCommand &args );			// select current area and all connected areas, recursively
	void CommandNavToggleSelectedSet( void );							// toggles all areas into/out of the selected set
	void CommandNavStoreSelectedSet( void );							// stores the current selected set for later
	void CommandNavRecallSelectedSet( void );							// restores an older selected set
	void CommandNavAddToSelectedSet( void );							// add current area to selected set
	void CommandNavAddToSelectedSetByID(  const CCommand &args );		// add specified area id to selected set
	void CommandNavRemoveFromSelectedSet( void );						// remove current area from selected set
	void CommandNavToggleInSelectedSet( void );							// add/remove current area from selected set
	void CommandNavClearSelectedSet( void );							// clear the selected set to empty
	void CommandNavBeginSelecting( void );								// start continuously selecting areas into the selected set
	void CommandNavEndSelecting( void );								// stop continuously selecting areas into the selected set
	void CommandNavBeginDragSelecting( void );							// start dragging a selection area
	void CommandNavEndDragSelecting( void );							// stop dragging a selection area
	void CommandNavBeginDragDeselecting( void );						// start dragging a deselection area
	void CommandNavEndDragDeselecting( void );							// stop dragging a deselection area
	void CommandNavRaiseDragVolumeMax( void );							// raise the top of the drag volume
	void CommandNavLowerDragVolumeMax( void );							// lower the top of the drag volume
	void CommandNavRaiseDragVolumeMin( void );							// raise the bottom of the drag volume
	void CommandNavLowerDragVolumeMin( void );							// lower the bottom of the drag volume
	void CommandNavToggleSelecting( bool playSound = true );			// start/stop continuously selecting areas into the selected set
	void CommandNavBeginDeselecting( void );							// start continuously de-selecting areas from the selected set
	void CommandNavEndDeselecting( void );								// stop continuously de-selecting areas from the selected set
	void CommandNavToggleDeselecting( bool playSound = true );			// start/stop continuously de-selecting areas from the selected set
	void CommandNavSelectInvalidAreas( void );							// adds invalid areas to the selected set
	void CommandNavSelectBlockedAreas( void );							// adds blocked areas to the selected set
	void CommandNavSelectObstructedAreas( void );						// adds obstructed areas to the selected set
	void CommandNavSelectDamagingAreas( void );							// adds damaging areas to the selected set
	void CommandNavSelectHalfSpace( const CCommand &args );				// selects all areas that intersect the half-space
	void CommandNavSelectStairs( void );								// adds stairs areas to the selected set
	void CommandNavSelectOrphans( void );								// adds areas not connected to mesh to the selected set

	void CommandNavSplit( void );										// split current area
	void CommandNavMerge( void );										// merge adjacent areas
	void CommandNavMark( const CCommand &args );						// mark an area for further operations
	void CommandNavUnmark( void );										// removes the mark

	void CommandNavBeginArea( void );									// begin creating a new nav area
	void CommandNavEndArea( void );										// end creation of the new nav area

	void CommandNavBeginShiftXY( void );								// begin shifting selected set in the XY plane
	void CommandNavEndShiftXY( void );									// end shifting selected set in the XY plane

	void CommandNavConnect( void );										// connect marked area to selected area
	void CommandNavDisconnect( void );									// disconnect marked area from selected area
	void CommandNavDisconnectOutgoingOneWays( void );					// disconnect all outgoing one-way connects from each area in the selected set
	void CommandNavSplice( void );										// create new area in between marked and selected areas
	void CommandNavCrouch( void );										// toggle crouch attribute on current area
	void CommandNavTogglePlaceMode( void );								// switch between normal and place editing
	void CommandNavSetPlaceMode( void );								// switch between normal and place editing
	void CommandNavPlaceFloodFill( void );								// floodfill areas out from current area
	void CommandNavPlaceSet( void );									// sets the Place for the selected set
	void CommandNavPlacePick( void );									// "pick up" the place at the current area
	void CommandNavTogglePlacePainting( void );							// switch between "painting" places onto areas
	void CommandNavMarkUnnamed( void );									// mark an unnamed area for further operations
	void CommandNavCornerSelect( void );								// select a corner on the current area
	void CommandNavCornerRaise( const CCommand &args );					// raise a corner on the current area
	void CommandNavCornerLower( const CCommand &args );					// lower a corner on the current area
	void CommandNavCornerPlaceOnGround( const CCommand &args );			// position a corner on the current area at ground height
	void CommandNavWarpToMark( void );									// warp a spectating local player to the selected mark
	void CommandNavLadderFlip( void );									// Flips the direction a ladder faces
	void CommandNavToggleAttribute( NavAttributeType attribute );		// toggle an attribute on current area
	void CommandNavMakeSniperSpots( void );								// cuts up the marked area into individual areas suitable for sniper spots
	void CommandNavBuildLadder( void );									// builds a nav ladder on the climbable surface under the cursor
	void CommandNavRemoveJumpAreas( void );								// removes jump areas, replacing them with connections
	void CommandNavSubdivide( const CCommand &args );					// subdivide each nav area in X and Y to create 4 new areas - limit min size
	void CommandNavSaveSelected( const CCommand &args );				// Save selected set to disk
	void CommandNavMergeMesh( const CCommand &args );					// Merge a saved selected set into the current mesh
	void CommandNavMarkWalkable( void );

	void AddToDragSelectionSet( CNavArea *pArea );
	void RemoveFromDragSelectionSet( CNavArea *pArea );
	void ClearDragSelectionSet( void );

	CNavArea *GetMarkedArea( void ) const;										// return area marked by user in edit mode
	CNavLadder *GetMarkedLadder( void ) const	{ return m_markedLadder; }		// return ladder marked by user in edit mode

	CNavArea *GetSelectedArea( void ) const		{ return m_selectedArea; }		// return area user is pointing at in edit mode
	CNavLadder *GetSelectedLadder( void ) const	{ return m_selectedLadder; }	// return ladder user is pointing at in edit mode
	void SetMarkedLadder( CNavLadder *ladder );							// mark ladder for further edit operations
	void SetMarkedArea( CNavArea *area );								// mark area for further edit operations

	bool IsContinuouslySelecting( void ) const
	{
		return m_isContinuouslySelecting;
	}

	bool IsContinuouslyDeselecting( void ) const
	{
		return m_isContinuouslyDeselecting;
	}

	void CreateLadder( const Vector &mins, const Vector &maxs, float maxHeightAboveTopArea );
	void CreateLadder( const Vector &top, const Vector &bottom, float width, const Vector2D &ladderDir, float maxHeightAboveTopArea );

	float SnapToGrid( float x, bool forceGrid = false ) const;					// snap given coordinate to generation grid boundary
	Vector SnapToGrid( const Vector& in, bool snapX = true, bool snapY = true, bool forceGrid = false ) const;	// snap given vector's X & Y coordinates to generation grid boundary

	const Vector &GetEditCursorPosition( void ) const	{ return m_editCursorPos; }	// return position of edit cursor
	void StripNavigationAreas( void );
	const char *GetFilename( void ) const;								// return the filename for this map's "nav" file

	/// @todo Remove old select code and make all commands use this selected set
	void AddToSelectedSet( CNavArea *area );							// add area to the currently selected set
	void RemoveFromSelectedSet( CNavArea *area );						// remove area from the currently selected set
	void ClearSelectedSet( void );										// clear the currently selected set to empty
	bool IsSelectedSetEmpty( void ) const;								// return true if the selected set is empty
	bool IsInSelectedSet( const CNavArea *area ) const;					// return true if the given area is in the selected set
	int GetSelecteSetSize( void ) const;
	const NavAreaVector &GetSelectedSet( void ) const;					// return the selected set

	/**
	 * Apply the functor to all navigation areas in the Selected Set,
	 * or the current selected area.
	 * If functor returns false, stop processing and return false.
	 */
	template < typename Functor >
	bool ForAllSelectedAreas( Functor &func )
	{
		if (IsSelectedSetEmpty())
		{
			CNavArea *area = GetSelectedArea();
			
			if (area)
			{
				if (func( area ) == false)
					return false;
			}
		}
		else
		{
			FOR_EACH_VEC( m_selectedSet, it )
			{
				CNavArea *area = m_selectedSet[ it ];

				if (func( area ) == false)
					return false;
			}
		}
		
		return true;
	}

	//-------------------------------------------------------------------------------------
	/**
	 * Apply the functor to all navigation areas.
	 * If functor returns false, stop processing and return false.
	 */
	template < typename Functor >
	bool ForAllAreas( Functor &func )
	{
		FOR_EACH_VEC( TheNavAreas, it )
		{
			CNavArea *area = TheNavAreas[ it ];

			if (func( area ) == false)
				return false;
		}

		return true;
	}

	// const version of the above
	template < typename Functor >
	bool ForAllAreas( Functor &func ) const
	{
		FOR_EACH_VEC( TheNavAreas, it )
		{
			const CNavArea *area = TheNavAreas[ it ];

			if (func( area ) == false)
				return false;
		}

		return true;
	}

	//-------------------------------------------------------------------------------------
	/**
	 * Apply the functor to all navigation areas that overlap the given extent.
	 * If functor returns false, stop processing and return false.
	 */
	template < typename Functor >
	bool ForAllAreasOverlappingExtent( Functor &func, const Extent &extent )
	{
		if ( !m_grid.Count() )
		{
#if _DEBUG
			Warning("Query before nav mesh is loaded! %d\n", TheNavAreas.Count() );
#endif
			return true;
		}
		static unsigned int searchMarker = RandomInt(0, 1024*1024 );
		if ( ++searchMarker == 0 )
		{
			++searchMarker;
		}

		Extent areaExtent;

		// get list in cell that contains position
		int startX = WorldToGridX( extent.lo.x );
		int endX = WorldToGridX( extent.hi.x );
		int startY = WorldToGridY( extent.lo.y );
		int endY = WorldToGridY( extent.hi.y );

		for( int x = startX; x <= endX; ++x )
		{
			for( int y = startY; y <= endY; ++y )
			{
				int iGrid = x + y*m_gridSizeX;
				if ( iGrid >= m_grid.Count() )
				{
					ExecuteNTimes( 10, Warning( "** Walked off of the CNavMesh::m_grid in ForAllAreasOverlappingExtent()\n" ) );
					return true;
				}

				NavAreaVector *areaVector = &m_grid[ iGrid ];

				// find closest area in this cell
				FOR_EACH_VEC( (*areaVector), it )
				{
					CNavArea *area = (*areaVector)[ it ];

					// skip if we've already visited this area
					if ( area->m_nearNavSearchMarker == searchMarker )
						continue;

					// mark as visited
					area->m_nearNavSearchMarker = searchMarker;
					area->GetExtent( &areaExtent );

					if ( extent.IsOverlapping( areaExtent ) )
					{
						if ( func( area ) == false )
							return false;
					}
				}
			}
		}
		return true;
	}

	//-------------------------------------------------------------------------------------
	/**
	 * Populate the given vector with all navigation areas that overlap the given extent.
	 */
	template< typename NavAreaType >
	void CollectAreasOverlappingExtent( const Extent &extent, CUtlVector< NavAreaType * > *outVector )
	{
		if ( !m_grid.Count() )
		{
			return;
		}

		static unsigned int searchMarker = RandomInt( 0, 1024*1024 );
		if ( ++searchMarker == 0 )
		{
			++searchMarker;
		}

		Extent areaExtent;

		// get list in cell that contains position
		int startX = WorldToGridX( extent.lo.x );
		int endX = WorldToGridX( extent.hi.x );
		int startY = WorldToGridY( extent.lo.y );
		int endY = WorldToGridY( extent.hi.y );

		for( int x = startX; x <= endX; ++x )
		{
			for( int y = startY; y <= endY; ++y )
			{
				int iGrid = x + y*m_gridSizeX;
				if ( iGrid >= m_grid.Count() )
				{
					ExecuteNTimes( 10, Warning( "** Walked off of the CNavMesh::m_grid in CollectAreasOverlappingExtent()\n" ) );
					return;
				}

				NavAreaVector *areaVector = &m_grid[ iGrid ];

				// find closest area in this cell
				for( int v=0; v<areaVector->Count(); ++v )
				{
					CNavArea *area = areaVector->Element( v );

					// skip if we've already visited this area
					if ( area->m_nearNavSearchMarker == searchMarker )
						continue;

					// mark as visited
					area->m_nearNavSearchMarker = searchMarker;
					area->GetExtent( &areaExtent );

					if ( extent.IsOverlapping( areaExtent ) )
					{
						outVector->AddToTail( (NavAreaType *)area );
					}
				}
			}
		}
	}


	template < typename Functor >
	bool ForAllAreasInRadius( Functor &func, const Vector &pos, float radius )
	{
		// use a unique marker for this method, so it can be used within a SearchSurroundingArea() call
		static unsigned int searchMarker = RandomInt(0, 1024*1024 );

		++searchMarker;

		if ( searchMarker == 0 )
		{
			++searchMarker;
		}


		// get list in cell that contains position
		int originX = WorldToGridX( pos.x );
		int originY = WorldToGridY( pos.y );
		int shiftLimit = ceil( radius / m_gridCellSize );
		float radiusSq = radius * radius;
		if ( radius == 0.0f )
		{
			shiftLimit = MAX( m_gridSizeX, m_gridSizeY );	// range 0 means all areas
		}

		for( int x = originX - shiftLimit; x <= originX + shiftLimit; ++x )
		{
			if ( x < 0 || x >= m_gridSizeX )
				continue;

			for( int y = originY - shiftLimit; y <= originY + shiftLimit; ++y )
			{
				if ( y < 0 || y >= m_gridSizeY )
					continue;

				NavAreaVector *areaVector = &m_grid[ x + y*m_gridSizeX ];

				// find closest area in this cell
				FOR_EACH_VEC( (*areaVector), it )
				{
					CNavArea *area = (*areaVector)[ it ];

					// skip if we've already visited this area
					if ( area->m_nearNavSearchMarker == searchMarker )
						continue;

					// mark as visited
					area->m_nearNavSearchMarker = searchMarker;

					float distSq = ( area->GetCenter() - pos ).LengthSqr();

					if ( ( distSq <= radiusSq ) || ( radiusSq == 0 ) )
					{
						if ( func( area ) == false )
							return false;
					}
				}
			}
		}
		return true;
	}

	//---------------------------------------------------------------------------------------------------------------
	/*
	 * Step through nav mesh along line between startArea and endArea.
	 * Return true if enumeration reached endArea, false if doesn't reach it (no mesh between, bad connection, etc)
	 */
	template < typename Functor >
	bool ForAllAreasAlongLine( Functor &func, CNavArea *startArea, CNavArea *endArea )
	{
		if ( !startArea || !endArea )
			return false;

		if ( startArea == endArea )
		{
			func( startArea );
			return true;
		}

		Vector start = startArea->GetCenter();
		Vector end = endArea->GetCenter();

		Vector to = end - start;
		float range = to.NormalizeInPlace();

		const float epsilon = 0.00001f;

		if ( range < epsilon )
		{
			func( startArea );
			return true;
		}

		if ( abs( to.x ) < epsilon )
		{
			NavDirType dir = ( to.y < 0.0f ) ? NORTH : SOUTH;

			CNavArea *area = startArea;
			while( area )
			{
				func( area );

				if ( area == endArea )
					return true;

				const NavConnectVector *adjVector = area->GetAdjacentAreas( dir );

				area = NULL;

				for( int i=0; i<adjVector->Count(); ++i )
				{
					CNavArea *adjArea = adjVector->Element(i).area;

					const Vector &adjOrigin = adjArea->GetCorner( NORTH_WEST );

					if ( adjOrigin.x <= start.x && adjOrigin.x + adjArea->GetSizeX() >= start.x )
					{
						area = adjArea;
						break;
					}
				}
			}

			return false;
		}
		else if ( abs( to.y ) < epsilon )
		{
			NavDirType dir = ( to.x < 0.0f ) ? WEST : EAST;

			CNavArea *area = startArea;
			while( area )
			{
				func( area );

				if ( area == endArea )
					return true;

				const NavConnectVector *adjVector = area->GetAdjacentAreas( dir );

				area = NULL;

				for( int i=0; i<adjVector->Count(); ++i )
				{
					CNavArea *adjArea = adjVector->Element(i).area;

					const Vector &adjOrigin = adjArea->GetCorner( NORTH_WEST );

					if ( adjOrigin.y <= start.y && adjOrigin.y + adjArea->GetSizeY() >= start.y )
					{
						area = adjArea;
						break;
					}
				}
			}

			return false;
		}


		CNavArea *area = startArea;

		while( area )
		{
			func( area );

			if ( area == endArea )
				return true;

			const Vector &origin = area->GetCorner( NORTH_WEST );
			float xMin = origin.x;
			float xMax = xMin + area->GetSizeX();
			float yMin = origin.y;
			float yMax = yMin + area->GetSizeY();

			// clip ray to area
			Vector exit;
			NavDirType edge = NUM_DIRECTIONS;

			if ( to.x < 0.0f )
			{
				// find Y at west edge intersection
				float t = ( xMin - start.x ) / ( end.x - start.x );
				if ( t > 0.0f && t < 1.0f )
				{
					float y = start.y + t * ( end.y - start.y );
					if ( y >= yMin && y <= yMax )
					{
						// intersects this edge
						exit.x = xMin;
						exit.y = y;
						edge = WEST;
					}
				}
			}
			else
			{
				// find Y at east edge intersection
				float t = ( xMax - start.x ) / ( end.x - start.x );
				if ( t > 0.0f && t < 1.0f )
				{
					float y = start.y + t * ( end.y - start.y );
					if ( y >= yMin && y <= yMax )
					{
						// intersects this edge
						exit.x = xMax;
						exit.y = y;
						edge = EAST;
					}
				}
			}

			if ( edge == NUM_DIRECTIONS )
			{
				if ( to.y < 0.0f )
				{
					// find X at north edge intersection
					float t = ( yMin - start.y ) / ( end.y - start.y );
					if ( t > 0.0f && t < 1.0f )
					{
						float x = start.x + t * ( end.x - start.x );
						if ( x >= xMin && x <= xMax )
						{
							// intersects this edge
							exit.x = x;
							exit.y = yMin;
							edge = NORTH;
						}
					}
				}
				else
				{
					// find X at south edge intersection
					float t = ( yMax - start.y ) / ( end.y - start.y );
					if ( t > 0.0f && t < 1.0f )
					{
						float x = start.x + t * ( end.x - start.x );
						if ( x >= xMin && x <= xMax )
						{
							// intersects this edge
							exit.x = x;
							exit.y = yMax;
							edge = SOUTH;
						}
					}
				}
			}

			if ( edge == NUM_DIRECTIONS )
				break;

			const NavConnectVector *adjVector = area->GetAdjacentAreas( edge );

			area = NULL;

			for( int i=0; i<adjVector->Count(); ++i )
			{
				CNavArea *adjArea = adjVector->Element(i).area;

				const Vector &adjOrigin = adjArea->GetCorner( NORTH_WEST );

				if ( edge == NORTH || edge == SOUTH )
				{
					if ( adjOrigin.x <= exit.x && adjOrigin.x + adjArea->GetSizeX() >= exit.x )
					{
						area = adjArea;
						break;
					}
				}
				else
				{
					if ( adjOrigin.y <= exit.y && adjOrigin.y + adjArea->GetSizeY() >= exit.y )
					{
						area = adjArea;
						break;
					}
				}
			}
		}

		return false;
	}


	//-------------------------------------------------------------------------------------
	/**
	 * Apply the functor to all navigation ladders.
	 * If functor returns false, stop processing and return false.
	 */
	template < typename Functor >
	bool ForAllLadders( Functor &func )
	{
		for ( int i=0; i<m_ladders.Count(); ++i )
		{
			CNavLadder *ladder = m_ladders[i];

			if (func( ladder ) == false)
				return false;
		}

		return true;
	}

	//-------------------------------------------------------------------------------------
	/**
	 * Apply the functor to all navigation ladders.
	 * If functor returns false, stop processing and return false.
	 */
	template < typename Functor >
	bool ForAllLadders( Functor &func ) const
	{
		for ( int i=0; i<m_ladders.Count(); ++i )
		{
			const CNavLadder *ladder = m_ladders[i];

			if (func( ladder ) == false)
				return false;
		}

		return true;
	}

	//-------------------------------------------------------------------------------------
	/**
	 * tests a new area for connections to adjacent pre-existing areas
	 */
	template < typename Functor > void StitchAreaIntoMesh( CNavArea *area, NavDirType dir, Functor &func );

	//-------------------------------------------------------------------------------------
	/**
	 * Use the functor to test if an area is needing stitching into the existing nav mesh.
	 * The functor is different from how we normally use functors - it does no processing,
	 * and it's return value is true if the area is in the new set to be stiched, and false
	 * if it's a pre-existing area.
	 */
	template < typename Functor >
		bool StitchMesh( Functor &func )
	{
		FOR_EACH_VEC( TheNavAreas, it )
		{
			CNavArea *area = TheNavAreas[ it ];

			if ( func( area ) )
			{
				StitchAreaIntoMesh( area, NORTH, func );
				StitchAreaIntoMesh( area, SOUTH, func );
				StitchAreaIntoMesh( area, EAST, func );
				StitchAreaIntoMesh( area, WEST, func );
			}
		}

		return true;
	}

	NavLadderVector& GetLadders( void ) { return m_ladders; }	// Returns the list of ladders
	CNavLadder *GetLadderByID( unsigned int id ) const;

	CUtlVector< CNavArea * >& GetTransientAreas( void ) { return m_transientAreas; }

	enum EditModeType
	{
		NORMAL,				// normal mesh editing
		PLACE_PAINTING,		// in place painting mode
		CREATING_AREA,		// creating a new nav area
		CREATING_LADDER,	// creating a nav ladder
		DRAG_SELECTING,		// drag selecting a set of areas
		SHIFTING_XY,		// shifting selected set in XY plane
		SHIFTING_Z,			// shifting selected set in Z plane
	};
	EditModeType GetEditMode( void ) const;						// return the current edit mode
	void SetEditMode( EditModeType mode );						// change the edit mode
	bool IsEditMode( EditModeType mode ) const;					// return true if current mode matches given mode

	bool FindNavAreaOrLadderAlongRay( const Vector &start, const Vector &end, CNavArea **area, CNavLadder **ladder, CNavArea *ignore = NULL );

	void PostProcessCliffAreas();
	void SimplifySelectedAreas( void );	// Simplifies the selected set by reducing to 1x1 areas and re-merging them up with loosened tolerances

protected:
	virtual void PostCustomAnalysis( void ) { }					// invoked when custom analysis step is complete
	bool FindActiveNavArea( void );								// Finds the area or ladder the local player is currently pointing at.  Returns true if a surface was hit by the traceline.
	virtual void RemoveNavArea( CNavArea *area );				// remove an area from the grid
	bool FindGroundForNode( Vector *pos, Vector *normal );
	void GenerateNodes( const Extent &bounds );
	void RemoveNodes( void );

private:
	friend class CNavArea;
	friend class CNavNode;
	friend class CNavUIBasePanel;

	mutable CUtlVector<NavAreaVector> m_grid;
	float m_gridCellSize;										// the width/height of a grid cell for spatially partitioning nav areas for fast access
	int m_gridSizeX;
	int m_gridSizeY;
	float m_minX;
	float m_minY;
	unsigned int m_areaCount;									// total number of nav areas

	bool m_isLoaded;											// true if a Navigation Mesh has been loaded
	bool m_isOutOfDate;											// true if the Navigation Mesh is older than the actual BSP
	bool m_isAnalyzed;											// true if the Navigation Mesh needs analysis

	enum { HASH_TABLE_SIZE = 256 };
	CNavArea *m_hashTable[ HASH_TABLE_SIZE ];					// hash table to optimize lookup by ID
	int ComputeHashKey( unsigned int id ) const;				// returns a hash key for the given nav area ID

	int WorldToGridX( float wx ) const;							// given X component, return grid index
	int WorldToGridY( float wy ) const;							// given Y component, return grid index
	void AllocateGrid( float minX, float maxX, float minY, float maxY );	// clear and reset the grid to the given extents
	void GridToWorld( int gridX, int gridY, Vector *pos ) const;

	void AddNavArea( CNavArea *area );							// add an area to the grid

	void DestroyNavigationMesh( bool incremental = false );		// free all resources of the mesh and reset it to empty state
	void DestroyHidingSpots( void );

	void ComputeBattlefrontAreas( void );						// determine areas where rushing teams will first meet

	//----------------------------------------------------------------------------------
	// Place directory
	//
	char **m_placeName;											// master directory of place names (ie: "places")
	unsigned int m_placeCount;									// number of "places" defined in placeName[]
	void LoadPlaceDatabase( void );								// load the place names from a file

	//----------------------------------------------------------------------------------
	// Edit mode
	//
	EditModeType m_editMode;									// the current edit mode
	bool m_isEditing;											// true if in edit mode

	unsigned int m_navPlace;									// current navigation place for editing
	void OnEditModeStart( void );								// called when edit mode has just been enabled
	void DrawEditMode( void );									// draw navigation areas
	void OnEditModeEnd( void );									// called when edit mode has just been disabled
	void UpdateDragSelectionSet( void );							// update which areas are overlapping the drag selected bounds
	Vector m_editCursorPos;										// current position of the cursor
	CNavArea *m_markedArea;										// currently marked area for edit operations
	CNavArea *m_selectedArea;									// area that is selected this frame
	CNavArea *m_lastSelectedArea;								// area that was selected last frame
	NavCornerType m_markedCorner;								// currently marked corner for edit operations
	Vector m_anchor;											// first corner of an area being created
	bool m_isPlacePainting;										// if true, we set an area's place by pointing at it
	bool m_splitAlongX;											// direction the selected nav area would be split
	float m_splitEdge;											// location of the possible split

	bool m_climbableSurface;									// if true, the cursor is pointing at a climable surface
	Vector m_surfaceNormal;										// Normal of the surface the cursor is pointing at
	Vector m_ladderAnchor;										// first corner of a ladder being created
	Vector m_ladderNormal;										// Normal of the surface of the ladder being created
	CNavLadder *m_selectedLadder;								// ladder that is selected this frame
	CNavLadder *m_lastSelectedLadder;							// ladder that was selected last frame
	CNavLadder *m_markedLadder;									// currently marked ladder for edit operations

	bool FindLadderCorners( Vector *c1, Vector *c2, Vector *c3 );	// computes the other corners of a ladder given m_ladderAnchor, m_editCursorPos, and m_ladderNormal

	void GetEditVectors( Vector *pos, Vector *forward );		// Gets the eye position and view direction of the editing player

	CountdownTimer m_showAreaInfoTimer;							// Timer that controls how long area info is displayed
	
	NavAreaVector m_selectedSet;								// all currently selected areas
	NavAreaVector m_dragSelectionSet;							// all areas in the current drag selection
	bool m_isContinuouslySelecting;								// if true, we are continuously adding to the selected set
	bool m_isContinuouslyDeselecting;							// if true, we are continuously removing from the selected set

	bool m_bIsDragDeselecting;
	int m_nDragSelectionVolumeZMax;
	int m_nDragSelectionVolumeZMin;

	void DoToggleAttribute( CNavArea *area, NavAttributeType attribute );		// toggle an attribute on given area


	//----------------------------------------------------------------------------------
	// Auto-generation
	//
	bool UpdateGeneration( float maxTime = 0.25f );				// process the auto-generation for 'maxTime' seconds. return false if generation is complete.

	virtual void BeginCustomAnalysis( bool bIncremental ) {}
	virtual void EndCustomAnalysis() {}

	CNavNode *m_currentNode;									// the current node we are sampling from
	NavDirType m_generationDir;
	CNavNode *AddNode( const Vector &destPos, const Vector &destNormal, NavDirType dir, CNavNode *source, bool isOnDisplacement, float obstacleHeight, float flObstacleStartDist, float flObstacleEndDist );		// add a nav node and connect it, update current node

	NavLadderVector m_ladders;									// list of ladder navigation representations
	void BuildLadders( void );
	void DestroyLadders( void );

	bool SampleStep( void );									// sample the walkable areas of the map
	void CreateNavAreasFromNodes( void );						// cover all of the sampled nodes with nav areas

	bool TestArea( CNavNode *node, int width, int height );		// check if an area of size (width, height) can fit, starting from node as upper left corner
	int BuildArea( CNavNode *node, int width, int height );		// create a CNavArea of size (width, height) starting fom node at upper left corner
	bool CheckObstacles( CNavNode *node, int width, int height, int x, int y );

	void MarkPlayerClipAreas( void );
	void MarkJumpAreas( void );
	void StichAndRemoveJumpAreas( void );
	void RemoveJumpAreas( void );	
	void SquareUpAreas( void );
	void MergeGeneratedAreas( void );
	void ConnectGeneratedAreas( void );
	void FixUpGeneratedAreas( void );
	void FixCornerOnCornerAreas( void );
	void FixConnections( void );
	void SplitAreasUnderOverhangs( void );
	void ValidateNavAreaConnections( void );
	void StitchGeneratedAreas( void );							// Stitches incrementally-generated areas into the existing mesh
	void StitchAreaSet( CUtlVector< CNavArea * > *areas );		// Stitches an arbitrary set of areas into the existing mesh
	void HandleObstacleTopAreas( void );						// Handles fixing/generating areas on top of slim obstacles such as fences and railings
	void RaiseAreasWithInternalObstacles();
	void CreateObstacleTopAreas();
	bool CreateObstacleTopAreaIfNecessary( CNavArea *area, CNavArea *areaOther, NavDirType dir, bool bMultiNode );
	void RemoveOverlappingObstacleTopAreas();	


	enum GenerationStateType
	{
		SAMPLE_WALKABLE_SPACE,
		CREATE_AREAS_FROM_SAMPLES,
		FIND_HIDING_SPOTS,
		FIND_ENCOUNTER_SPOTS,
		FIND_SNIPER_SPOTS,
		FIND_EARLIEST_OCCUPY_TIMES,
		FIND_LIGHT_INTENSITY,
		COMPUTE_MESH_VISIBILITY,
		CUSTOM,													// mod-specific generation step
		SAVE_NAV_MESH,

		NUM_GENERATION_STATES
	}
	m_generationState;											// the state of the generation process
	enum GenerationModeType
	{
		GENERATE_NONE,
		GENERATE_FULL,
		GENERATE_INCREMENTAL,
		GENERATE_SIMPLIFY,
		GENERATE_ANALYSIS_ONLY,
	}
	m_generationMode;											// true while a Navigation Mesh is being generated
	int m_generationIndex;										// used for iterating nav areas during generation process
	int m_sampleTick;											// counter for displaying pseudo-progress while sampling walkable space
	bool m_bQuitWhenFinished;
	float m_generationStartTime;
	Extent m_simplifyGenerationExtent;

	char *m_spawnName;											// name of player spawn entity, used to initiate sampling

	struct WalkableSeedSpot
	{
		Vector pos;
		Vector normal;
	};
	CUtlVector< WalkableSeedSpot > m_walkableSeeds;				// list of walkable seed spots for sampling

	CNavNode *GetNextWalkableSeedNode( void );					// return the next walkable seed as a node
	int m_seedIdx;
	int m_hostThreadModeRestoreValue;							// stores the value of host_threadmode before we changed it

	void BuildTransientAreaList( void );
	CUtlVector< CNavArea * > m_transientAreas;

	void UpdateAvoidanceObstacleAreas( void );
	CUtlVector< CNavArea * > m_avoidanceObstacleAreas;
	CUtlVector< INavAvoidanceObstacle * > m_avoidanceObstacles;

	void UpdateBlockedAreas( void );
	CUtlVector< CNavArea * > m_blockedAreas;

	CUtlVector< int > m_storedSelectedSet;						// "Stored" selected set, so we can do some editing and then restore the old selected set.  Done by ID, so we don't have to worry about split/delete/etc.

	void BeginVisibilityComputations( void );
	void EndVisibilityComputations( void );

	void TestAllAreasForBlockedStatus( void );					// Used to update blocked areas after a round restart. Need to delay so the map logic has all fired.
	CountdownTimer m_updateBlockedAreasTimer;			
};

// the global singleton interface
extern CNavMesh *TheNavMesh;

// factory for creating the Navigation Mesh
extern CNavMesh *NavMeshFactory( void );

// for debugging the A* algorithm, if nonzero, show debug display and decrement for each pathfind
extern int g_DebugPathfindCounter;


//--------------------------------------------------------------------------------------------------------------
inline bool CNavMesh::IsEditMode( EditModeType mode ) const
{
	return m_editMode == mode;
}

//--------------------------------------------------------------------------------------------------------------
inline CNavMesh::EditModeType CNavMesh::GetEditMode( void ) const
{
	return m_editMode;
}

//--------------------------------------------------------------------------------------------------------------
inline unsigned int CNavMesh::GetSubVersionNumber( void ) const
{
	return 0;
}


//--------------------------------------------------------------------------------------------------------------
inline CNavArea *CNavMesh::CreateArea( void ) const
{
	return new CNavArea;
}

//--------------------------------------------------------------------------------------------------------------
inline void CNavMesh::DestroyArea( CNavArea *pArea ) const
{
	delete pArea;
}

//--------------------------------------------------------------------------------------------------------------
inline int CNavMesh::ComputeHashKey( unsigned int id ) const
{
	return id & 0xFF;
}

//--------------------------------------------------------------------------------------------------------------
inline int CNavMesh::WorldToGridX( float wx ) const
{ 
	int x = (int)( (wx - m_minX) / m_gridCellSize );

	if (x < 0)
		x = 0;
	else if (x >= m_gridSizeX)
		x = m_gridSizeX-1;
	
	return x;
}

//--------------------------------------------------------------------------------------------------------------
inline int CNavMesh::WorldToGridY( float wy ) const
{ 
	int y = (int)( (wy - m_minY) / m_gridCellSize );

	if (y < 0)
		y = 0;
	else if (y >= m_gridSizeY)
		y = m_gridSizeY-1;
	
	return y;
}


//--------------------------------------------------------------------------------------------------------------
inline unsigned int CNavMesh::GetGenerationTraceMask( void ) const
{
	return MASK_NPCSOLID_BRUSHONLY;
}


//--------------------------------------------------------------------------------------------------------------
//
// Function prototypes
//

extern void ApproachAreaAnalysisPrep( void );
extern void CleanupApproachAreaAnalysisPrep( void );
extern bool IsHeightDifferenceValid( float test, float other1, float other2, float other3 );

#endif // _NAV_MESH_H_
