//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Shared_RouteHelper.h
//
// Created by NJD - 07 December 2006
//
// Copyright (C) 2006 Rebellion, All Rights Reserved.
//
//////////////////////////////////////////////////////////////////////////////

// B E G I N   F I L E   W R A P P E R ///////////////////////////////////////

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __AVP_BEHAVIOUR_SHARED_ROUTEHELPER_H__
#define __AVP_BEHAVIOUR_SHARED_ROUTEHELPER_H__ 1

// E N D   F I L E   W R A P P E R ///////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

// Asura
#include "Core/Asura_Maths.h"
#ifdef ASURA_USE_STREAMING
#include "Core/Asura_Hash.h"
#endif

#include "AI/Asura_AI_Navigation.h"

// axon
#include "Behaviour/Axon_Project_Behaviour.h"

// F O R W A R D   D E C L A R A T I O N S ///////////////////////////////////

class Asura_AI_Navigation_Handler;
class Axon_Project_Brain;

//////////////////////////////////////////////////////////////////////////////
//
// AVP_Behaviour_Shared_RouteHelper
//
// This should probably be moved into Axon, not as a behaviour but as a
// stand alone helper class.
//
//////////////////////////////////////////////////////////////////////////////

class AVP_Behaviour_Shared_RouteHelper : public Axon_Project_Behaviour
{
	typedef Axon_Project_Behaviour PARENT;
	friend class AVP_Behaviour_Alien;

public:
	AVP_Behaviour_Shared_RouteHelper( Axon_Project_Brain* pxBrain, u_int uBehaviourType );
	virtual ~AVP_Behaviour_Shared_RouteHelper();

	enum AVP_AI_PATHFINDER_FLAGS
	{
		AVP_AI_PATHFINDER_FLAG_HUMANOID = ASURA_AI_PATHFINDER_FLAGS_BEGIN_PROJECT_SPECIFIC, // 0x00010000
		AVP_AI_PATHFINDER_FLAG_ALIEN    = 0x00020000
	};

	enum AVP_AI_CONNECTION_FLAGS
	{
		AVP_AI_CONNECTION_FLAG_HUMANOID   = ASURA_AI_CONNECTION_FLAGS_BEGIN_PROJECT_SPECIFIC, // 0x0100
		AVP_AI_CONNECTION_FLAG_ALIEN      = 0x0200,
		AVP_AI_CONNECTION_FLAG_TRANSITION = 0x0400
	};

	enum AVP_AI_NAVVERTEX_FLAGS
	{
		AVP_AI_NAVVERTEX_FLAG_HUMANOID = ASURA_AI_NAVVERTEX_FLAG_BEGIN_PROJECT_SPECIFIC, // 0x0100
		AVP_AI_NAVVERTEX_FLAG_ALIEN    = 0x0200
	};

	u_short*		GetRoute()				{return m_pusRoute;}
	int				GetRouteIndex()			{return m_iRouteIndex;}
	unsigned short	GetCurrentWaypoint()	{return m_usCurrentWaypoint;}
	int				GetRouteLength()		{return m_iRouteLength;}


protected:
	virtual void	Deactivate			 ();
	virtual void	Update				 ( const float fTimeslice );
	void			DestroyRoute();

protected:
	u_short*		m_pusRoute;				// A* route found by navigation system
	int				m_iRouteLength;			// length of A* route
	int				m_iRouteIndex;			// current target in A* route
	Asura_Vector_3	m_xCurrentDestination;	// destination we are currently heading to (on our way to our final destination)
	Asura_Vector_3	m_xFinalDestination;	// our final destination
	unsigned short	m_usCurrentWaypoint;	// current/last waypoint we were in
	unsigned short	m_usFinalWaypoint;		// our final destination waypoint

	// Waypoint weights
	static unsigned char s_ucFloorWeight;
	static unsigned char s_ucWallWeight;
	static unsigned char s_ucCeilingWeight;
	static unsigned char s_ucStepsWeight;
	static unsigned char s_ucSlopeWeight;

#ifndef ASURA_NO_DEBUG_OUTPUT
	int      m_iPreSmoothedRouteLength;
	u_short* m_pusPreSmoothedRoute;
#endif

#ifndef ASURA_NO_DEBUG_OUTPUT
	virtual void RenderDebugInfo();
#endif

	enum RouteTraversalState
	{
		RTS_UNSET,

		RTS_BEGINNING_ROUTE,
		RTS_EN_ROUTE,
		RTS_MOVING_TO_DESTINATION,
		RTS_IN_FINAL_WAYPOINT,
		RTS_AT_DESTINATION,

		RTS_NUM_STATES
	};
	RouteTraversalState m_eRouteTraversalState;

	// Virtual function used by all sub-classes to re-orient...
	virtual void OrientAlongRoute(const float fTimeStep, const bool bBackwards = false, AVP_ServerEntity_Actor* pxOtherActor = NULL, const bool bCanTurnInMidAir = false);
	// If validate position is true the position will be resolved to the ground.
	// Returns whether it found a route or not.
	virtual bool GetRouteTo( const Asura_Vector_3& xPosition, const bool bValidatePosition = true, const bool bStraightenRoute = true, const bool bDestroyOld = true );
	// Returns whether it found a route or not.
	bool GetCustomRouteTo (const Asura_Vector_3& xPosition, const bool bUseFloorOnly = true, const bool bValidatePosition = true, const bool bStraightenRoute = true, const bool bDestroyOld = true);
	// Updates the current waypoint without using a full waypoint search, if at all possible.
	virtual void UpdateCurrentWaypoint( const Asura_Vector_3& xCurrentPosition );
	// Resolves the position to be within waypoint space if necessary. Returns the index of the nearest waypoint to the position.
	virtual u_short ResolveToValidPosition( Asura_Vector_3& xPosition );
	// Updates the current destination on any route we have.
	virtual void UpdateCurrentDestination();
	// Returns whether we're close enough to be considered at the target position.
	bool AtTargetPosition( const Asura_Vector_3& xCurrentPosition, const Asura_Vector_3& xTargetPosition, float fXZTolerance = 0.75f );
	// returns true if the closest waypoints to the positions supplied are in the same zone.
	static bool PositionsAreInSameWaypointZone(Asura_Vector_3 pxPositionOne, Asura_Vector_3 pxPositionTwo);

	void RemoveRedundantWaypoints();
	bool IsStuck() const { return m_bIsStuck; }

	RouteTraversalState		GetRouteTraversalState()	const { return m_eRouteTraversalState; }
	const Asura_Vector_3&	GetCurrentDestination()		const { return m_xCurrentDestination;  }
	const Asura_Vector_3&	GetFinalDestination()		const { return m_xFinalDestination;    }
	const u_short			GetCurrentWaypoint()		const { return m_usCurrentWaypoint;    }
	const u_short			GetFinalWaypoint()			const { return m_usFinalWaypoint;      }

private:    
	virtual bool	WaypointValidForSmoothing( const u_short usWaypointIndex );
	static bool		AVP_LinkFlagsTestFn(const unsigned short usLinkFlags, const unsigned int uPathfinderFlags);
	static bool		AVP_Human_LinkFlagsTestFn(const unsigned short usLinkFlags, const unsigned int uPathfinderFlags);

	Asura_AI_Navigation_Handler*	GetCurrentNavHandler() const;
	void							AddWaypointWeighting();

private:   

#ifdef ASURA_USE_STREAMING
	Asura_Hash_ID  m_uCurrentZoneHash;          // the streaming zone we're currently in
#endif

	float			m_fUnableToMoveTimer;			// time AI has been stuck for
	float			m_fDestinationRange4SecsAgo;	// to check for getting stuck
	Asura_Vector_3	m_xPosition4SecsAgo;			// to check for getting stuck
	float			m_fAtTargetYTol;				// tolerance used on up vector when checking if we're at our destination
	// A time out while it is on route, if the actor gets within a threshold range of the current destination, 
	// but doesn't reach the current destination with in a certain time thej it will update the route
	float			m_EN_ROUTE_fTimeout;			

	union
	{
		u_short m_usFlags;
		struct
		{
			u_int m_bIsStuck				: 1;
		};
	};

private:
	static const float s_fUnableToMoveDuration;
	enum { CURRENT_CHUNK_VERSION = 0 };
};

//////////////////////////////////////////////////////////////////////////////
//
// 
//
//////////////////////////////////////////////////////////////////////////////

#endif //__AVP_BEHAVIOUR_SHARED_ROUTEHELPER_H__
