//
// Tools_MultiElementSkin_Processor.h
//
// Created by KJL - 28/10/2003 12:43:18
//
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
	#pragma once
#endif

#ifndef __TOOLS_MULTIELEMENTSKIN_PROCESSOR_H__
#define __TOOLS_MULTIELEMENTSKIN_PROCESSOR_H__

#include "Core/Asura_Maths.h"

// KJL - 04/11/2003 12:42:32 - External systems only need to worry about the interfaces given by the first two classes
// in this file: 'Tools_MultiElementSkin_Processor' and 'Tools_MultiElementSkin_Element'.

class Asura_Mesh;
class Asura_Hierarchy_Skin;
class Tools_MultiElementSkin_Element;
class Tools_MultiElementSkin_VertexAdjacencies;
class Tools_MultiElementSkin_Vertex;
class Tools_MultiElementSkin_Stripified;

class Tools_MultiElementSkin_Processor
{
	public:
		static bool Initialise(const Asura_Mesh* pxMesh, const Asura_Hierarchy_Skin* pxSkin, const bool bRenumberBones);

		static const Tools_MultiElementSkin_Element* GetFirstElement(void);

		static const Tools_MultiElementSkin_Stripified* GetStripifiedSkin(void);

		// KJL - 10/11/2003 17:03:43 - function to create new skins and meshes for each element
		static void MakeNewMeshesAndSkins(void);

		static void Shutdown(void);

	private:

		static void MergeSmallElements(void);
		static Tools_MultiElementSkin_Element* FindBestElementToMergeTo(Tools_MultiElementSkin_Element* pxElement);
		static void DeleteElement(Tools_MultiElementSkin_Element* pxElement);
		static void AddNewElement(const u_int uFirstPolyInElement);


		static u_int*										s_puElementArray;
		static const Asura_Mesh*							s_pxMesh;
		static const Asura_Hierarchy_Skin*					s_pxSkin;
		static Tools_MultiElementSkin_Element*				s_pxFirstElement;
		static Tools_MultiElementSkin_VertexAdjacencies*	s_pxVertexAdjacencies;
		static Asura_Vector_3*								s_pxFaceNormal;
		static u_int										s_uNumberOfElements;
		static Tools_MultiElementSkin_Stripified*			s_pxStripifiedSkin;

		static bool											s_bInitialised;
};


class Tools_MultiElementSkin_Element
{
	public:

		// KJL - 04/11/2003 12:14:18 - access methods for external systems.
		// These functions return triangle list data and bone information.

		u_int GetNumberOfTriangles(void) const
		{
			return m_uNumberOfTriangles;
		}

		u_int GetNumberOfVertices(void) const
		{
			return m_uNumberOfVertices;
		}

		const Tools_MultiElementSkin_Vertex* GetVertices(void) const
		{
			return m_pxVertices;
		}

		u_int GetNumberOfIndices(void) const 
		{
			return m_uNumberOfTriangles*3;
		}

		const u_int* GetIndices(void) const
		{
			return m_puTriangleIndices;
		}
	
		int GetMaterialID(void) const
		{
			return m_iMaterialID;
		}

		u_int GetNumberOfUsedBones(void) const
		{
			return m_uNumberOfBonesUsed;
		}

		const u_int* GetBoneConversionArray(void) const
		{
			return m_puBoneConversionArray;
		}


		const Tools_MultiElementSkin_Element* GetNextElement(void) const
		{
			return m_pxNextElement;
		}


	private:
		// KJL - 04/11/2003 12:14:23 - internal methods used in construction of an element

		friend class Tools_MultiElementSkin_Processor;
		
		Tools_MultiElementSkin_Element(u_int uNumberOfTriangles, Tools_MultiElementSkin_Element* pxNextElement);
		
		~Tools_MultiElementSkin_Element();
	
		u_int AddVertex(const Tools_MultiElementSkin_Vertex& xVertex);
	
		void AddTriangle(const u_int u0, const u_int u1, const u_int u2);

					
		void MergeInElement(const Tools_MultiElementSkin_Element* pxElement);
	
		void CalculateBoneUsage(const u_int uMaxNumberOfBones);
		void ConvertBoneIndices(const u_int uMaxNumberOfBones);
			
		int								m_iMaterialID;			// Potentially allow different materials per element

		u_int							m_uNumberOfVertices;	
		u_int							m_uNumberOfTriangles;
		//u_int							m_uNumberOfIndices;     

		
		Tools_MultiElementSkin_Vertex*	m_pxVertices;			// Simple array of all the vertices in the element
		u_int*							m_puTriangleIndices;	// Array of indices into vertex array, with three indices for each triangle.
																// e.g. the n triangles are represented by n*3 indices: (i0,i1,i2), (i3,i4,i5), (i6,i7,i8) ... 
		u_int							m_uNumberOfBonesUsed;
		bool*							m_pbUsedBones;
		u_int*							m_puBoneConversionArray;

		Tools_MultiElementSkin_Element* m_pxPrevElement;
		Tools_MultiElementSkin_Element* m_pxNextElement;
};







// KJL - 04/11/2003 12:43:17 - classes after this are internal to the Processor system.


class Tools_MultiElementSkin_Vertex
{
	public:
	
		Asura_Vector_3	m_xPosition;
		Asura_Vector_3	m_xNormal;

		float			m_fU;
		float			m_fV;

		u_int			m_acIndex[4];
		float			m_afWeight[4];


		inline bool operator == ( const class Tools_MultiElementSkin_Vertex& xVertex) const
		{
			if (	(m_xPosition	== xVertex.m_xPosition)
				&&	(m_xNormal.ApproximatelyEquals(xVertex.m_xNormal,1.0e-6f))
				&&	(m_fU			== xVertex.m_fU)
				&&	(m_fV			== xVertex.m_fV)
				&&	(m_acIndex[0]	== xVertex.m_acIndex[0])
				&&	(m_acIndex[1]	== xVertex.m_acIndex[1])
				&&	(m_acIndex[2]	== xVertex.m_acIndex[2])
				&&	(m_acIndex[3]	== xVertex.m_acIndex[3])
				&&	(m_afWeight[0]	== xVertex.m_afWeight[0])
				&&	(m_afWeight[1]	== xVertex.m_afWeight[1])
				&&	(m_afWeight[2]	== xVertex.m_afWeight[2])
				&&	(m_afWeight[3]	== xVertex.m_afWeight[3]) )
			{
				return true;
			}
			else
			{
				return false;
			}
		}

};

class Tools_MultiElementSkin_IndexListNode
{
	public:

		Tools_MultiElementSkin_IndexListNode(u_int uIndex, Tools_MultiElementSkin_IndexListNode* pxNext)
		{
			m_uIndex = uIndex;
			m_pxNext = pxNext;
		}
		~Tools_MultiElementSkin_IndexListNode()
		{
			delete m_pxNext;
		}

		u_int	m_uIndex;

		Tools_MultiElementSkin_IndexListNode* m_pxNext;
};

class Tools_MultiElementSkin_VertexAdjacencies
{
	public:
		Tools_MultiElementSkin_VertexAdjacencies()
		{
			m_pxPolyList = 0;
			m_pxVertexList = 0;
		}
	
		~Tools_MultiElementSkin_VertexAdjacencies()
		{
			delete m_pxPolyList;
			delete m_pxVertexList;
		}

		void AddAdjacentVertex(u_int uVertexIndex)
		{
			m_pxVertexList = new Tools_MultiElementSkin_IndexListNode(uVertexIndex, m_pxVertexList);
		}

		void AddAdjacentPolygon(u_int uPolyIndex)
		{
			m_pxPolyList = new Tools_MultiElementSkin_IndexListNode(uPolyIndex, m_pxPolyList);
		}
		
		Tools_MultiElementSkin_IndexListNode*	m_pxPolyList;
		Tools_MultiElementSkin_IndexListNode*	m_pxVertexList;

};


class Tools_MultiElementSkin_PolyListNode
{
	public:

		Tools_MultiElementSkin_PolyListNode(u_int uIndex, Tools_MultiElementSkin_PolyListNode* pxNext)
		{
			m_uIndex = uIndex;
			m_uNumberOfSharedVertices = 1;
			m_pxNext = pxNext;

		}
		~Tools_MultiElementSkin_PolyListNode()
		{
			delete m_pxNext;
		}

		u_int	m_uIndex;
		u_int	m_uNumberOfSharedVertices;

		Tools_MultiElementSkin_PolyListNode* m_pxNext;
};

class Tools_MultiElementSkin_PolyAdjacencies
{
	public:
		Tools_MultiElementSkin_PolyAdjacencies()
		{
			m_pxPolyList = 0;
		}
	
		~Tools_MultiElementSkin_PolyAdjacencies()
		{
			delete m_pxPolyList;
		}

		void AddAdjacentPolygon(u_int uPolyIndex)
		{
			Tools_MultiElementSkin_PolyListNode* pxPolyList = m_pxPolyList;

			// KJL - 29/10/2003 14:09:11 - is this a poly we've seen before?
			while (pxPolyList)
			{
				if (pxPolyList->m_uIndex == uPolyIndex)
				{
					pxPolyList->m_uNumberOfSharedVertices++;
					return;
				}

				pxPolyList = pxPolyList->m_pxNext;
			};

			// KJL - 29/10/2003 14:09:21 - add a new poly to the list
			m_pxPolyList = new Tools_MultiElementSkin_PolyListNode(uPolyIndex, m_pxPolyList);
		}
		
		Tools_MultiElementSkin_PolyListNode*	m_pxPolyList;
};


class Tools_MultiElementSkin_ElementStrip
{
	public:
		int		m_iMaterialID;
		
		u_int	m_uFirstIndexInStrip;
		u_int	m_uFirstVertexInStrip;
		
		u_int	m_uNumberOfIndicesInStrip;
		u_int	m_uNumberOfVerticesInStrip;
};

class Tools_MultiElementSkin_Stripified
{
	// KJL - 04/11/2003 17:25:14 - Tools_MultiElementSkin_Stripified makes a stripified (PC/Xbox/GC style) version of the elements
	// and wraps it all together in one vertex/index array pairing.
	public:
		
		u_int										m_uTotalNumberOfVertices;
		u_int										m_uTotalNumberOfIndices;

		Tools_MultiElementSkin_Vertex*				m_pxVertices;
		u_int*										m_puIndices;

		u_int										m_uNumberOfElementStrips;
		Tools_MultiElementSkin_ElementStrip*		m_pxElementStrip;

	private:

		friend class Tools_MultiElementSkin_Processor;

		Tools_MultiElementSkin_Stripified(const u_int uNumberOfStrips, const Tools_MultiElementSkin_Element* pxFirstElement);
		~Tools_MultiElementSkin_Stripified(void);

};


#endif//__TOOLS_MULTIELEMENTSKIN_PROCESSOR_H__
