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

#include "Core/Asura.h"
#include "MeshProcessing/Tools_MeshAdjacency.h"

#include "Core/Memory/Asura_Mem.h"
#include "Core/Asura_Mempool.h"
#include "Debugging/Asura_Assert.h"
#include "Mesh/Asura_Mesh.h"
#include "Rendering/Asura_Module.h"



//////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------**
** Tools_MeshAdjacency **
**---------------------*/
Tools_MeshAdjacency::Tools_MeshAdjacency()
{
	NumberOfPolygons = 0;
	Polygons = 0;
	NumberOfVertices = 0;
	Vertices = 0;

	m_pxMempool = 0;

	m_bLooseVerticesValid = false;
	m_iNumLooseVertices = 0;
	m_piLooseVertices = 0;
}

Tools_MeshAdjacency::~Tools_MeshAdjacency()
{
	delete [] Polygons;
	delete [] Vertices;
	delete [] m_piLooseVertices;
	delete m_pxMempool;
}


void Tools_MeshAdjacency::Initialise(const Asura_Mesh* pxMesh)
{
	m_xMesh.Initialise(pxMesh);
}

void Tools_MeshAdjacency::Initialise(const Asura_CollisionMesh* pxCollMesh)
{
	m_xMesh.Initialise(pxCollMesh);
}


void Tools_MeshAdjacency::Invalidate()
{
	delete [] Polygons;
	Polygons = 0;
	delete [] Vertices;
	Vertices = 0;

	delete m_pxMempool;
	m_pxMempool = 0;

	delete [] m_piLooseVertices;
	m_piLooseVertices = 0;
	m_iNumLooseVertices = 0;
	m_bLooseVerticesValid = false;
}


const Tools_Polygon_Adjacency* Tools_MeshAdjacency::GetPolygonAdjacencies(int polygonIndex)
{
	//calculate adjacencies if they haven't been done yet
	if(!Polygons) CalculatePolygonAdjacencies();
	
	if(polygonIndex>=0 && polygonIndex<NumberOfPolygons)
	{
		return &Polygons[polygonIndex];
	}
	else
	{
		Asura_Assert(polygonIndex>=0 && polygonIndex<NumberOfPolygons,"Invalid polygon index for adjacencies");
		static Tools_Polygon_Adjacency s_xBlankAdjacency;
		return &s_xBlankAdjacency;
	}

}

const Tools_Polygon_Adjacency* Tools_MeshAdjacency::GetVertexAdjacencies(int vertexIndex)
{
	//calculate adjacencies if they haven't been done yet
	if(!Vertices) CalculateVertexAdjacencies();
	
	if(vertexIndex>=0 && vertexIndex<NumberOfVertices)
	{
		return &Vertices[vertexIndex];
	}
	else
	{
		Asura_Assert(vertexIndex>=0 && vertexIndex<NumberOfVertices,"Invalid vertex index for adjacencies");
		static Tools_Polygon_Adjacency s_xBlankAdjacency;
		return &s_xBlankAdjacency;
	}

}


void Tools_MeshAdjacency::CalculateVertexAdjacencies()
{
	//determine adjacencies within the module
	if(!Vertices)
	{
		CalculateInternalVertexAdjacencies();
	}
}

void Tools_MeshAdjacency::CalculatePolygonAdjacencies()
{
	if(!Polygons)
	{
		CalculateInternalPolygonAdjacencies();
	}
}


void Tools_MeshAdjacency::CalculateInternalVertexAdjacencies()
{
	if(Vertices) return;

	const int iModuleIndex = GetModuleIndex();
	const Asura_Hash_ID uZoneHash = GetZoneHash();


	NumberOfVertices = m_xMesh.GetNumberOfVertices();
	Vertices = new Tools_Polygon_Adjacency[NumberOfVertices];
	for(int i=0;i<NumberOfVertices;i++)
	{
		Vertices[i].Initialise(this);
	}

	
//	if(!m_pxMesh->NormalsDataValid) m_pxMesh->CreateNormals();


	
	//Determine the polygons attached to each vertex
	int iNumPolys = m_xMesh.GetNumberOfPolygons();
	for(int i=0;i<iNumPolys;i++)
	{
		int iNumPolyVerts = m_xMesh.GetNumberOfVertsInPoly(i);

		for(int j=0;j<iNumPolyVerts;j++)
		{
			//add this polygon to the list for this vertex
			int vertex = m_xMesh.GetVertexIndex(i,j);
			Vertices[vertex].AddAdjacency(uZoneHash,iModuleIndex,i);
		}
	}


}

void Tools_MeshAdjacency::CalculateInternalPolygonAdjacencies()
{
	if(Polygons) return;

	//vertex adjacencies are needed to calculate polygon adjacencies
	if(!Vertices)
	{
		CalculateInternalVertexAdjacencies();
	}

	const int iModuleIndex = GetModuleIndex();
	const Asura_Hash_ID uZoneHash = GetZoneHash();


	NumberOfPolygons = m_xMesh.GetNumberOfPolygons();
	Polygons = new Tools_Polygon_Adjacency[NumberOfPolygons];

	for(int i=0;i<NumberOfPolygons;i++)
	{
		Polygons[i].Initialise(this);		
	}
	
//	if(!m_pxMesh->NormalsDataValid) m_pxMesh->CreateNormals();
#if !defined( TOOLS_USE_OLD_ADJACENCY_CODE )
	// KJFL - 18 October 2007
	// Turn the test on it's head, and loop through the vertices instead of the polys.
	// Seems to be twice as fast on the biggest modules, and makes a lot of sense cache-wise.
	for (int i=0; i<NumberOfVertices; ++i)
	{
		const Tools_Polygon_Adjacency* pxVertAdj = &Vertices[i];

		// go through the polys attached to this vertex, and make pairwise adjacencies
		const Tools_Adjacent_Polygon* pxAdjPoly = pxVertAdj->GetFirstAdjacency();

		while ( pxAdjPoly )
		{
			const int iPolyIndex1 = pxAdjPoly->m_iPolygonIndex;

			// KJFL - 18 October 2007 - tried two approaches here - the currently used one
			// of looping through the list for each poly, and only adding adjacencies to one 
			// poly at a time, and only looping over the remaining polys and adding pairs
			// of adjacent polys. Didn't seem to be much of a speed win either way. 
			// Possibly slightly better caching when doing one poly's adjacencies at a time?

			const Tools_Adjacent_Polygon* pxAdjPoly2 = pxVertAdj->GetFirstAdjacency();
			//	const Tools_Adjacent_Polygon* pxAdjPoly2 = pxAdjPoly->m_pxNext;

			while ( pxAdjPoly2 )
			{
				const int iPolyIndex2 = pxAdjPoly2->m_iPolygonIndex;

				if ( iPolyIndex1 != iPolyIndex2 )
				{
					Polygons[iPolyIndex1].AddAdjacency(uZoneHash,iModuleIndex,iPolyIndex2);
		//			Polygons[iPolyIndex2].AddAdjacency(uZoneHash,iModuleIndex,iPolyIndex1);
				}

				pxAdjPoly2 = pxAdjPoly2->m_pxNext;
			}

			pxAdjPoly = pxAdjPoly->m_pxNext;
		}
	}

#else // original polycentric approach

	//For each polygon determine all adjacent polygons
	for(int i=0;i<NumberOfPolygons;i++)
	{
		//look at each vertex used by this polygon , and find the polygons using it

		int iNumVerts = m_xMesh.GetNumberOfVertsInPoly(i);
		for(int j=0;j<iNumVerts;j++)
		{
			int vertex = m_xMesh.GetVertexIndex(i,j);
			const Tools_Polygon_Adjacency* pxVertAdj = &Vertices[vertex];

			for(int k=0;k<pxVertAdj->GetNumAdjacentPolygons();k++)
			{
				int iPolyIndex = pxVertAdj->GetPolygonIndex(k);
				if(iPolyIndex == i) continue;//don't want to count a polygon as adjacent to itself
				Asura_Assert(iPolyIndex<NumberOfPolygons,"");
				Polygons[i].AddAdjacency(uZoneHash,iModuleIndex,iPolyIndex);
			}
		}
	}
#endif
}

void Tools_MeshAdjacency::CalculateLooseVertices()
{
	Asura_Assert(!m_bLooseVerticesValid,"");

	int iNumVertices = m_xMesh.GetNumberOfVertices();
	bool* pbVertexUsed = new bool[iNumVertices];
	Asura_Mem::Set(pbVertexUsed,0,sizeof(bool)*iNumVertices);

	//Go over the polygons and flag all the vertices that are used
	//and determine how many are unused
	m_iNumLooseVertices = iNumVertices;

	int iNumPolys = m_xMesh.GetNumberOfPolygons();
	for(int i=0;i<iNumPolys;i++)
	{
		int iVertsInPoly = m_xMesh.GetNumberOfVertsInPoly(i);
		for(int j=0;j<iVertsInPoly;j++)
		{
			int iVertIndex = m_xMesh.GetVertexIndex(i,j);
			if(!pbVertexUsed[iVertIndex])
			{
				pbVertexUsed[iVertIndex] = true;
				m_iNumLooseVertices--;
			}
		}
	}

	if(m_iNumLooseVertices)
	{
		//allocate space for loose vertices , and go through the used vertex array extracting the unused ones
		m_piLooseVertices = new int[m_iNumLooseVertices];
		int iPos = 0;
		for(int i=0;i<iNumVertices;i++)
		{
			if(!pbVertexUsed[i])
			{
				m_piLooseVertices[iPos++] = i;
			}
		}
		Asura_Assert(iPos == m_iNumLooseVertices,"");
	}

	delete [] pbVertexUsed;
	m_bLooseVerticesValid = true;
}

Tools_Adjacent_Polygon* Tools_MeshAdjacency::AllocateAdjacency()
{
	if(!m_pxMempool) m_pxMempool = new Asura_ResizingMempool(4096,sizeof(Tools_Adjacent_Polygon),__FILE__,__LINE__);
	return (Tools_Adjacent_Polygon*) m_pxMempool->Allocate();
}


//////////////////////////////////////////////////////////////////////////////////////////////////////
/*------------------------**
** Tools_Polygon_Adjacency **
**------------------------*/

Tools_Polygon_Adjacency::Tools_Polygon_Adjacency()
{
	m_pxMeshAdj = 0;

	m_pxFirstAdj = 0;
	m_iNumberOfAdjacentPolygons = 0;
}

Tools_Polygon_Adjacency::~Tools_Polygon_Adjacency()
{
}

void Tools_Polygon_Adjacency::AddAdjacency(Asura_Hash_ID uZoneHash,int iModuleIndex,int iPolygonIndex)
{
#if !defined( TOOLS_USE_OLD_ADJACENCY_CODE )
	{
		// check for an existing entry, using list pointers directly - much faster
		Tools_Adjacent_Polygon* pxAdj = m_pxFirstAdj;

		while ( pxAdj )
		{
			// most usually adding polys from the same module, so check poly index first
			if(	pxAdj->m_iPolygonIndex==iPolygonIndex
	#ifdef ASURA_USE_STREAMING
				&& pxAdj->m_uZoneHash==uZoneHash
	#endif
				&& pxAdj->m_sModuleIndex==(short)iModuleIndex )
			{
				//increase the number of shared vertices
				pxAdj->m_usSharedVertices++;
				return;
			}

			pxAdj = pxAdj->m_pxNext;
		}
	}
#else
	//check for an existing entry
	for(int i=0;i<m_iNumberOfAdjacentPolygons;i++)
	{
		Tools_Adjacent_Polygon* pxAdj = GetAdjacency(i);
		
		if(	pxAdj->m_sModuleIndex==(short)iModuleIndex
			#ifdef ASURA_USE_STREAMING
			&& pxAdj->m_uZoneHash==uZoneHash
			#endif
			&& pxAdj->m_iPolygonIndex==iPolygonIndex)
		{
			//increase the number of shared vertices
			pxAdj->m_usSharedVertices++;
			return;
		}
	}
#endif

	//insert new entry at the start
	Tools_Adjacent_Polygon* pxAdj = m_pxMeshAdj->AllocateAdjacency();
	pxAdj->m_pxNext = m_pxFirstAdj;
	m_pxFirstAdj = pxAdj;
	
	
	#ifdef ASURA_USE_STREAMING
	pxAdj->m_uZoneHash = uZoneHash;
	#endif
	pxAdj->m_sModuleIndex = (short)iModuleIndex;
	pxAdj->m_iPolygonIndex = iPolygonIndex;
	pxAdj->m_usSharedVertices = 1;
	pxAdj->m_bBetweenZones = (uZoneHash != GetZoneHash());
	pxAdj->m_bBetweenModules = pxAdj->m_bBetweenZones || (iModuleIndex != GetModuleIndex());
	m_iNumberOfAdjacentPolygons++;


}

u_int Tools_Polygon_Adjacency::GetNumSharedVertices(Asura_Hash_ID uZoneHash, int iModuleIdx, int iPolyIdx ) const
{
    u_int uCount = 0;
    
    for(const Tools_Adjacent_Polygon* pxAdj = m_pxFirstAdj; pxAdj; pxAdj = pxAdj->m_pxNext)
    {
        if( pxAdj->m_sModuleIndex == (short)iModuleIdx 
			#ifdef ASURA_USE_STREAMING
			&& pxAdj->m_uZoneHash == uZoneHash 
			#endif
			&& pxAdj->m_iPolygonIndex == iPolyIdx )
        {
            uCount = pxAdj->m_usSharedVertices;
            break;
        }
    }

    return uCount;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////
void Tools_RenderOrCollMesh::GetCollisionMeshFromHandler() const
{
	Asura_Assert(m_pxModHandler,"");
	m_pxCollMesh = m_pxModHandler->GetCollisionMeshForModule(m_iModuleIndex);
	Asura_Assert(m_pxCollMesh,"");
}
