// NextBotPathFollow.h
// Path following
// Author: Michael Booth, April 2005
//========= Copyright Valve Corporation, All rights reserved. ============//

#ifndef _NEXT_BOT_PATH_FOLLOWER_
#define _NEXT_BOT_PATH_FOLLOWER_

#include "nav_mesh.h"
#include "nav_pathfind.h"
#include "NextBotPath.h"

class INextBot;
class ILocomotion;


//--------------------------------------------------------------------------------------------------------
/**
 * A PathFollower extends a Path to include mechanisms to move along (follow) it
 */
class PathFollower : public Path
{
public:
	PathFollower( void );
	virtual ~PathFollower();

	virtual void Invalidate( void );				// (EXTEND) cause the path to become invalid
	virtual void Draw( const Path::Segment *start = NULL ) const;	// (EXTEND) draw the path for debugging
	virtual void OnPathChanged( INextBot *bot, Path::ResultType result );	// invoked when the path is (re)computed (path is valid at the time of this call)

	virtual void Update( INextBot *bot );			// move bot along path

	virtual const Path::Segment *GetCurrentGoal( void ) const;	// return current goal along the path we are trying to reach

	virtual void SetMinLookAheadDistance( float value );		// minimum range movement goal must be along path
	
	virtual CBaseEntity *GetHindrance( void ) const;			// returns entity that is hindering our progress along the path

	virtual bool IsDiscontinuityAhead( INextBot *bot, Path::SegmentType type, float range = -1.0f ) const;	// return true if there is a the given discontinuity ahead in the path within the given range (-1 = entire remaining path)

	void SetGoalTolerance( float range );			// set tolerance within at which we're considered to be at our goal

private:
	const Path::Segment *m_goal;					// our current goal along the path
	float m_minLookAheadRange;

	bool CheckProgress( INextBot *bot );
	bool IsAtGoal( INextBot *bot ) const;			// return true if reached current path goal

	//bool IsOnStairs( INextBot *bot ) const;		// return true if bot is standing on a stairway
	bool m_isOnStairs;

	CountdownTimer m_avoidTimer;					// do avoid check more often if we recently avoided

	CountdownTimer m_waitTimer;						// for waiting for a blocker to move off our path
	CHandle< CBaseEntity > m_hindrance;
	
	// debug display data for avoid volumes
	bool m_didAvoidCheck;
	Vector m_leftFrom;
	Vector m_leftTo;
	bool m_isLeftClear;
	Vector m_rightFrom;
	Vector m_rightTo;
	bool m_isRightClear;
	Vector m_hullMin, m_hullMax;

	void AdjustSpeed( INextBot *bot );				// adjust speed based on path curvature

	Vector Avoid( INextBot *bot, const Vector &goalPos, const Vector &forward, const Vector &left );		// avoidance movements for very nearby obstacles. returns modified goal position
	bool Climbing( INextBot *bot, const Path::Segment *goal, const Vector &forward, const Vector &left, float goalRange );		// climb up ledges 
	bool JumpOverGaps( INextBot *bot, const Path::Segment *goal, const Vector &forward, const Vector &left, float goalRange );	// jump over gaps

	bool LadderUpdate( INextBot *bot );				// move bot along ladder
	CBaseEntity *FindBlocker( INextBot *bot );		// if entity is returned, it is blocking us from continuing along our path

	float m_goalTolerance;
};


inline void PathFollower::SetGoalTolerance( float range )
{
	m_goalTolerance = range;
}


inline const Path::Segment *PathFollower::GetCurrentGoal( void ) const
{
	return m_goal;
}


inline void PathFollower::SetMinLookAheadDistance( float value )
{
	m_minLookAheadRange = value;
}

inline CBaseEntity *PathFollower::GetHindrance( void ) const
{
	return m_hindrance;
}


#endif // _NEXT_BOT_PATH_FOLLOWER_