//
// Asura_Physics_Pairing.h
//
// Created by KJFL - 16 August 2004
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_PHYSICS_PAIRING_H__
#define __ASURA_PHYSICS_PAIRING_H__

#include "Core/Asura_Mempool.h"

class Asura_Physics_RigidBodyInstance;
class Asura_AVector3;

#include "Core/Asura_Hash.h"

template <class T> class Asura_MempoolHashListTemplate_Node
{
	public:
		Asura_Hash_ID							m_uHash;

		Asura_MempoolHashListTemplate_Node<T>*	m_pxNext;

		T										m_xData;
};

template <class T> class Asura_MempoolHashListTemplate
{
	public:
		Asura_MempoolHashListTemplate( u_int uNumberOfNodesPerMempool )
			: m_xMempool( uNumberOfNodesPerMempool, sizeof(Asura_MempoolHashListTemplate_Node<T>), __FILE__, __LINE__ )
		{
			m_pxHead = 0;
		}


		~Asura_MempoolHashListTemplate()
		{
			FreeAll();
		}

        void Reset()
        {
			// empty mempool and then reset it
			FreeAll();
			m_pxHead = 0;
		}

		// Add a new node if the hash value is unique, and return a pointer to the node data and set bCreated to be true
		// If hash isn't unique return the found node, but set the bCreated flag to be false.
		// If we run out of memory, return false
		T* AddNode( const Asura_Hash_ID uHash, bool& bNewNodeCreated )
		{
			// assume we haven't made a new node yet
			bNewNodeCreated = false;

			// search for a node of this hash value
			Asura_MempoolHashListTemplate_Node<T>* pxNode = m_pxHead;

			while (pxNode)
			{
				if ( uHash == pxNode->m_uHash )
				{
					// we already have a hash of this name
					// return a pointer to the actual data element
					return &(pxNode->m_xData);
				}
				pxNode = pxNode->m_pxNext;
			}

			// okay, it's a new node. Allocate some memory for it.
			pxNode = (Asura_MempoolHashListTemplate_Node<T>*) m_xMempool.Allocate();

			if (!pxNode) return 0;

			// set Hash value for node and add to head of list
			pxNode->m_uHash = uHash;
			pxNode->m_pxNext = m_pxHead;

			m_pxHead = pxNode;

			bNewNodeCreated = true;

			// return a pointer to the actual data element
			return &(m_pxHead->m_xData);
		}

		void RemoveBrokenParings(void)
		{
			Asura_MempoolHashListTemplate_Node<T>** ppxNextNode = &m_pxHead;

			// is there a non-null 'next pointer'?
			while(*ppxNextNode)
			{
				// let's get it
				Asura_MempoolHashListTemplate_Node<T>* pxNode = *ppxNextNode;

				bool bShouldDelete = pxNode->m_xData.IsBroken();
				
				if (bShouldDelete)
				{
					// make the previous 'next pointer' point to the node following this one
					*ppxNextNode = pxNode->m_pxNext;

					pxNode->m_xData.OnDeletion();

					// and release the memory
					m_xMempool.Deallocate( pxNode );
				}
				else
				{
					// move along to next node then
					ppxNextNode = &(pxNode->m_pxNext);
				}
			}
		}

		void DeleteNode( T* pxNodeData )
		{
			Asura_MempoolHashListTemplate_Node<T>** ppxNextNode = &m_pxHead;

			// is there a non-null 'next pointer'?
			while(*ppxNextNode)
			{
				// let's get it
				Asura_MempoolHashListTemplate_Node<T>* pxNode = *ppxNextNode;

				bool bShouldDelete = ( &(pxNode->m_xData) == pxNodeData );
				
				if (bShouldDelete)
				{
					// make the previous 'next pointer' point to the node following this one
					*ppxNextNode = pxNode->m_pxNext;

					pxNode->m_xData.OnDeletion();

					// and release the memory
					m_xMempool.Deallocate( pxNode );

					// our job here is done
					return;
				}
				else
				{
					// move along to next node then
					ppxNextNode = &(pxNode->m_pxNext);
				}
			}
		}

		void RemoveNodesUsingInstance( const Asura_Physics_RigidBodyInstance* pxInstance )
		{
			Asura_MempoolHashListTemplate_Node<T>** ppxNextNode = &m_pxHead;

			// is there a non-null 'next pointer'?
			while(*ppxNextNode)
			{
				// let's get it
				Asura_MempoolHashListTemplate_Node<T>* pxNode = *ppxNextNode;

				bool bShouldDelete = pxNode->m_xData.UsesInstance( pxInstance );
				
				if (bShouldDelete)
				{
					// make the previous 'next pointer' point to the node following this one
					*ppxNextNode = pxNode->m_pxNext;

					pxNode->m_xData.OnDeletion(true);

					// and release the memory
					m_xMempool.Deallocate( pxNode );
				}
				else
				{
					// move along to next node then
					ppxNextNode = &(pxNode->m_pxNext);
				}
			}
		}



	protected:

		void FreeAll( void )
		{
			Asura_MempoolHashListTemplate_Node<T>* pxNode = m_pxHead;

			while(pxNode)
			{
				Asura_MempoolHashListTemplate_Node<T>* pxNodeToDelete = pxNode;
				pxNode = pxNode->m_pxNext;

				pxNodeToDelete->m_xData.OnDeletion();

				m_xMempool.Deallocate( pxNodeToDelete );
			}
			m_xMempool.Reset();
		}

		Asura_ResizingMempool					m_xMempool;
		Asura_MempoolHashListTemplate_Node<T>*	m_pxHead;
};


// hash ID = ((16bit object A id)<<16) + (16bit object B id)
// limits to 65536 objects, but always unique ID etc.

class Asura_Physics_Pairing
{
	public:
		static void Initialise( void )
		{
			s_uNumberOfActivePairings = 0;
		}
		static void Reset( void )
		{
			s_uNumberOfActivePairings = 0;

			s_xPairingsList.Reset();
		}
		static void Shutdown( void )
		{
			// reset frees nodes
			s_xPairingsList.Reset();
		}

		static u_int GetNumberOfActivePairings(void)
		{
			return s_uNumberOfActivePairings;
		}
	
		// Returns a non-null pointer if the pairing is a valid one - 
		// which means that it either existed already, or has just been created.
		static Asura_Physics_Pairing* GetOrCreatePairing( Asura_Physics_RigidBodyInstance* pxInstanceA, Asura_Physics_RigidBodyInstance* pxInstanceB, bool bAssignIslands = true );

		static void RemovePairingsUsingInstance( const Asura_Physics_RigidBodyInstance* pxInstance )
		{
			s_xPairingsList.RemoveNodesUsingInstance( pxInstance );
		}



		static bool AreAnyInstancesPairedWithThisOneWideAwake( class Asura_Physics_RigidBodyInstance* pxInstance );


		static void RemoveBrokenParings( void );
		

	
		bool Update( void );

		static void Destroy( Asura_Physics_Pairing* pxPairing )
		{
			s_xPairingsList.DeleteNode( pxPairing );
		}
			
		void OnDeletion( bool bWakeObjects = false );

		bool UsesInstance( const Asura_Physics_RigidBodyInstance* pxInstance ) const ;

		bool IsBroken( void ) const;

		Asura_Physics_RigidBodyInstance* GetInstanceA(void) const { return m_pxInstanceA; }
		Asura_Physics_RigidBodyInstance* GetInstanceB(void) const { return m_pxInstanceB; }
					
	private:		
		#ifdef ASURA_USE_COLLISIONFX
		bool ShouldAddCollisionFXReport( void );
		void AddCollisionFXReport( const Asura_AVector3& xPosition, const Asura_AVector3& xCollisionAxis  );
		#endif //#ifdef ASURA_USE_COLLISIONFX
		
		void NotifyObjectsOfCollision( const Asura_AVector3& xAxis );

		void CalculateCollisionResponses( u_int uNumberOfContacts, const Asura_AVector3* pxContactsOnA, const Asura_AVector3* pxContactsOnB, const Asura_AVector3& xAxis );

		void CalculateCollisionResponses( Asura_Physics_RigidBodyInstance* m_pxDynamicInstance, Asura_Physics_RigidBodyInstance* m_pxStaticInstance, u_int uNumberOfContacts, const Asura_AVector3* pxContactsOnA, const Asura_AVector3& xAxis  );

		u_int FindContactPointsFromSupportFeatures(	Asura_AVector3* pxContactPointsForA, Asura_AVector3* pxContactPointsForB, const Asura_AVector3& xAxis );

#ifndef ASURA_PHYSICS_USE_OLD_SOLVER
		bool FindContactPoints_MPR( void );
		bool FindContactPoints_MPR_Concave( Asura_Physics_RigidBodyInstance* pxPolygonInstance, Asura_Physics_RigidBodyInstance* pxPrimitiveInstance );
#endif
		bool FindContactPoints_SweptOBBs( void );
		bool FindContactPoints_SweptOBBs2( void );
		bool FindContactPoints_SweptOBBVsSweptPolygons( Asura_Physics_RigidBodyInstance* pxOBBInstance, Asura_Physics_RigidBodyInstance* pxPolygonInstance );
		bool FindContactPoints_SweptOBBVsSweptSphere( Asura_Physics_RigidBodyInstance* pxOBBInstance, Asura_Physics_RigidBodyInstance* pxSphereInstance );
		bool FindContactPoints_SweptSphereVsSweptPolygons( Asura_Physics_RigidBodyInstance* pxSphereInstance, Asura_Physics_RigidBodyInstance* pxPolygonInstance );

		//void DestroyBrokenContacts( void );
		
		u_int								m_uLastUpdateStamp;
		u_int								m_uFXReportStamp;

		// object A id
		// object B id
		Asura_Physics_RigidBodyInstance*	m_pxInstanceA;
		Asura_Physics_RigidBodyInstance*	m_pxInstanceB;

		// pairing contacts list
		//Asura_Physics_Pairing_Contact*		m_pxContactsList;
		
		// handy mass information for the pairing - so we don't keep recalculating it
		// coeff = m1 * m2 / ( m1 + m2)
		float								m_fMassCoefficient;

		// last reject plane for separation etc. could be stored
			
		//Asura_Physics_Pairing_Contact* AddContact(void);

			
		static u_int												s_uNumberOfActivePairings;
		static Asura_MempoolHashListTemplate<Asura_Physics_Pairing> s_xPairingsList;
};

#include "Core/Asura_HashTree.h"

class Asura_Physics_PairingHashTree : public Asura_TemplateHashTree<Asura_Physics_Pairing>
{
	public:
};



#endif//__ASURA_PHYSICS_PAIRING_H__
