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

#ifndef AI_TRACKPATHER_H
#define AI_TRACKPATHER_H

#if defined( _WIN32 )
#pragma once
#endif


#include "ai_basenpc.h"


class CPathTrack;

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

class CAI_TrackPather : public CAI_BaseNPC
{
	DECLARE_CLASS( CAI_TrackPather, CAI_BaseNPC );
	DECLARE_DATADESC();
public:

	bool			IsOnPathTrack()							{ return (m_pCurrentPathTarget != NULL); }

protected:	
	void			InitPathingData( float flTrackArrivalTolerance, float flTargetDistance, float flAvoidDistance );
	virtual bool	GetTrackPatherTarget( Vector *pPos ) { return false; }
	virtual CBaseEntity *GetTrackPatherTargetEnt()	{ return NULL; }

	const Vector &	GetDesiredPosition() const				{ return m_vecDesiredPosition; 	}
	void 			SetDesiredPosition( const Vector &v )	{ m_vecDesiredPosition = v; 	}
	const Vector &	GetGoalOrientation() const				{ return m_vecGoalOrientation; 	}
	void 			SetGoalOrientation( const Vector &v )	{ m_vecGoalOrientation = v; 	}

	bool			CurPathTargetIsDest()					{ return ( m_pDestPathTarget == m_pCurrentPathTarget ); }

	virtual bool	HasReachedTarget( void ) 				{ return (WorldSpaceCenter() - m_vecDesiredPosition).Length() < 128; }

	CPathTrack *	GetDestPathTarget()						{ return m_pDestPathTarget;		}

	bool			IsInForcedMove() const					{ return m_bForcedMove;			}
	void			ClearForcedMove()						{ m_bForcedMove = false;		}

	float			GetPathMaxSpeed() const					{ return m_flPathMaxSpeed;		}

	void			OnSave( IEntitySaveUtils *pUtils );
	void			OnRestore( void );

protected:
	enum PauseState_t
	{
		PAUSE_NO_PAUSE = 0,
		PAUSED_AT_POSITION,
		PAUSE_AT_NEXT_LOS_POSITION,

		PAUSE_FORCE_DWORD = 0xFFFFFFFF,
	};

	// Sets a track
	void SetTrack( string_t strTrackName );
	void SetTrack( CBaseEntity *pGoalEnt );

	// Fly to a particular track point via the path
	virtual void InputFlyToPathTrack( inputdata_t &inputdata );

	// Updates the nav target if we've reached it
	void UpdateTrackNavigation( void );

	// Computes distance + nearest point from the current path..
	float ClosestPointToCurrentPath( Vector *pVecPoint ) const;

	// Computes a "path" velocity at a particular point along the current path
	void ComputePathTangent( float t, Vector *pVecTangent ) const;

	// Computes the *normalized* velocity at which the helicopter should approach the final point
	void ComputeNormalizedDestVelocity( Vector *pVecVelocity ) const;

	// Sets the farthest path distance
	void SetFarthestPathDist( float flMaxPathDist );

	// Returns the next/previous path along our current path
	CPathTrack *NextAlongCurrentPath( CPathTrack *pPath ) const;
	CPathTrack *PreviousAlongCurrentPath( CPathTrack *pPath ) const;

	// Adjusts a "next"most node based on the current movement direction
	CPathTrack *AdjustForMovementDirection( CPathTrack *pPath ) const;

	// Enemy visibility check
	virtual CBaseEntity *FindTrackBlocker( const Vector &vecViewPoint, const Vector &vecTargetPos );

	// Compute a point n units along a path
	void ComputePointAlongPath( const Vector &vecStartPoint, float flDistance, Vector *pTarget );

	// Are we leading?
	bool IsLeading() const { return m_bLeading && !m_bForcedMove; }

	// Leading + leading distance
	void EnableLeading( bool bEnable );
	void SetLeadingDistance( float flLeadDistance );
	float GetLeadingDistance( ) const;

	// Compute a point n units along the current path from our current position
	// (but don't pass the desired target point)
	void ComputePointAlongCurrentPath( float flDistance, float flPerpDist, Vector *pTarget );

	// Returns the perpendicular distance of the target from the nearest path point
	float TargetDistanceToPath() const { return m_flTargetDistFromPath; }

	// Returns the speed of the target relative to the path
	float TargetSpeedAlongPath() const;

	// Returns the speed of the target *across* the path
	float TargetSpeedAcrossPath() const;

	// Compute a path direction
	void ComputePathDirection( CPathTrack *pPath, Vector *pVecPathDir );

	// What's the current path direction?
	void CurrentPathDirection( Vector *pVecPathDir );

	// Returns the max distance we can be from the path
	float MaxDistanceFromCurrentPath() const;

	// true to use farthest, false for nearest
	void UseFarthestPathPoint( bool useFarthest );

	// Moves to an explicit track point
	void MoveToTrackPoint( CPathTrack *pTrack );

	// Sets up a new current path target
	void SetupNewCurrentTarget( CPathTrack *pTrack );

	// Compute the distance to the leading position
	float ComputeDistanceToLeadingPosition();

	// Compute the distance to the target position
	float ComputeDistanceToTargetPosition();

	// Set the pause state.
	void SetPauseState( PauseState_t pauseState ) { m_nPauseState = pauseState; }

	// Does this path track have LOS to the target?
	bool HasLOSToTarget( CPathTrack *pTrack );

	// FIXME: Work this back into the base class
	virtual bool ShouldUseFixedPatrolLogic() { return false; }

	// Deal with teleportation
	void Teleported();

private:

	CPathTrack		*BestPointOnPath( CPathTrack *pPath, const Vector &targetPos, float avoidRadius, bool visible, bool bFarthestPointOnPath );

	// Input methods
	void InputSetTrack( inputdata_t &inputdata );
	void InputChooseFarthestPathPoint( inputdata_t &inputdata );
	void InputChooseNearestPathPoint( inputdata_t &inputdata );
	void InputStartBreakableMovement( inputdata_t &inputdata );
	void InputStopBreakableMovement( inputdata_t &inputdata );
	void InputStartPatrol( inputdata_t &inputdata );
	void InputStopPatrol( inputdata_t &inputdata );
	void InputStartLeading( inputdata_t &inputdata );
	void InputStopLeading( inputdata_t &inputdata );

	// Obsolete, for backward compatibility
	void InputStartPatrolBreakable( inputdata_t &inputdata );

	// Flies to a point on a track
	void FlyToPathTrack( string_t strTrackName );

	// Selects a new destination target
	void SelectNewDestTarget();

	// Makes sure we've picked the right position along the path if we're chasing an enemy
	void UpdateTargetPosition( );

	// Moves to the track
	void UpdateCurrentTarget();
	void UpdateCurrentTargetLeading();

	// Track debugging info
	void VisualizeDebugInfo( const Vector &vecNearestPoint, const Vector &vecTarget );

	// Moves to the closest track point
	void MoveToClosestTrackPoint( CPathTrack *pTrack );

	// Are the two path tracks connected?
	bool IsOnSameTrack( CPathTrack *pPath1, CPathTrack *pPath2 ) const;

	// Is pPathTest in "front" of pPath on the same path? (Namely, does GetNext() get us there?)
	bool IsForwardAlongPath( CPathTrack *pPath, CPathTrack *pPathTest ) const;

	// Purpose: 
	void UpdateTargetPositionLeading( void );

	// Compute a point n units along a path
	CPathTrack *ComputeLeadingPointAlongPath( const Vector &vecStartPoint, CPathTrack *pFirstTrack, float flDistance, Vector *pTarget );

	// Finds the closest point on the path, returns a signed perpendicular distance
	CPathTrack *FindClosestPointOnPath( CPathTrack *pPath, const Vector &targetPos, Vector *pVecClosestPoint, Vector *pVecPathDir, float *pDistanceFromPath );

	// Methods to find a signed perp distance from the track
	// and to compute a point off the path based on the signed perp distance
	float ComputePerpDistanceFromPath( const Vector &vecPointOnPath, const Vector &vecPathDir, const Vector &vecPointOffPath );
	void ComputePointFromPerpDistance( const Vector &vecPointOnPath, const Vector &vecPathDir, float flPerpDist, Vector *pResult );

	// Returns the direction of the path at the closest point to the target
	const Vector &TargetPathDirection() const;
	const Vector &TargetPathAcrossDirection() const;

	// Returns distance along path to target, returns -1 if there's no path
	float ComputePathDistance( CPathTrack *pStart, CPathTrack *pDest, bool bForward ) const;

	// Compute the distance to a particular point on the path
	float ComputeDistanceAlongPathToPoint( CPathTrack *pStartTrack, CPathTrack *pDestTrack, const Vector &vecDestPosition, bool bMovingForward );

private:
	//---------------------------------
	Vector			m_vecDesiredPosition;
	Vector			m_vecGoalOrientation; // orientation of the goal entity.

	// NOTE: CurrentPathTarget changes meaning based on movement direction
	// For this *after* means the "next" (m_pnext) side of the line segment
	// and "before" means the "prev" (m_pprevious) side of the line segment
	// CurrentPathTarget is *after* the desired point when moving forward, 
	// and *before* the desired point when moving backward.
	// DestPathTarget + TargetNearestPath always represent points
	// *after* the desired point.
	CHandle<CPathTrack> m_pCurrentPathTarget;
	CHandle<CPathTrack> m_pDestPathTarget;
	CHandle<CPathTrack> m_pLastPathTarget;
	CHandle<CPathTrack> m_pTargetNearestPath;	// Used only by leading, it specifies the path point *after* where the target is

	string_t		m_strCurrentPathName;
	string_t		m_strDestPathName;
	string_t		m_strLastPathName;
	string_t		m_strTargetNearestPathName;

	Vector			m_vecLastGoalCheckPosition;	// Last position checked for moving towards
	float			m_flEnemyPathUpdateTime;	// Next time to update our enemies position
	bool			m_bForcedMove;				// Means the destination point must be reached regardless of enemy position
	bool			m_bPatrolling;				// If set, move back and forth along the current track until we see an enemy
	bool			m_bPatrolBreakable;			// If set, I'll stop patrolling if I see an enemy
	bool			m_bLeading;					// If set, we can lead our enemies

	// Derived class pathing data
	float			m_flTargetDistanceThreshold;// Distance threshold used to determine when a target has moved enough to update our navigation to it
	float			m_flAvoidDistance;			//
	
	float			m_flTargetTolerance;		// How far from a path track do we need to be before we 'reached' it?
	Vector			m_vecSegmentStartPoint;		// Starting point for the current segment
	Vector			m_vecSegmentStartSplinePoint;	// Used to define a spline which is used to compute path velocity
	bool			m_bMovingForward;
	bool			m_bChooseFarthestPoint;
	float			m_flFarthestPathDist;		// How far from a path track do we need to be before we 'reached' it?

	float			m_flPathMaxSpeed;
	float			m_flTargetDistFromPath;		// How far is the target from the closest point on the path?
	float			m_flLeadDistance;
	Vector			m_vecTargetPathDir;
	Vector			m_vecTargetPathPoint;		// What point on the path is closest to the target?

	PauseState_t	m_nPauseState;
};

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

#endif // AI_TRACKPATHER_H