/**
*
* \file Asura_LOS.h
*
* Created by Kevin Lea - 13/02/2002 15:06:26
*
* Copyright (C) 2002 Rebellion, All Rights Reserved.
*
* \brief
*
**/

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_LOS_H__
#define __ASURA_LOS_H__

#include "Core/Asura_Maths.h"
#include "Network/Asura_Guid.h"
#include "Core/Asura_Hash.h"

#define ASURA_LOS_MAX_DISTANCE (Asura_Maths::LargeFloat)
  
//////////////////////////
// Asura_LOS_OutputData //
//////////////////////////
class Asura_LOS_OutputData
{
	public:
        
        void Reset( float fInitialMaximumRange );

		float m_fDistanceToIntersection;
		
		Asura_Guid m_uHitEntity;
		//m_iHitBoneID is the index of the bone hit in the main skin. If an attachment is hit , then this will be the index
		//of the bone that the attachment is linked to , not the bone in the attachment itself. RAR 28/02/03
		int m_iHitBoneID;  
		int m_iHitAttachmentID;
		unsigned short m_usPolyFlags;

		// TODO: when the new material system supports project flags for surface
		// type, we'll only need to return the new material ID
#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
		Asura_Hash_ID m_uMaterialResponseHashID;
#endif
		short m_sMaterialID;
	
		Asura_Vector_3 m_xNormal;
		bool m_bHitEnvironment;
		bool m_bStartedInside;

		u_int m_uEnvironmentFaceIndex;

		// equality test added for unit test of different LOS routines
		inline bool operator == ( const Asura_LOS_OutputData& xOther) const
		{
			bool bEquals =		(m_fDistanceToIntersection	== xOther.m_fDistanceToIntersection)
							&&	(m_uHitEntity				== xOther.m_uHitEntity)
							&&	(m_iHitBoneID				== xOther.m_iHitBoneID)
							&&	(m_iHitAttachmentID			== xOther.m_iHitAttachmentID)
							&&	(m_usPolyFlags				== xOther.m_usPolyFlags)
							&&	(m_xNormal					== xOther.m_xNormal)
							&&	(m_bHitEnvironment			== xOther.m_bHitEnvironment)
							&&	(m_bStartedInside			== xOther.m_bStartedInside)
							&&	(m_uEnvironmentFaceIndex	== xOther.m_uEnvironmentFaceIndex)
#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
							&&	(m_uMaterialResponseHashID	== xOther.m_uMaterialResponseHashID)
#endif
							&&	(m_sMaterialID				== xOther.m_sMaterialID)
							;

			return bEquals;
		}
};

/////////////////////////
// Asura_LOS_InputData //
/////////////////////////
class Asura_LOS_InputData
{
	public:
		Asura_LOS_InputData(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore)																												{ Initialise(xOrigin, xDirection, uEntityToIgnore); }
		Asura_LOS_InputData(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange)																						{ Initialise(xOrigin, xDirection, uEntityToIgnore, fMaxRange); }
		Asura_LOS_InputData(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const bool bBoundingBoxOnly)																					{ Initialise(xOrigin, xDirection, uEntityToIgnore, bBoundingBoxOnly); }
		Asura_LOS_InputData(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange, const bool bBoundingBoxOnly)															{ Initialise(xOrigin, xDirection, uEntityToIgnore, fMaxRange, bBoundingBoxOnly); }
		Asura_LOS_InputData(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange, const bool bBoundingBoxOnly, const u_int uFlagsToIgnore)								{ Initialise(xOrigin, xDirection, uEntityToIgnore, fMaxRange, bBoundingBoxOnly, uFlagsToIgnore); }
		Asura_LOS_InputData(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange, const bool bBoundingBoxOnly, const u_int uFlagsToIgnore, const u_int uFlagsToTestFor)	{ Initialise(xOrigin, xDirection, uEntityToIgnore, fMaxRange, bBoundingBoxOnly, uFlagsToIgnore, uFlagsToTestFor); }

		void Initialise(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore);
		void Initialise(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange);
		void Initialise(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const bool bBoundingBoxOnly);
		void Initialise(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange, const bool bBoundingBoxOnly);		
		void Initialise(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange, const bool bBoundingBoxOnly, const u_int uFlagsToIgnore );
		void Initialise(const Asura_Vector_3& xOrigin, const Asura_Vector_3& xDirection, const Asura_Guid uEntityToIgnore, const float fMaxRange, const bool bBoundingBoxOnly, const u_int uFlagsToIgnore, const u_int uFlagsToTestFor );

		void ResetBoneScale(void)						{ m_bUseBoneScale = false; }
		void SetBoneScale(const Asura_Vector_3& xScale)	{ m_bUseBoneScale = true; m_xBoneScale = xScale; }

		void SetIncludeBackfacingTris( bool bInclude ) { m_bIncludeBackfacing = bInclude; }
		bool GetIncludeBackfacingTris( void ) const	{ return m_bIncludeBackfacing; }

		Asura_LOS_InputData& operator=( const Asura_LOS_InputData& xRhs ) { m_xOrigin = xRhs.m_xOrigin; m_xDirection = xRhs.m_xDirection; m_xBoneScale = xRhs.m_xBoneScale; m_uEntityToIgnore = xRhs.m_uEntityToIgnore; m_fInitialMaximumRange = xRhs.m_fInitialMaximumRange; m_uFlags = xRhs.m_uFlags; m_uPolyFlagsToIgnore = xRhs.m_uPolyFlagsToIgnore; m_uPolyFlagsToTestFor = xRhs.m_uPolyFlagsToTestFor; return *this; }

		Asura_Vector_3 m_xOrigin;
		Asura_Vector_3 m_xDirection;
		Asura_Vector_3 m_xBoneScale;
		Asura_Guid m_uEntityToIgnore;

		// MGH 5/2/03: Only used once at start of TestAgainstEverything() to
		// set m_fDistanceToIntersection, ignored everywhere else.
		float m_fInitialMaximumRange;   
		
		union
		{
			u_int	m_uFlags;		// Single access point for flag access.

			struct
			{
				u_int	m_bBoundingBoxOnly		: 1,	// Flag to allow bounding-box only test.
						m_bBoundingSphereOnly	: 1,	// Flag to allow bounding-sphere only test.
						m_bUseBoneScale			: 1,	// Flag to allow us to scale the bones.
						m_bIncludeBackfacing	: 1,	// Flag to allow tris facing away from the raytest to be reported.

												: 0;	// 29 spare-bits.
			};
		};

		u_int m_uPolyFlagsToIgnore;
		u_int m_uPolyFlagsToTestFor;
};

///////////////
// Asura_LOS //
///////////////
class Asura_LOS
{
public:
    // los test interface
	static const Asura_LOS_OutputData* TestAgainstEverything( const Asura_LOS_InputData* pxData );
	static const Asura_LOS_OutputData* TestAgainstEnvironment( const Asura_LOS_InputData* pxData );
	static const Asura_LOS_OutputData* TestAgainstEntities( const Asura_LOS_InputData* pxData );

    // los test count interface
    static unsigned int GetLOSTestCount()    { return s_uLOSTestCount; }
    static void         ResetLOSTestStats()  { s_uLOSTestCount = 0; }

	static inline u_int GetPolyFlagsToIgnore()  { return s_uPolyFlagsToIgnore; }
	static inline u_int GetPolyFlagsToTestFor()  { return s_uPolyFlagsToTestFor; }
	
    static void AddCallTrackingData( u_int uCallType, const char* szFilename, u_int uLine, const char* szExtraDescription );
    static void FlushCallTrackingData( void );

private:

	static Asura_LOS_OutputData s_xData;
    static unsigned int         s_uLOSTestCount;
	
	static unsigned int			s_uPolyFlagsToIgnore;
	static unsigned int			s_uPolyFlagsToTestFor;
	
#ifdef ASURA_SUPPORT_LOS_FUDGE
public:
    static void SetLOSFudge( const float fFudge ) { s_fLOSFudge = fFudge; }
    static void ClearLOSFudge() { s_fLOSFudge = 0.0f; }
        
    static float s_fLOSFudge;
#endif

	friend class Asura_LOS_Penetration;
};


// Optional LOS system call tracking.
#ifdef ASURA_LOS_TRACK_CALLS

    #ifndef ASURA_LOGFILES_ENABLED
        #error ASURA_LOS_TRACK_CALLS requires ASURA_LOGFILES_ENABLED to also be on, otherwise it doesnt do much
    #endif

    enum ASURA_LOS_CALLTYPE
    {
        ASURA_LOS_CALLTYPE_EVERYTHING             = 0x0000,
        ASURA_LOS_CALLTYPE_ENV                    = 0x0001,
        ASURA_LOS_CALLTYPE_ENTITIES               = 0x0002,

        ASURA_LOS_CALLTYPE_PENETRATION_EVERYTHING = 0x1000,
        ASURA_LOS_CALLTYPE_PENETRATION_ENV        = 0x1001,
        ASURA_LOS_CALLTYPE_PENETRATION_ENTITIES   = 0x1002,

        ASURA_LOS_CALLTYPE_FIRST_PROJECT_SPECIFIC = 0x2000,
    };

    #ifdef __SN_FILE__
        // Compact version provided by some SN tools (PS2 ones at least).
        #define ASURA_LOS_CURRENT_FILENAME __SN_FILE__
    #else
        #define ASURA_LOS_CURRENT_FILENAME __FILE__
    #endif

    #define ASURA_LOS_TRACK_CALL_WITH_EXTRA_DESC( call_type_, extra_desc_ ) \
        Asura_LOS::AddCallTrackingData( call_type_, ASURA_LOS_CURRENT_FILENAME, __LINE__, extra_desc_ )

    #define ASURA_LOS_TRACK_CALL( call_type_ ) \
        Asura_LOS::AddCallTrackingData( call_type_, ASURA_LOS_CURRENT_FILENAME, __LINE__, NULL )

#else

    #define ASURA_LOS_TRACK_CALL_WITH_EXTRA_DESC( call_type_, extra_desc ) \
        { }
    
    #define ASURA_LOS_TRACK_CALL( call_type_ ) \
        { }

#endif // #ifdef ASURA_LOS_TRACK_CALLS


#endif//__ASURA_LOS_H__
