//
// Asura_Physics_EnvironmentCache.h
//
// Created by KJFL - 2004-03-24 15:03:58
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif

#ifndef __ASURA_PHYSICS_ENVIRONMENTCACHE_H__
#define __ASURA_PHYSICS_ENVIRONMENTCACHE_H__

#include "Core/Asura_Maths.h"
#include "Mesh/Asura_Mesh.h" // for the mesh flags, unfortunately

class Asura_Physics_EnvironmentCache_Face;
class Asura_Physics_EnvironmentCache_Edge;
class Asura_Physics_EnvironmentCache_Vertex;

class Asura_Physics_Contact;

//! This class acts as a buffer for environment vertices, faces and edges around an object.
//! The idea is to find the environment features inside a bounding box which is bigger than
//! the bounding box made by considering the object's movement, and then updating those features
//! only when the object's movement bounding box leaves the cache bounding box. This means we don't
//! have to search the environment every frame/iteration. In theory we could also shrink the cache
//! if the object stops moving etc.
//!
//! Inside the cache we can also contain extra data such as contact details (e.g. this vertex is
//! already in contact with the object) and information about previous rejection tests (e.g. if a
//! vertex is not intersecting with an object because of a plane test against an object face, 
//! remember that so next time we can test that face first, thus making rejection quicker).
//!
class Asura_Physics_EnvironmentCache
{
	public:
		Asura_Physics_EnvironmentCache()
		{
			m_pxFaceList = 0;
			m_pxEdgeList = 0;
			m_pxVertexList = 0;

            Reset();

#if defined(ASURA_USE_STREAMING)
			// Add to linked list.
			m_pxPrev = 0;
			m_pxNext = s_pxEnvironmentCacheList;
			if (m_pxNext)
			{
				m_pxNext->m_pxPrev = this;
			}
			s_pxEnvironmentCacheList = this;
#endif //defined(ASURA_USE_STREAMING)
		}

		~Asura_Physics_EnvironmentCache()
		{
			Reset();

#if defined(ASURA_USE_STREAMING)
			// Remove from linked list.
			if (m_pxNext)
			{
				m_pxNext->m_pxPrev = m_pxPrev;
			}
			if (m_pxPrev)
			{
				m_pxPrev->m_pxNext = m_pxNext;
			}
			if (s_pxEnvironmentCacheList == this)
			{
				s_pxEnvironmentCacheList = m_pxNext;
			}
#endif //defined(ASURA_USE_STREAMING)
		}

		void Reset( void );
        void Flush( void ); // Similar to Reset() but only dumps internally accumulated data, not settings such as which flags should be ignored.

		void Update( const Asura_Bounding_Box& xObjectBB, const float fBBExpansionMargin = 0.0f );
		void Update( const Asura_Bounding_Box& xObjectBB, const float fDisp, const Asura_AVector3& xDirection );

#ifdef ASURA_TOOLS
        u_short GetFlagsOfPolysToIgnore() const { return static_cast< u_short >( m_uFlagsOfPolysToIgnore ); }
        void    SetFlagsOfPolysToIgnore( const u_short usFlagsOfPolysToIgnore ) { m_uFlagsOfPolysToIgnore = usFlagsOfPolysToIgnore; }

        u_int Tools_GetFlagsOfPolysToIgnore() const { return m_uFlagsOfPolysToIgnore; }
        void  Tools_SetFlagsOfPolysToIgnore( const u_int uFlagsOfPolysToIgnore ) { m_uFlagsOfPolysToIgnore = uFlagsOfPolysToIgnore; }
#else
        u_short GetFlagsOfPolysToIgnore() const { return m_usFlagsOfPolysToIgnore; }
        void    SetFlagsOfPolysToIgnore( const u_short usFlagsOfPolysToIgnore ) { m_usFlagsOfPolysToIgnore = usFlagsOfPolysToIgnore; }
#endif

		bool GetFacesOnly( void ) const
		{
			return m_bFacesOnly;
		}

		void SetFacesOnly( const bool bFaces )
		{
			m_bFacesOnly = bFaces;
		}

		Asura_Physics_EnvironmentCache_Face* GetFaceList( void ) const
		{
			return m_pxFaceList;
		}

		Asura_Physics_EnvironmentCache_Edge* GetEdgeList( void ) const 
		{
			return m_pxEdgeList;
		}

		Asura_Physics_EnvironmentCache_Vertex* GetVertexList( void ) const
		{
			return m_pxVertexList;
		}

		const Asura_Bounding_Box& GetCacheBoundingBox( void ) const
		{
			return m_xCacheBoundingBox;
		}

		void AddFaceToCache( u_int uFaceID );
		void AddEdgeToCache( u_int uVertex0Index, u_int uVertex1Index );
		void AddVertexToCache( u_int uVertexID );

#if defined(ASURA_USE_STREAMING)
		// MGH 10/4/2006: Zone has been unloaded. Clean up any remaining references to it
		// in all the environment caches. NOTE: clean up contacts before calling this function.
		static void RemoveZone(const u_int uZoneIndex);
#endif //defined(ASURA_USE_STREAMING)

		static u_int							s_uNumberOfCacheUpdates;

	private:

		void PruneCache( void );

#if defined(ASURA_USE_STREAMING)
		void RemoveZoneFromInstance(const u_int uZoneIndex);

		// Head of linked list of environment caches.
		static Asura_Physics_EnvironmentCache*  s_pxEnvironmentCacheList;

		Asura_Physics_EnvironmentCache*			m_pxPrev; // Previous in linked list.
		Asura_Physics_EnvironmentCache*			m_pxNext; // Next in linked list.
#endif //defined(ASURA_USE_STREAMING)

		Asura_Bounding_Box						m_xCacheBoundingBox;
		
		Asura_Physics_EnvironmentCache_Face*	m_pxFaceList;
		Asura_Physics_EnvironmentCache_Edge*	m_pxEdgeList;
		Asura_Physics_EnvironmentCache_Vertex*	m_pxVertexList;

#ifdef ASURA_ENVCACHE_DEBUG
		int m_iNumFaces;
		int m_iNumEdges;
		int m_iNumVerts;
#endif

#ifdef ASURA_TOOLS
        u_int                                   m_uFlagsOfPolysToIgnore;
#else
        u_short									m_usFlagsOfPolysToIgnore;
#endif
		u_short									m_bFacesOnly :1, :0;
};

class Asura_Physics_EnvironmentCache_Face
{
	public:

		static Asura_Physics_EnvironmentCache_Face* Create( void );

		static void Destroy( Asura_Physics_EnvironmentCache_Face* pxFace );

		u_int					m_uFaceID;
		
		// object vertex - environment face contacts are tracked by the object vertices,
		// so no contact ptr here
		// KJFL - 05 September 2006 - added them now for sphere collisions. Probably will refactor 
		// other code to eliminate duplication
		Asura_Physics_Contact*	m_pxContact;
		
		u_int					m_uLastSeparationPlane;

		Asura_Physics_EnvironmentCache_Face* m_pxNext;

	private:

		Asura_Physics_EnvironmentCache_Face(){}
		~Asura_Physics_EnvironmentCache_Face(){}
};

class Asura_Physics_EnvironmentCache_Edge
{
	public:

		static Asura_Physics_EnvironmentCache_Edge* Create( void );

		static void Destroy( Asura_Physics_EnvironmentCache_Edge* pxEdge );

		u_int					m_uEdgeVertex0ID;
		u_int					m_uEdgeVertex1ID;

		Asura_Physics_Contact*	m_pxContact;

		Asura_Physics_EnvironmentCache_Edge* m_pxNext;

	private:

		Asura_Physics_EnvironmentCache_Edge(){}
		~Asura_Physics_EnvironmentCache_Edge(){}
};

class Asura_Physics_EnvironmentCache_Vertex
{
	public:

		static Asura_Physics_EnvironmentCache_Vertex* Create( void );

		static void Destroy( Asura_Physics_EnvironmentCache_Vertex* pxVertex );

		u_int					m_uVertexID;
	
		u_int					m_uObjectFaceLastRejectedBy;

		Asura_Physics_Contact*	m_pxContact;

		Asura_Physics_EnvironmentCache_Vertex* m_pxNext;


	private:

		Asura_Physics_EnvironmentCache_Vertex(){}
		~Asura_Physics_EnvironmentCache_Vertex(){}
};


#endif//__ASURA_PHYSICS_ENVIRONMENTCACHE_H__
