///////////////////////////////////////////////////////////////////////////////
/// @file deBrush_priv.hpp
///
/// @brief Private Geometry Calculation & storage header
///
/// @author Assassin
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Oct 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#ifndef DEBRUSH_PRIV_HPP
#define DEBRUSH_PRIV_HPP

#include "deBrush.hpp"
#include "deMath.hpp"
#include "deLine.hpp"
#include "deHash.hpp"
#include "deFunctors.hpp"
#include "deWorld_Helper.hpp"

class deBPoly
{
public:
	deVec3d Normal;
	deDouble Dist;
	deLVert *Verts;
	long TextureIndex;
	long NumVerts;
	deBPoly(long verts = 3);
	~deBPoly();
	deBPoly(const deBPoly & ref) ;
	deBPoly & operator=(deBPoly & ref);
	void Resize(long verts);
};

#pragma warning (disable : 4512) // assignment operator
//=======================================================
class deBrush : public deWorldObject, virtual public IdeBrush
{
public:
	virtual ~deBrush();

	void* GetWOInterface(long interface_id);
	deBoolean Serialize(IdeFile* FS);
	deBoolean DeSerialize(IdeFile* FS, long DataLength);
	deBoolean DeSerializeLoad();
	
	void* GetBrushInterface(deBrushType Type) const;

	void GetWorldTransform(deTransformInfo & Matrix) const;
//	void SetWorldTransform(deTransformInfo & Matrix);
	void Rotate(deVec3d &Center, deVec3d &Axis, deDouble Degrees);
	void Rotate(deVec3d &Center, deTransformInfo &M);
	void Translate(deVec3d &Delta);
	void Scale(deVec3d &Center, deDouble Scale);
	
	deBoolean Update();
	long GetLOD() const;
	long GetLOD2() const;
	void SetLOD(long LOD);
	void SetLOD2(long LOD2);

	long GetTexFacesNum();
	long GetTexCoordsNum(long faceindex);
	deTexCoord GetTexCoord(long faceindex, long index);
	void SetTexCoord(long faceindex, long index, deTexCoord &coord);
	void SetTexCoord(long faceindex, long index, deFloat u, deFloat v);
	void SetTexture(long faceindex, IdeRenderTexture * tex);
	IdeRenderTexture* GetTexture(long faceindex);
	void SetSmooth(deBoolean SmoothNotSharp);
	deBoolean GetSmooth();
	
	long GetNumCVs();
	void GetCVList(deVec3d buffer[]);
	deVec3d GetCV(long num);
	deBoolean SetCV(long num, deVec3d & CV);
	deBoolean SetCVList(long num, deVec3d CVList[]);
	deBoolean AddCV(deVec3d & point);

	IdeBrush * Copy();

	void GetAABB(deVec3d &Mins, deVec3d &Maxs);
//	deTList <deBPoly> & GetPolyList();
//	void GetPolyList(deTList <deBPoly*> & list);
	long GetNumVBs();
	IdeVertexBuffer * GetVertexBuffer(long index);
	void SetDiffuseColor(deARGB color);
	deARGB GetDiffuseColor();

	void Subtract(IdeBrush * cutter);

	static void MakeTri(deBPoly & Result,
				deVec3d &Vert1, deVec3d &Vert2, deVec3d &Vert3);
	static void MakeTri(deBPoly & Result,
				deVec3d &Vert1, deVec3d &Vert2, deVec3d &Vert3,
				deVec3d &Norm1, deVec3d &Norm2, deVec3d &Norm3);
	static void MakeTri(deBPoly & Result,
				deVec3d &Vert1, deVec3d &Vert2, deVec3d &Vert3,
				deVec3d &Norm1, deVec3d &Norm2, deVec3d &Norm3,
				deColor &Color1, deColor &Color2, deColor &Color3);
	static void MakeTri(deBPoly & Result,
				deVec3d &Vert1, deVec3d &Vert2, deVec3d &Vert3,
				deVec3d &Norm1, deVec3d &Norm2, deVec3d &Norm3,
				deTexCoord &Coord1, deTexCoord &Coord2, deTexCoord &Coord3);
	static void MakeTri(deBPoly & Result,
				deVec3d &Vert1, deVec3d &Vert2, deVec3d &Vert3,
				deVec3d &Norm1, deVec3d &Norm2, deVec3d &Norm3,
				deColor &Color1, deColor &Color2, deColor &Color3,
				deTexCoord &Coord1, deTexCoord &Coord2, deTexCoord &Coord3);

	struct DataQuantity_t
	{
		enum QuantityRatio_t
		{
			Quantity_Unmanaged = 0,
			Quantity_Constant,
			Quantity_PerCV,
			Quantity_Force32 = 0x7fffffff,
		} QuantityRatio;
		long CVNum;			// used to allow fractional amounts (example: 2 CVs = 1 texture)
		long NumFaces;		// number of textured faces
		long CoordsPerFace;	// number of UV coordinate pairs per face (don't all need to be used)
	};
	
	
protected:
	// methods
	deBrush(IdeBrush::deBrushType Type, const DataQuantity_t &QuantityDefs);

	enum DrawType
	{
		Points,
		LineList,
		LineStrip,
		TriList = 0,
		TriStrip,
		TriFan
	};
	// methods for specific brushes to implement
	virtual void SpecificRotate(deVec3d Center, deTransformInfo &M);
	virtual void SpecificTranslate(deVec3d Delta);
	virtual void SpecificScale(deVec3d Center, deDouble Scale);
	virtual void UpdateBrush() = 0;
	virtual deBrush * CopyBrush() = 0;
	// internal data-handling methods
	deBoolean FillVertexBuffers();
	deBoolean FillIndexedBuffers();
	void ClearGeometry();
	void ResizeLists(long size);
	deBoolean RecalcListQuantities(deBoolean Creation = deFALSE);

protected:
	// variables for storing persistent data
	const DataQuantity_t&				m_QuantityDefs;
	deTransform							m_WorldTransform;
	long								m_CVNum;
	deTArray <deVec3d>					m_CVList;
	deTArray <deTArray<deTexCoord> >	m_FaceList;
	deTArray <IdeRenderTexture*>		m_TextureList;
	deTArray <IdeVertexBuffer*>			m_SubVBs;
	deTArray <DrawType>					m_DrawTypeList;
	IdeVertexBuffer*					m_MainVBuffer;
	long								m_LOD, m_LOD2;
	deBoolean							m_SmoothNormals;
	deBoolean							m_Changed;
	deBoolean							m_UsingIndexedBuffer;
	deBoolean							m_SaveVBuffers;
	deARGB								m_DiffuseColor;
	
	// variables for storing "built" data
	deTList <deBPoly>					m_PolyList;
	deTArray <deTList <BIndexList> >	m_IndexedBuffers;
	deTArray <deIDPair>					m_CachedTextures;
	
	// variables that need removing
//	deTArray <IdeVertexBuffer*> m_VBuffers;
	
//	deTArray <IdeRenderTexture*> m_TextureList;
//	IdeGroupBrush * m_GroupParent;
//	BrushOctree *m_Octree;

private:
	const IdeBrush::deBrushType m_Type;
};

class deGroupBrush : public IdeGroupBrush, public deBrush
{
public:
	deGroupBrush();
	~deGroupBrush();

	WorldObjectClassDef;
	
	bool AddBrush(IdeBrush* brush);
	bool RemoveBrush(IdeBrush* brush);

	void GetBrushList(deTList <IdeBrush*> &list);

protected:
	deBrush * CopyBrush();
	void SpecificRotate(deVec3d Center, deTransformInfo &M);
	void SpecificTranslate(deVec3d Delta);
	void SpecificScale(deVec3d Center, deDouble Scale);

private:
	void UpdateBrush();
	deTList <IdeBrush*> m_BrushList;
};

class deLineBrush : public IdeLineBrush, public deBrush
{
public:
	deLineBrush();
	~deLineBrush();

	WorldObjectClassDef;
	
protected:
	deBrush * CopyBrush();

private:
	void UpdateBrush();
};

struct edge_normals { bool PairedFaces; short CV1, CV2; deVec3d Norm1, Norm2; };
struct edge { short p1, p2, p_opp; };
class deBlockBrush : public IdeBlockBrush, public deBrush
{
public:
	deBlockBrush();
	~deBlockBrush();

	WorldObjectClassDef;

	deBoolean GetProjectedEdgePlanes(const deVec3d & Position, const deTransformInfo & transform, IdePlaneFrustum* pFrustum);

protected:
	deBrush * CopyBrush();

private:
	void UpdateBrush();

	void SubdivideSurfaces();	// LOD calculator

	// convex hull functions
	// based on methods described at
	// http://www.cse.unsw.edu.au/~lambert/java/3d/giftwrap.html
	void GiftWrap();
	short FindA();
	short FindB(short A);
	short FindC(short A, short B, short DontUse);
	bool PointTotallyUsed(short p);
	void RecalcEdgeConnectivity();

	deTArray <bool> m_TotallyUsed;
	deTArray< deTList<edge> > m_EdgesOnVert;
	deTArray <edge> m_FaceList;
	deTArray<edge_normals> m_EdgeNormals;
	deBoolean m_EdgesUpdated;

	deDouble Dist(deBPoly & poly, deVec3d Point);	// positive in front
	void MakeTri(long v1, long v2, long v3, deBPoly&);	// CCW winding
};

class deCylinderBrush : public IdeCylinderBrush, public deBrush
{
public:
	deCylinderBrush();
	~deCylinderBrush();

	WorldObjectClassDef;
	
protected:
	deBrush * CopyBrush();

private:
	void UpdateBrush();

	void GenerateHull();
	void GenerateHull2();
	deDouble Dist(const deVec3d &LineP1, const deVec3d &LineP2, const deVec3d &P3);
};

class deLathedBrush : public IdeLathedBrush, public deBrush
{
public:
	deLathedBrush();
	~deLathedBrush();

	WorldObjectClassDef;
	
	void SetLine(IdeLine * line);
	IdeLine * GetLine();

	void SetStyle(LatheStyle style);
	LatheStyle GetStyle();
	void SetDegree(deDouble RotDegree);
	deDouble GetDegree();

protected:
	deBrush * CopyBrush();

private:
	void UpdateBrush();

	void Lathe();
	deDouble Dist(deVec3d LineP1, deVec3d LineP2, deVec3d Pt);

	IdeLine * m_Line;
	LatheStyle m_Style;
	deDouble m_Degree;
};

class deExtrudedBrush : public IdeExtrudedBrush, public deBrush
{
public:
	deExtrudedBrush();
	~deExtrudedBrush();

	WorldObjectClassDef;
	
	void SetSourceLine(IdeLine * line);
	void SetExtrudeLine(IdeLine * line);
	void MakeSolid(deBoolean IsSolid);

protected:
	deBrush * CopyBrush();

private:
	void UpdateBrush();

	void Extrude();

	IdeLine * m_SourceLine;
	IdeLine * m_ExtrudeLine;
	bool m_Solid;
};

class deXMesh;
class deStaticBrush : public IdeStaticBrush, public deBrush
{
public:
	deStaticBrush();
	~deStaticBrush();

	WorldObjectClassDef;
	
	void SetTextureState(IdeRenderTexture* texture);
	void AddPoly(deBPoly & poly);
	void AddIndexedBuffer(BIndexList indexbuffer);
	void BuildIndexedBuffers();

	deBoolean AddVertices(const deBrushVertexData & vertexdata, long & indexoffset);
	deBoolean AddIndices(const deBrushIndexData & vertexdata, long indexoffset);

	friend deBoolean IdeBrush_LoadFromXFile(char * filename, deTList<IdeStaticBrush*> & brushplist);
	friend deBoolean MakeStaticBrushList(deXMesh * mesh, deTList<IdeStaticBrush*> & brushplist);

protected:
	deBrush * CopyBrush();
	void SpecificRotate(deVec3d Center, deTransformInfo &M);
	void SpecificTranslate(deVec3d Delta);
	void SpecificScale(deVec3d Center, deDouble Scale);

private:
	void UpdateBrush();

	void MakeArrays();

	long m_CurrentTexture;
};

class dePrismBrush : public IdePrismBrush, public deBrush
{
public:
	dePrismBrush();
	~dePrismBrush();

	WorldObjectClassDef;
	
protected:
	deBrush * CopyBrush();

private:
	void UpdateBrush();

	deTArray <dePlane> m_Planes;

	void GenerateHull();
	deBoolean PlaneIntersectionPoint(dePlane &p1, dePlane &p2, dePlane &p3, deVec3d &p);
};

class deBezierBrush : public deBrush, public IdeBezierBrush
{
public:
	deBezierBrush();
	~deBezierBrush();

	WorldObjectClassDef;

protected:
	deBrush * CopyBrush();

private:
	void UpdateBrush();

	void MakePatches();
	deBoolean m_IsSolid;
};

class deLargeStaticBrush : public deBrush, public IdeLargeStaticBrush
{
public:
	deLargeStaticBrush();
	~deLargeStaticBrush();
	
	WorldObjectClassDef;
	
	deBoolean SetNumTextures(long NumTextures);
	void SetWorldTransform(deTransformInfo & Matrix);
	deBoolean SetVertexBuffer(long TextureNum, IdeVertexBuffer* VBuffer);

protected:
	deBrush * CopyBrush();
	
private:
	void UpdateBrush();
	
	void MakePatches();
};

#endif