/* Copyright (c) <2003-2011> <Julio Jerez, Newton Game Dynamics>
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 * claim that you wrote the original software. If you use this software
 * in a product, an acknowledgment in the product documentation would be
 * appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and must not be
 * misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source distribution.
 */

#ifndef __dgMeshEffect_H__
#define __dgMeshEffect_H__

#include "hpl1/engine/libraries/newton/core/dg.h"

class dgWorld;
class dgCollision;
class dgMeshEffect;
class dgMeshEffectSolidTree;
class dgMeshTreeCSGEdgePool;

#define DG_MESH_EFFECT_INITIAL_VERTEX_SIZE 8
#define DG_MESH_EFFECT_BOLLEAN_STACK 512
#define DG_MESH_EFFECT_POINT_SPLITED 512
#define DG_MESH_EFFECT_POLYGON_SPLITED 256
#define DG_MESH_EFFECT_FLAT_CUT_BORDER_EDGE 0x01
#define DG_VERTEXLIST_INDEXLIST_TOL (dgFloat64(0.0f))

#define DG_MESH_EFFECT_PRECISION_BITS 30
#define DG_MESH_EFFECT_PRECISION_SCALE dgFloat64(1 << DG_MESH_EFFECT_PRECISION_BITS)
#define DG_MESH_EFFECT_PRECISION_SCALE_INV (dgFloat64(1.0f) / DG_MESH_EFFECT_PRECISION_SCALE)

#define DG_MESG_EFFECT_BOOLEAN_INIT()     \
	dgMeshEffect *result = NULL;          \
	dgMeshEffect *sourceCoplanar = NULL;  \
	dgMeshEffect *leftMeshSource = NULL;  \
	dgMeshEffect *rightMeshSource = NULL; \
	dgMeshEffect *clipperCoplanar = NULL; \
	dgMeshEffect *leftMeshClipper = NULL; \
	dgMeshEffect *rightMeshClipper = NULL;

#define DG_MESG_EFFECT_BOOLEAN_FINISH()                 \
	if (sourceCoplanar) {                               \
		sourceCoplanar->Release();                      \
	}                                                   \
	if (clipperCoplanar) {                              \
		clipperCoplanar->Release();                     \
	}                                                   \
	if (leftMeshClipper) {                              \
		leftMeshClipper->Release();                     \
	}                                                   \
	if (rightMeshClipper) {                             \
		rightMeshClipper->Release();                    \
	}                                                   \
	if (leftMeshSource) {                               \
		leftMeshSource->Release();                      \
	}                                                   \
	if (rightMeshSource) {                              \
		rightMeshSource->Release();                     \
	}                                                   \
	if (result) {                                       \
		result->ConvertToPolygons();                    \
		dgStack<dgInt32> map(result->m_pointCount + 1); \
		result->RemoveUnusedVertices(&map[0]);          \
	}

class dgMeshEffect : public dgPolyhedra, public dgRefCounter {
public:
	class dgVertexAtribute {
	public:
		dgBigVector m_vertex;
		dgFloat64 m_normal_x;
		dgFloat64 m_normal_y;
		dgFloat64 m_normal_z;
		dgFloat64 m_u0;
		dgFloat64 m_v0;
		dgFloat64 m_u1;
		dgFloat64 m_v1;
		dgFloat64 m_material;
	};

	class dgIndexArray {
	public:
		dgInt32 m_materialCount;
		dgInt32 m_indexCount;
		dgInt32 m_materials[256];
		dgInt32 m_materialsIndexCount[256];
		dgInt32 *m_indexList;
	};

	dgMeshEffect(dgMemoryAllocator *const allocator, bool preAllocaBuffers);
	dgMeshEffect(dgCollision *const collision);
	dgMeshEffect(const dgMeshEffect &source);
	dgMeshEffect(dgPolyhedra &mesh, const dgMeshEffect &source);

	// Create a convex hull Mesh form point cloud
	dgMeshEffect(dgMemoryAllocator *const allocator, const dgFloat64 *const vertexCloud, dgInt32 count, dgInt32 strideInByte, dgFloat64 distTol);

	// create a convex approximation
	dgMeshEffect(const dgMeshEffect &source, dgFloat32 maxConcavity, dgInt32 maxCount = 32);

	// create a planar Mesh
	dgMeshEffect(dgMemoryAllocator *const allocator, const dgMatrix &planeMatrix, dgFloat32 witdth, dgFloat32 breadth, dgInt32 material, const dgMatrix &textureMatrix0, const dgMatrix &textureMatrix1);
	virtual ~dgMeshEffect(void);

	void ApplyTransform(const dgMatrix &matrix);
	dgMatrix CalculateOOBB(dgBigVector &size) const;
	void CalculateAABB(dgBigVector &min, dgBigVector &max) const;

	void CalculateNormals(dgFloat64 angleInRadians);
	void SphericalMapping(dgInt32 material);
	void BoxMapping(dgInt32 front, dgInt32 side, dgInt32 top);
	void UniformBoxMapping(dgInt32 material, const dgMatrix &textruMatrix);
	void CylindricalMapping(dgInt32 cylinderMaterial, dgInt32 capMaterial);

	dgEdge *InsertEdgeVertex(dgEdge *const edge, dgFloat64 param);

	dgMeshEffect *Union(const dgMatrix &matrix, const dgMeshEffect *const clip) const;
	dgMeshEffect *Difference(const dgMatrix &matrix, const dgMeshEffect *const clip) const;
	dgMeshEffect *Intersection(const dgMatrix &matrix, const dgMeshEffect *const clip) const;
	void ClipMesh(const dgMatrix &matrix, const dgMeshEffect *const clip, dgMeshEffect **const top, dgMeshEffect **const bottom) const;

	bool CheckIntersection(const dgMeshEffectSolidTree *const solidTree, dgFloat64 scale) const;
	dgMeshEffectSolidTree *CreateSolidTree() const;
	static void DestroySolidTree(dgMeshEffectSolidTree *const tree);
	static bool CheckIntersection(const dgMeshEffect *const meshA, const dgMeshEffectSolidTree *const solidTreeA,
	                              const dgMeshEffect *const meshB, const dgMeshEffectSolidTree *const solidTreeB, dgFloat64 scale);

	dgMeshEffect *GetFirstLayer() const;
	dgMeshEffect *GetNextLayer(const dgMeshEffect *const layer) const;

	void Triangulate();
	void ConvertToPolygons();

	void RemoveUnusedVertices(dgInt32 *const vertexRemapTable);

	void BeginPolygon();
	void AddPolygon(dgInt32 count, const dgFloat32 *const vertexList, dgInt32 stride, dgInt32 material);
#ifndef __USE_DOUBLE_PRECISION__
	void AddPolygon(dgInt32 count, const dgFloat64 *const vertexList, dgInt32 stride, dgInt32 material);
#endif
	void EndPolygon(dgFloat64 tol);

	void PackVertexArrays();

	void BuildFromVertexListIndexList(dgInt32 faceCount, const dgInt32 *const faceIndexCount, const dgInt32 *const faceMaterialIndex,
	                                  const dgFloat32 *const vertex, dgInt32 vertexStrideInBytes, const dgInt32 *const vertexIndex,
	                                  const dgFloat32 *const normal, dgInt32 normalStrideInBytes, const dgInt32 *const normalIndex,
	                                  const dgFloat32 *const uv0, dgInt32 uv0StrideInBytes, const dgInt32 *const uv0Index,
	                                  const dgFloat32 *const uv1, dgInt32 uv1StrideInBytes, const dgInt32 *const uv1Index);

	dgInt32 GetVertexCount() const;
	dgInt32 GetVertexStrideInByte() const;
	dgFloat64 *GetVertexPool() const;

	dgInt32 GetPropertiesCount() const;
	dgInt32 GetPropertiesStrideInByte() const;
	dgFloat64 *GetAttributePool() const;
	dgFloat64 *GetNormalPool() const;
	dgFloat64 *GetUV0Pool() const;
	dgFloat64 *GetUV1Pool() const;

	dgEdge *ConectVertex(dgEdge *const e0, dgEdge *const e1);

	dgInt32 GetTotalFaceCount() const;
	dgInt32 GetTotalIndexCount() const;
	void GetFaces(dgInt32 *const faceCount, dgInt32 *const materials, void **const faceNodeList) const;

	void RepairTJoints(bool triangulate);
	bool SeparateDuplicateLoops(dgEdge *const edge);
	bool HasOpenEdges() const;
	dgFloat64 CalculateVolume() const;

	void GetVertexStreams(dgInt32 vetexStrideInByte, dgFloat32 *const vertex,
	                      dgInt32 normalStrideInByte, dgFloat32 *const normal,
	                      dgInt32 uvStrideInByte0, dgFloat32 *const uv0,
	                      dgInt32 uvStrideInByte1, dgFloat32 *const uv1) const;

	void GetIndirectVertexStreams(dgInt32 vetexStrideInByte, dgFloat64 *const vertex, dgInt32 *const vertexIndices, dgInt32 *const vertexCount,
	                              dgInt32 normalStrideInByte, dgFloat64 *const normal, dgInt32 *const normalIndices, dgInt32 *const normalCount,
	                              dgInt32 uvStrideInByte0, dgFloat64 *const uv0, dgInt32 *const uvIndices0, dgInt32 *const uvCount0,
	                              dgInt32 uvStrideInByte1, dgFloat64 *const uv1, dgInt32 *const uvIndices1, dgInt32 *const uvCount1);

	dgIndexArray *MaterialGeometryBegin() const;
	void MaterialGeomteryEnd(dgIndexArray *const handle) const;
	dgInt32 GetFirstMaterial(dgIndexArray *const handle) const;
	dgInt32 GetNextMaterial(dgIndexArray *const handle, dgInt32 materialHandle) const;
	dgInt32 GetMaterialID(dgIndexArray *const handle, dgInt32 materialHandle) const;
	dgInt32 GetMaterialIndexCount(dgIndexArray *const handle, dgInt32 materialHandle) const;
	void GetMaterialGetIndexStream(dgIndexArray *const handle, dgInt32 materialHandle, dgInt32 *const index) const;
	void GetMaterialGetIndexStreamShort(dgIndexArray *const handle, dgInt32 materialHandle, dgInt16 *const index) const;

	dgCollision *CreateCollisionTree(dgInt32 shapeID) const;
	dgCollision *CreateConvexCollision(dgFloat64 tolerance, dgInt32 shapeID, const dgMatrix &matrix = dgGetIdentityMatrix()) const;

	dgMeshEffect *CreateConvexApproximation(dgFloat32 maxConcavity, dgInt32 maxCount = 32) const;
	dgMeshEffect *CreateDelanayTretrahedralization(dgInt32 interionMaterial, dgMatrix &matrix) const;
	dgMeshEffect *CreateVoronoiPartition(dgInt32 pointsCount, dgInt32 pointStrideInBytes, const dgFloat32 *const pointCloud, dgInt32 interionMaterial, dgMatrix &matrix) const;

	void PlaneClipMesh(const dgMatrix &planeMatrix, const dgMatrix &planeTextMatrix, dgInt32 planeMaterial, dgMeshEffect **const leftMeshSource, dgMeshEffect **const rightMeshSource) const;

	dgVertexAtribute &GetAttribute(dgInt32 index) const;
	void TransformMesh(const dgMatrix &matrix);

	void *GetFirstVertex() const;
	void *GetNextVertex(void *const vertex) const;
	dgInt32 GetVertexIndex(void *const vertex) const;

	void *GetFirstPoint() const;
	void *GetNextPoint(void *const point) const;
	dgInt32 GetPointIndex(const void *const point) const;
	dgInt32 GetVertexIndexFromPoint(void *const point) const;

	void *GetFirstEdge() const;
	void *GetNextEdge(void *const edge) const;
	void GetEdgeIndex(const void *const edge, dgInt32 &v0, dgInt32 &v1) const;
	//  void GetEdgeAttributeIndex (const void* edge, dgInt32& v0, dgInt32& v1) const;

	void *GetFirstFace() const;
	void *GetNextFace(void *const face) const;
	dgInt32 IsFaceOpen(const void *const face) const;
	dgInt32 GetFaceMaterial(const void *const face) const;
	dgInt32 GetFaceIndexCount(const void *const face) const;
	void GetFaceIndex(const void *const face, int *const indices) const;
	void GetFaceAttributeIndex(const void *const face, int *const indices) const;

	bool Sanity() const;

protected:
	void Init(bool preAllocaBuffers);
	dgBigVector GetOrigin() const;
	dgInt32 CalculateMaxAttributes() const;
	dgFloat64 QuantizeCordinade(dgFloat64 val) const;
	dgInt32 EnumerateAttributeArray(dgVertexAtribute *const attib);
	void ApplyAttributeArray(dgVertexAtribute *const attib, dgInt32 maxCount);
	void AddVertex(const dgBigVector &vertex);
	void AddAtribute(const dgVertexAtribute &attib);
	void AddPoint(const dgFloat64 *vertexList, dgInt32 material);
	void FixCylindricalMapping(dgVertexAtribute *const attib) const;

	void MergeFaces(const dgMeshEffect *const source);
	void ReverseMergeFaces(dgMeshEffect *const source);
	dgVertexAtribute InterpolateEdge(dgEdge *const edge, dgFloat64 param) const;
	dgVertexAtribute InterpolateVertex(const dgBigVector &point, dgEdge *const face) const;

	dgMeshEffect *GetNextLayer(dgInt32 mark) const;

	void FilterCoplanarFaces(const dgMeshEffect *const otherCap, dgFloat32 sign);
	void ClipMesh(const dgMeshEffect *const clipMesh, dgMeshEffect **const back, dgMeshEffect **const front, dgMeshEffect **const coplanar) const;
	void ClipMesh(const dgMeshEffectSolidTree *const clipper, dgMeshEffect **const back, dgMeshEffect **const front, dgMeshEffect **const coplanar) const;

	dgInt32 PlaneApplyCap(const dgMeshEffect *planeMesh, const dgBigPlane &normal);
	void PlaneClipMesh(const dgMeshEffect *planeMesh, dgMeshEffect **leftMeshSource, dgMeshEffect **rightMeshSource) const;

	dgMeshEffect *MakeDelanayIntersection(dgMeshEffectSolidTree *const tree, dgBigVector *const points, dgInt32 count, dgInt32 materialId, const dgMatrix &textureProjectionMatrix, dgFloat32 normalAngleInRadians) const;

	bool CheckSingleMesh() const;

	dgInt32 m_pointCount;
	dgInt32 m_maxPointCount;

	dgInt32 m_atribCount;
	dgInt32 m_maxAtribCount;

	dgBigVector *m_points;
	dgVertexAtribute *m_attib;

	friend class dgConvexHull3d;
	friend class dgConvexHull4d;
	friend class dgMeshTreeCSGFace;
	friend class dgMeshEffectSolidTree;
};

inline dgInt32 dgMeshEffect::GetVertexCount() const {
	return m_pointCount;
}

inline dgInt32 dgMeshEffect::GetPropertiesCount() const {
	return m_atribCount;
}

inline dgInt32 dgMeshEffect::GetMaterialID(dgIndexArray *const handle, dgInt32 materialHandle) const {
	return handle->m_materials[materialHandle];
}

inline dgInt32 dgMeshEffect::GetMaterialIndexCount(dgIndexArray *const handle, dgInt32 materialHandle) const {
	return handle->m_materialsIndexCount[materialHandle];
}

inline dgMeshEffect::dgVertexAtribute &dgMeshEffect::GetAttribute(dgInt32 index) const {
	return m_attib[index];
}

inline dgInt32 dgMeshEffect::GetPropertiesStrideInByte() const {
	return sizeof(dgVertexAtribute);
}

inline dgFloat64 *dgMeshEffect::GetAttributePool() const {
	return &m_attib->m_vertex.m_x;
}

inline dgFloat64 *dgMeshEffect::GetNormalPool() const {
	return &m_attib->m_normal_x;
}

inline dgFloat64 *dgMeshEffect::GetUV0Pool() const {
	return &m_attib->m_u0;
}

inline dgFloat64 *dgMeshEffect::GetUV1Pool() const {
	return &m_attib->m_u1;
}

inline bool dgMeshEffect::CheckIntersection(const dgMeshEffect *const meshA, const dgMeshEffectSolidTree *const solidTreeA, const dgMeshEffect *const meshB, const dgMeshEffectSolidTree *const solidTreeB, dgFloat64 scale) {
	return (meshA->CheckIntersection(solidTreeB, scale) || meshB->CheckIntersection(solidTreeA, scale));
}

inline dgInt32 dgMeshEffect::GetVertexStrideInByte() const {
	return sizeof(dgBigVector);
}

inline dgFloat64 *dgMeshEffect::GetVertexPool() const {
	return &m_points[0].m_x;
}

inline dgMeshEffect *dgMeshEffect::GetFirstLayer() const {
	return GetNextLayer(IncLRU());
}

inline dgMeshEffect *dgMeshEffect::GetNextLayer(const dgMeshEffect *const layerSegment) const {
	if (!layerSegment) {
		return NULL;
	}
	return GetNextLayer(layerSegment->IncLRU() - 1);
}

inline dgFloat64 dgMeshEffect::QuantizeCordinade(dgFloat64 x) const {
	int exp;
	dgFloat64 mantissa = frexp(x, &exp);
	mantissa = DG_MESH_EFFECT_PRECISION_SCALE_INV * floor(mantissa * DG_MESH_EFFECT_PRECISION_SCALE);

	dgFloat64 x1 = ldexp(mantissa, exp);
	return x1;
}

#endif
