
//**************************************************************************/
// Copyright (c) 2008 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: October 2008
//**************************************************************************/

#include <memory.h>

namespace mudbox {
	
/// Type of one component in a normal vector
typedef short int tnormal;
/// Type of the whole normal vector
typedef unsigned long long tnormalv;

/// Normal vector with 16 bit components
struct Normal
{
	union
	{
		tnormal m_vNormal[4];
		tnormalv m_iNormal;
	};
};

/// Represents the topology data of a mesh, providing information about the structure of a surface, e.g. how faces are layed down.

/// The class Mesh is inherited from this class, and provides information about the vertices.
/// Since only full triangle or quad meshes are allowed in Mudbox, an instance of this class is
/// basically an array of triangles and quads. When a mixed triangle-quad or n-sided mesh is
/// imported into mudbox, then the mesh is converted to a full triangle topology, see the member
/// functions IsFakeTriangle and SetFakeTriangle.
/// The Topology class can hold different type of data for the polygons, like position indices, adjacency,
/// texture coordinate indices etc. These are called face components. To save memory not every buffer is 
/// allocated at the beginning. You can control which buffers will be allocated by calling the AddFaceComponent() 
/// and RemoveFaceComponent() functions.
class MBDLL_DECL Topology : virtual public TreeNode
{
public:
	enum FaceState
	{
		fsSelected = 0x01,
		fsVisible = 0x02,
		fsFake = 0x04,
		fsActive = 0x08,
		fsMapped = 0x10,
		// fsTemp = 0x20    // for performance reasons, the temp state is split into a seperate vector
	};

	enum FaceComponent
	{
		fcIndex = 1,
		fcAdjacency = 2,
		fcTCIndex = 4,
		fcNormal = 8,
		fcID = 16,
		fcOctree = 32,
		fcFreeze = 64,
		fcState = 128
	};
	enum FaceType
	{
		typeTriangular,
		typeQuadric
	};

private:
	Store<unsigned int>  m_pTCI;
	Store<unsigned int>  m_pAdjacency;
public:
	Store<unsigned int>  m_pIndices;
private:
	Store<unsigned char> m_pFaceState;
    Store<unsigned char> m_pFaceTemp; // this is split out from the state byte for performance reasons. -- I.A.
	Store<unsigned int>  m_pFaceIDs;

	FaceType m_eType;
	int m_eFaceComponents;
	unsigned int m_iFaceCount;

protected:
	/// Do not use constructors directly. Use CreateInstances() instead.
	Topology( void );
	/// Constructs the object with zero faces.
	Topology( FaceType eType );
public:
	/// \brief Move the content of this object to another one. This is a fast function.

	/// 
	/// This object will loose all data and become an empty topology.
	void MoveTo( Topology *pDestination );
	/// \brief Copy the content of this object to another one, while keeps the content of the current object. 


	/// 
	/// This function is similar to MoveTo, but since it is copying all the data, it is slower. You can use this function
	/// when you want to do some temporary change to a mesh (like you want to change the vertex ordering
	/// inside faces) but then you want to restore the original state. In that case you can simply create a copy 
	/// of the mesh, do the modifications, and when you don't need it any longer, just delete it.
	void CopyTo( Topology *pDestination ) const;
	/// serializes the content of the object into/from a stream.
	void Serialize( Stream &s );
	/// Returns the type of the topology. Can be full triangular or full quadric.
	inline FaceType Type( void ) const { return m_eType; };
	/// Sets the type of the object. This call also clears all the content of the object.
	void SetType( FaceType eType );
	/// Returns the number of sides in the faces. This is three if the topology is triangle based, and four if its quad based.
	int SideCount( void ) const { return Type() == typeTriangular ? 3 : 4; };

	// basic data access
	/// Returns the number of faces in the topology.
	inline unsigned int FaceCount( void ) const { return m_iFaceCount; };
	/// Returns the normal vector for a face in the topology.
	virtual Vector FaceNormal( unsigned int iFaceIndex ) const;
	/// This function is used only internally.
	inline void SetFaceID( unsigned int iFaceIndex, unsigned int iID ) { m_pFaceIDs[iFaceIndex] = iID; };
	/// This function is used only internally.
	inline unsigned int FaceID( unsigned int iFaceIndex ) const { return m_pFaceIDs[iFaceIndex]; };

	/// Call this method to indicate how many fake triangles are in the topology. 
	/// Call SetFaceCount before calling this method. Note this has no effect on quadric meshes.
	virtual void SetFakeTriangleCount( unsigned int iFakeTriangleCount );

	/// Returns the vertex position index of a corner of a quad. The value returned by this call can be used as a patameter
	/// for the VertexPosition() function in class Mesh.
	inline unsigned int QuadIndex( unsigned int iFaceIndex, unsigned int iCornerIndex ) const { return m_pIndices[iFaceIndex*4+iCornerIndex]; };
	/// Sets the vertex position index of a corner of a quad. The value returned by this call can be used as parameter
	/// for the VertexTC() function in class Mesh.
	inline void SetQuadIndex( unsigned int iFaceIndex, unsigned int iCornerIndex, unsigned int iValue ) { m_pIndices[iFaceIndex*4+iCornerIndex] = iValue; };
	/// Returns the vertex texture coordinate index of a corner of a quad.
	inline unsigned int QuadTCI( unsigned int iFaceIndex, unsigned int iCornerIndex ) const { return m_pTCI[iFaceIndex*4+iCornerIndex]; }
	/// Sets the vertex texture coordinate index of a corner of a quad.
	inline void SetQuadTCI( unsigned int iFaceIndex, unsigned int iCornerIndex, unsigned int iValue ) { m_pTCI[iFaceIndex*4+iCornerIndex] = iValue; }
	/// Returns the vertex adjacency information index of a side of a quad. If 
    /// the returned value is x, then x/4 is the index of the adjacent quad, while 
    /// x%4 is the side in the adjacent quad which is common with the given one.
	virtual inline unsigned int QuadAdjacency( unsigned int iFaceIndex, unsigned int iSideIndex ) const { return m_pAdjacency[iFaceIndex*4+iSideIndex]; }
	inline unsigned int QuadAdjacency_ForcedInline( unsigned int iFaceIndex, unsigned int iSideIndex ) const { return m_pAdjacency[iFaceIndex*4+iSideIndex]; }
	/// Sets the adjacency info for a side of a quad. See QuadAdjacency for details.
	virtual inline void SetQuadAdjacency( unsigned int iFaceIndex, unsigned int iSideIndex, unsigned int iValue ) 
        { m_pAdjacency[iFaceIndex*4+iSideIndex] = iValue; }
	/// Returns true if the given edge of a quad has an adjacent quad on the other side.
	inline bool HasAdjacentQuad( unsigned int iFaceIndex, unsigned int iSideIndex ) const { return QuadAdjacency( iFaceIndex, iSideIndex ) < 0x80000000; };

	/// Returns the vertex position index of a corner of a triangle. The value returned 
    /// by this call can be used as a patameter
	/// for the VertexPosition() function in class Mesh.
	inline unsigned int TriangleIndex( unsigned int iFaceIndex, unsigned int iCornerIndex ) const { return m_pIndices[iFaceIndex*3+iCornerIndex]; };
	/// Sets the vertex position index of a corner of a triangle.
	inline void SetTriangleIndex( unsigned int iFaceIndex, unsigned int iCornerIndex, unsigned int iValue ) { m_pIndices[iFaceIndex*3+iCornerIndex] = iValue; };
	/// Returns the vertex texture coordinate index of a corner of a triangle.
	inline unsigned int TriangleTCI( unsigned int iFaceIndex, unsigned int iCornerIndex ) const { return m_pTCI[iFaceIndex*3+iCornerIndex]; }
	/// Sets the vertex texture coordinate index of a corner of a triangle. The value returned by this call can be used as parameter
	/// for the VertexTC() function in class Mesh.
	inline void SetTriangleTCI( unsigned int iFaceIndex, unsigned int iCornerIndex, unsigned int iValue ) { m_pTCI[iFaceIndex*3+iCornerIndex] = iValue; }
	/// Returns the vertex adjacency information index of a side of a triangle. 
    /// If the returned value is x, then x/3 is the index of the adjacent triangle, 
    /// while x%3 is the side in the adjacent triangle which is common with the given one.
	virtual inline unsigned int TriangleAdjacency( unsigned int iFaceIndex, unsigned int iSideIndex ) const { return m_pAdjacency[iFaceIndex*3+iSideIndex]; }
	/// Sets the adjacency info for a side of a triangle. See TriangleAdjacency for details.
	virtual inline void SetTriangleAdjacency( unsigned int iFaceIndex, unsigned int iSideIndex, unsigned int iValue ) 
        { m_pAdjacency[iFaceIndex*3+iSideIndex] = iValue; }
	/// Returns true if the given edge of a triangle has an adjacent triangle on the other side.
	inline bool HasAdjacentTriangle( unsigned int iFaceIndex, unsigned int iSideIndex ) const 
        { return TriangleAdjacency( iFaceIndex, iSideIndex ) < 0x80000000; };

	/// Returns the state of a face. See FaceState.
	inline unsigned char &FaceState( unsigned int iFaceIndex ) { return m_pFaceState[iFaceIndex]; };
	/// Sets the state of a face.
	inline unsigned char FaceState( unsigned int iFaceIndex ) const { return m_pFaceState[iFaceIndex]; };

	/// Returns true if the given face is selected.
	inline bool IsFaceSelected( unsigned int iFaceIndex ) const { return (FaceState( iFaceIndex ) & fsSelected) != 0; };
	/// Sets selection state for a face. The parameter bUpdateVertexSelection controls if the internal buffers should be
	/// updated or not. Passing true as this parameter makes the function slow, so do that only if you want to change
	/// the selection status for a few faces only. Otherwise use false, and after you changed all the vertices, update 
	/// the buffers once only. [HOW? OPEN QUESTION]
	virtual void SetFaceSelected( unsigned int iFaceIndex, bool bSelected = true, bool bUpdateVertexSelection = false, bool bBatchSelection = false );
	/// Returns true if the given face is visible.
	inline bool IsFaceVisible( unsigned int iFaceIndex ) const { return (FaceState( iFaceIndex ) & fsVisible) != 0; };
	/// Sets the visibility of a given face.
	inline void SetFaceVisible( unsigned int iFaceIndex, bool bSelected = true ) 
        { if ( bSelected ) FaceState( iFaceIndex ) |= fsVisible; else FaceState( iFaceIndex ) &= 0xff-fsVisible; };
	inline bool IsFaceActive( unsigned int iFaceIndex ) const { return (FaceState( iFaceIndex ) & fsActive) != 0; };
	inline void SetFaceActive( unsigned int iFaceIndex, bool bSelected = true ) 
        { if ( bSelected ) FaceState( iFaceIndex ) |= fsActive; else FaceState( iFaceIndex ) &= 0xff-fsActive; };
	/// Used only internally.
	inline bool IsFaceMapped( unsigned int iFaceIndex ) const { return (FaceState( iFaceIndex ) & fsMapped) != 0; };
	/// Used only internally.
	inline void SetFaceMapped( unsigned int iFaceIndex, bool bSelected = true ) 
        { if ( bSelected ) FaceState( iFaceIndex ) |= fsMapped; else FaceState( iFaceIndex ) &= 0xff-fsMapped; };

	/// This function can only be used for triangular meshes. If the mesh only contains triangles, then this function
	/// always return false. When the original mesh was a mixed tri-quad or n-sided one, then during import it gets
	/// converted into a triangular mesh. Each polygon which has more than tree sides are converted into N-2 triangles
	/// where N is the number of sides in the polygon. The first triangle is marked as real, but the other ones are marked
	/// as fake by this function. So this function makes it possible to restore the original polygon structure of the mesh.
	inline bool IsFakeTriangle( unsigned int iTriangleIndex ) const 
        { return iTriangleIndex < FaceCount() && (FaceState( iTriangleIndex ) & fsFake); };
	inline void SetFakeTriangle( unsigned int iTriangleIndex, bool b ) 
        { if( iTriangleIndex < FaceCount() ) { 
            if ( b ) FaceState( iTriangleIndex ) |= fsFake; 
            else     FaceState( iTriangleIndex ) &= 0xff-fsFake; 
          }; 
        };

	inline bool IsFaceTemp( unsigned int iFaceIndex ) const { return m_pFaceTemp[iFaceIndex]; };
	inline void SetFaceTemp( unsigned int iFaceIndex, bool bSelected = true ) 
        { m_pFaceTemp[iFaceIndex] = bSelected; }

    // do a fast clear of all the temp state flags for all the faces.
    inline void ClearFaceTempStates()
    {
        MB_ASSERT(m_iFaceCount <= m_pFaceTemp.ItemCount()); 
        memset(&m_pFaceTemp[0], 0, m_iFaceCount);  // memset is the fastest way to clear the array.
    }

	/// Adds new face component(s) to the object. If the specified components did not exist yet, memory will
	/// be allocated for them.
	virtual void AddFaceComponent( FaceComponent eComponentToAdd );
	/// Removes face component(s) from the object. This might delete some data from the memory.

	virtual void RemoveFaceComponent( FaceComponent eComponentToRemove );
	/// Returns the current face components for the object.
	inline unsigned int FaceComponents( void ) const { return m_eFaceComponents; };
	/// Sets the number of faces in the object. Existing face data will be kept.
	void SetFaceCount( unsigned int iFaceCount );
	/// Sets the size of the adjacency array
	virtual bool SetAdjacencyCount( unsigned int iCount, bool bKeepContent = true, bool bForced = false );
	/// Returns the size of the adjacency matrix
	inline unsigned int AdjacencyCount( void ) const { return m_pAdjacency.ItemCount(); };

	/// Returns true if the mesh has texture coordinates.
	inline bool HasTC( void ) const { return (FaceComponents() & fcTCIndex) != 0; };

	// Used only to solve some old compatibility issues

	friend class ::XMesh;
};

}; // end of namespace mudbox
