//
// Tools_MeshAdjacency.h
//
// Created by Richard Rice - 03/11/2003
// 
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __TOOLS_MESHADJACENCY_H__
#define __TOOLS_MESHADJACENCY_H__

#include "Core/Asura_Hash.h"

#include "Mesh/Asura_Mesh.h"
#include "Mesh/Asura_CollisionMesh.h"


class Tools_Polygon_Adjacency;
class Asura_ResizingMempool;
class Tools_Adjacent_Polygon;
class Asura_Module_Handler;

//Used by the adjacency code to allow either Asura_Mesh or Asura_CollisionMesh to be used
class Tools_RenderOrCollMesh
{
public :
	Tools_RenderOrCollMesh()
	{
		m_bCollisionMesh = false;
		m_pxRenderMesh=0;
		m_pxCollMesh=0;
		m_pxModHandler = 0;
		m_iModuleIndex = 0;
	}

	void Initialise(const Asura_Mesh* pxRenderMesh) {m_pxRenderMesh = pxRenderMesh;}
	void Initialise(const Asura_CollisionMesh* pxCollMesh) {m_pxCollMesh = pxCollMesh; m_bCollisionMesh = true;}
	//Collision mesh isn't initialised with the mesh immediately to avoid it having to force it
	//to be made valid in the module system too early
	void Initialise_CollMesh(Asura_Module_Handler* pxModHandler, int iModuleIndex) 
	{
		m_pxModHandler = pxModHandler;
		m_iModuleIndex = iModuleIndex;
		m_pxCollMesh = 0;
		m_bCollisionMesh = true;
	}


	//various functions that get polygon data from the type of mesh being used (coll mesh or render mesh)
	int GetNumberOfVertices() const
	{
		return m_pxRenderMesh ? m_pxRenderMesh->NumberOfVertices : GetCollisionMesh()->GetNumberOfVertices();
	}

	int GetNumberOfPolygons() const
	{
		return m_pxRenderMesh ? m_pxRenderMesh->NumberOfPolygons : GetCollisionMesh()->GetNumberOfPolys();
	}
	
	int GetNumberOfVertsInPoly(int iPoly) const
	{
		return m_pxRenderMesh ? m_pxRenderMesh->PolygonDataPtr[iPoly].NumberOfVertices() : GetCollisionMesh()->GetNumVerticesInPoly(iPoly);
	}
	
	int GetVertexIndex(int iPoly,int iVertInPoly) const
	{
		return m_pxRenderMesh ? m_pxRenderMesh->PolygonDataPtr[iPoly].VertexID[iVertInPoly] : GetCollisionMesh()->GetVertexIndex(iPoly,iVertInPoly);
	}

	const Asura_Bounding_Box& GetBoundingBox() const
	{
		return m_pxRenderMesh ? m_pxRenderMesh->GetBoundingBox() : GetCollisionMesh()->GetBoundingBox();
	}

	const Asura_Vector_3& GetVertex(int iVert) const
	{
		return m_pxRenderMesh ? m_pxRenderMesh->VertexDataPtr[iVert].Position : GetCollisionMesh()->GetVertex(iVert);
	}


	bool IsValid() const {return m_pxRenderMesh || m_bCollisionMesh;}

	bool IsCollMesh() const {return m_bCollisionMesh;}

private :
	const Asura_CollisionMesh* GetCollisionMesh() const
	{
		if(!m_pxCollMesh) GetCollisionMeshFromHandler();
		return m_pxCollMesh;
	}
	
	void GetCollisionMeshFromHandler() const;


	bool m_bCollisionMesh;

	const Asura_Mesh* m_pxRenderMesh;
	mutable const Asura_CollisionMesh* m_pxCollMesh;

	//for fetching the collision mesh when it is first needed
	Asura_Module_Handler* m_pxModHandler;
	int m_iModuleIndex;
};


//class for caculating all the internal polygon-to-polygon and vertex-to-polygon adjacencies 
//in a mesh
class Tools_MeshAdjacency
{
public :
	Tools_MeshAdjacency();
	virtual ~Tools_MeshAdjacency();

	//Only one of these should be used ,depending on the type of adjacencies needed
	void Initialise(const Asura_Mesh* pxMesh);
	void Initialise(const Asura_CollisionMesh* pxCollMesh);

	void Invalidate();

	const Tools_Polygon_Adjacency* GetPolygonAdjacencies(int polygonIndex);
	const Tools_Polygon_Adjacency* GetVertexAdjacencies(int vertexIndex);

	//Gets vertices in the mesh that aren't used by any polygons.
	int GetNumLooseVertices()
	{
		if(!m_bLooseVerticesValid) CalculateLooseVertices();
		return m_iNumLooseVertices;
	}
	int GetLooseVertex(int iIndex) const
	{
		if(!m_bLooseVerticesValid)
		{
			Asura_Assert(0,"Loose vertices not calculated");
			return 0;
		}
		else if(iIndex<0 || iIndex >= m_iNumLooseVertices)
		{
			Asura_Assert(0,"Invalid index into loose vertices array");
			return 0;
		}
		return m_piLooseVertices[iIndex];
	}

protected :

	Tools_Adjacent_Polygon* AllocateAdjacency();
	

	virtual void CalculateVertexAdjacencies();
	virtual void CalculatePolygonAdjacencies();

	void CalculateInternalVertexAdjacencies();
	void CalculateInternalPolygonAdjacencies();

	void CalculateLooseVertices();

	virtual int GetModuleIndex() const{return -1;}
	virtual Asura_Hash_ID GetZoneHash() const {return 0;}

	Tools_RenderOrCollMesh m_xMesh;

	//mem pool for storing the adjacencies
	Asura_ResizingMempool* m_pxMempool;


	int NumberOfPolygons;
	Tools_Polygon_Adjacency* Polygons;

	int NumberOfVertices;
	Tools_Polygon_Adjacency* Vertices;

	bool m_bLooseVerticesValid;
	int m_iNumLooseVertices;
	int* m_piLooseVertices;

	friend class Tools_Polygon_Adjacency;
};


class Tools_Adjacent_Polygon
{
	friend class Tools_Polygon_Adjacency;
	friend class Tools_MeshAdjacency;
public :
	//returns true if adjacency is to a polygon in another streaming zone
	bool IsBetweenZones() const {return m_bBetweenZones;}
	//returns true if adjacency is to a polygon in another module
	bool IsBetweenModules() const {return m_bBetweenModules;}

	#ifdef ASURA_USE_STREAMING
	Asura_Hash_ID GetZoneHash(int iIndex) const {return m_uZoneHash;}
	#else
	Asura_Hash_ID GetZoneHash(int iIndex) const {return 0;}
	#endif
	int GetModuleIndex() const {return m_sModuleIndex;}
	int GetPolygonIndex() const {return m_iPolygonIndex;}
	int GetNumSharedVertices() const {return m_usSharedVertices;}

	const Tools_Adjacent_Polygon* GetNext() const {return m_pxNext;}

private :

	#ifdef ASURA_USE_STREAMING
	Asura_Hash_ID m_uZoneHash;
	#endif
	short m_sModuleIndex;
	u_short m_usSharedVertices:14;
	u_short m_bBetweenZones:1;
	u_short m_bBetweenModules:1;
	int m_iPolygonIndex;
	Tools_Adjacent_Polygon* m_pxNext;
};

class Tools_Polygon_Adjacency
{
public : 
	Tools_Polygon_Adjacency();
	~Tools_Polygon_Adjacency();


	int GetNumAdjacentPolygons() const { return m_iNumberOfAdjacentPolygons;}

	//Gets the first in the linked list of adjacent polygons
	const Tools_Adjacent_Polygon* GetFirstAdjacency( void ) const { return m_pxFirstAdj; } 


    //Check shared vertices with polygon in module in same zone
    u_int GetNumSharedVertices( int iModuleIdx, int iPolyIdx ) const
		{return GetNumSharedVertices(GetZoneHash(),iModuleIdx,iPolyIdx);}
    bool Contains( int iModuleIdx, int iPolyIdx ) const
		{ return( GetNumSharedVertices(GetZoneHash(),iModuleIdx, iPolyIdx) > 0 ); }

    //Check shared vertices with polygon in module in any zone
    u_int GetNumSharedVertices(Asura_Hash_ID uZoneHash, int iModuleIdx, int iPolyIdx ) const;
    bool Contains( Asura_Hash_ID uZoneHash, int iModuleIdx, int iPolyIdx ) const
		{ return( GetNumSharedVertices(uZoneHash,iModuleIdx, iPolyIdx) > 0 ); }



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

	void Initialise(Tools_MeshAdjacency* pxMeshAdj)
	{
		m_pxMeshAdj = pxMeshAdj;
	}
	void AddAdjacency(Asura_Hash_ID uZoneHash,int iModuleIndex,int iPolygonIndex);

	Asura_Hash_ID GetZoneHash() const {return m_pxMeshAdj->GetZoneHash();}
	int GetModuleIndex() const {return m_pxMeshAdj->GetModuleIndex();}
	
private :
	Tools_Adjacent_Polygon* GetAdjacency(int iIndex)
	{
		Asura_Assert(iIndex<m_iNumberOfAdjacentPolygons,"");
		
		
		Tools_Adjacent_Polygon* pxAdj = m_pxFirstAdj;
		for(int i=0;i<iIndex;i++)
		{
			pxAdj = pxAdj->m_pxNext;
		}

		return pxAdj;
	}

	const Tools_Adjacent_Polygon* GetAdjacency(int iIndex) const
	{
		return ((Tools_Polygon_Adjacency*)this)->GetAdjacency(iIndex);
	}

	Tools_MeshAdjacency* m_pxMeshAdj;

	Tools_Adjacent_Polygon* m_pxFirstAdj;
	int m_iNumberOfAdjacentPolygons;

};





#endif //__TOOLS_MESHADJACENCY_H__
