///////////////////////////////////////////////////////////////////////////////
/// @file CylinderBrush.cpp
///
/// @brief Cylinder brush creation
///
/// @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 Feb 2002
/// @author Assassin
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

#include "deBrush_priv.hpp"
#include "deStack.hpp"
//#include <cstdarg>
#include "deMath.hpp"
//#include "octree.hpp"

//#include <crtdbg.h>

WorldObjectClassImp(deCylinderBrush);

static const deBrush::DataQuantity_t s_CylinderQuantities = 
{
	deBrush::DataQuantity_t::Quantity_Constant, 
	3,	//CVNum
	3,	//NumFaces
	4	//CoordsPerFace
};

deCylinderBrush::deCylinderBrush()
:deBrush(Brush_Cylinder, s_CylinderQuantities)
{
}

deCylinderBrush::~deCylinderBrush()
{
}

void deCylinderBrush::UpdateBrush()
{
	GenerateHull();
}

void deCylinderBrush::GenerateHull()
{
	// the normals for the top and bottom caps
	static const deVertex Up = {0, 1, 0};
	static const deVertex Down={0,-1, 0};

	// need the 3 control points to make a cylinder
	if (m_CVNum < 3)
		return;

	// some temporary variables for various things
	deDouble Radius;				// distance from Axis of revolution to the circumference
	deDouble Height;				// distance from bottom to top
	int Faces, i;					// Faces is the number of planar faces around the circumference of the cylinder
	unsigned short Winding; 		// to decide clockwise (0) or CCW (1)
	deVec3d Normal2, Normal2_Cross; // for getting the transform set up
	deVec3d Axis;					// the axis of revolution
	deVec3d FaceCoords[3][4];		// texture coords of each face (top, bottom, sides)
	deVec3d BaseCenter[2];			// texture coords at center of top and bottom
	deDouble BaseOffset[2];			// offset to start at for top and bottom
	deDouble BaseRotate[2];			// angular offset to start at
	deDouble XScale[2], YScale[2];	// scale of the textures along U and V (X & Y)

	// this is either 0 or 1
	Winding = 1;

	Faces = m_LOD;
	if (Faces < 4)
		Faces = 4;

	// the positions of the upper and lower rims
	deVertex *Points0 = new deVertex[Faces+1];
	deVertex *Points1 = new deVertex[Faces+1];
	// normals around the circumference (top and bottom are constant linear)
	deVertex *SideNorms = new deVertex[(Faces+1)*2];
	// texture coordinates for applied textures
	deTexCoord *SideCoords = new deTexCoord[(Faces+1)*2];
	deTexCoord *BaseCoords0 = new deTexCoord[Faces+1];
	deTexCoord *BaseCoords1 = new deTexCoord[Faces+1];

	// vertex winding decision-maker, for easy face reversals
	Winding = (unsigned short)((Winding == 0) ? 0 : 1);

	// radius from axis line to 3rd point
	Radius = Dist(m_CVList[0], m_CVList[1], m_CVList[2]);
	Axis = (m_CVList[1]-m_CVList[0]);
	Vec3d_Normalize(Axis);

	// find the vector for CP #2
	Normal2 = m_CVList[2]-m_CVList[0];
	Normal2_Cross = Vec3d_Cross(Axis,Normal2);
	Normal2 = Vec3d_Cross(Axis, Normal2_Cross);
	Vec3d_Normalize(Normal2);
	Vec3d_Normalize(Normal2_Cross);

	// normal2 is "left"
	// axis is "up"
	// normal2_cross is "in"
	deTransform M(Normal2, Axis, Normal2_Cross);
	M.Translation = m_CVList[0];
	M.Translation += m_CVList[1];
	M.Translation /= 2.0;

	// get the height
	Height = Vec3d_Length((m_CVList[0] - m_CVList[1]));

	// copy the brush texture coordinates for the sides
	for (i = 0; i < 4; i++)
	{
		FaceCoords[0][i].x = m_FaceList[0][i].u;
		FaceCoords[0][i].y = m_FaceList[0][i].v;
		FaceCoords[0][i].z = 0;
	}
	// copy the brush texture coordinates for the caps
	for (i = 0; i < 3; i++)
	{
		FaceCoords[1][i].x = m_FaceList[1][i].u;
		FaceCoords[1][i].y = m_FaceList[1][i].v;
		FaceCoords[1][i].z = 0;
		FaceCoords[2][i].x = m_FaceList[2][i].u;
		FaceCoords[2][i].y = m_FaceList[2][i].v;
		FaceCoords[2][i].z = 0;
	}
	// get the geometric offset of the cap texture mapping
	for (i = 0; i < 2; i++)
	{
		deDouble X,Y,Angle;

		BaseCenter[i] = FaceCoords[i+1][0];

		X = FaceCoords[i+1][1].x - BaseCenter[i].x;
		Y = FaceCoords[i+1][1].y - BaseCenter[i].y;
		Angle = ATAN(Y/X);
		BaseOffset[i] = Angle;
		XScale[i] = Vec3d_Dist(BaseCenter[i], FaceCoords[i+1][1]);

		X = FaceCoords[i+1][2].x - BaseCenter[i].x;
		Y = FaceCoords[i+1][2].y - BaseCenter[i].y;
		Angle = ATAN(Y/X);
		BaseRotate[i] = 4 * (Angle - BaseOffset[i]);
		YScale[i] = Vec3d_Dist(BaseCenter[i], FaceCoords[i+1][2]);
	}

	deDouble Angle, AngIncrement;
	deDouble PercentRotated, PercentInc;
	deDouble TexAngle;

	// make a simple, transformable cylinder
	Angle = 0;
	PercentRotated = 0;
	AngIncrement = (DE_PI * 2.0) / ((deDouble)Faces);
	PercentInc = 1.0 / ((deDouble)Faces);
	Height /= 2.0;
	deVec3d TempVert;
	for (i = 0; i < Faces; i++)
	{
		Points0[i+1].x = deFloat(COS(Angle) * Radius);
		Points0[i+1].z = deFloat(SIN(Angle) * Radius);
		Points0[i+1].y = deFloat(Height);

		Points1[i+1].x = Points0[i+1].x;
		Points1[i+1].z = Points0[i+1].z;
		Points1[i+1].y = deFloat(-Height);

		SideNorms[i*2].x = Points0[i+1].x;
		SideNorms[i*2].z = Points0[i+1].z;
		SideNorms[i*2].y = 0;
		SideNorms[i*2+1] = SideNorms[i*2];

		TempVert = (FaceCoords[0][1] - FaceCoords[0][0])*(PercentRotated) + FaceCoords[0][0];
		SideCoords[i*2].u = (deFloat)TempVert.x;
		SideCoords[i*2].v = (deFloat)TempVert.y;
		TempVert = (FaceCoords[0][3] - FaceCoords[0][2])*(PercentRotated) + FaceCoords[0][2];
		SideCoords[i*2+1].u = (deFloat)TempVert.x;
		SideCoords[i*2+1].v = (deFloat)TempVert.y;
		TexAngle = BaseOffset[0] + (BaseRotate[0] * PercentRotated);
		BaseCoords0[i+1].u = (deFloat)(BaseCenter[0].x + (COS(TexAngle)*(XScale[0])));
		BaseCoords0[i+1].v = (deFloat)(BaseCenter[0].y + (SIN(TexAngle)*(YScale[0])));
		TexAngle = BaseOffset[1] + (BaseRotate[1] * PercentRotated);
		BaseCoords1[i+1].u = (deFloat)(BaseCenter[1].x + (COS(TexAngle)*(XScale[1])));
		BaseCoords1[i+1].v = (deFloat)(BaseCenter[1].y + (SIN(TexAngle)*(YScale[1])));

		Angle += AngIncrement;
		PercentRotated += PercentInc;
	}
	{
		// i = Faces;
		SideNorms[i*2].x = Points0[1].x;
		SideNorms[i*2].z = Points0[1].z;
		SideNorms[i*2].y = 0;
		SideNorms[i*2+1] = SideNorms[i*2];

		TempVert = (FaceCoords[0][1] - FaceCoords[0][0])*(PercentRotated) + FaceCoords[0][0];
		SideCoords[i*2].u = (deFloat)TempVert.x;
		SideCoords[i*2].v = (deFloat)TempVert.y;
		TempVert = (FaceCoords[0][3] - FaceCoords[0][2])*(PercentRotated) + FaceCoords[0][2];
		SideCoords[i*2+1].u = (deFloat)TempVert.x;
		SideCoords[i*2+1].v = (deFloat)TempVert.y;
	}

	// let's make some triangles
	deVertex * TopNormals = new deVertex[Faces+1];
	deVertex * BotNormals = new deVertex[Faces+1];
	deVertex * SideVerts = new deVertex[(Faces+1)*2];
	unsigned short * TopIndices = new unsigned short[(Faces)*3];
	unsigned short * BotIndices = new unsigned short[(Faces)*3];
	unsigned short * SideIndices = new unsigned short[(Faces)*6];
	BIndexList Top, Bottom, Sides;

	// put in the middles
	Points0[0].x = 0;
	Points0[0].z = 0;
	Points0[0].y = (deFloat)(Height);
	Points1[0].x = 0;
	Points1[0].z = 0;
	Points1[0].y = (deFloat)(-Height);
	BaseCoords0[0].u = (deFloat)BaseCenter[0].x;
	BaseCoords0[0].v = (deFloat)BaseCenter[0].y;
	BaseCoords1[0].u = (deFloat)BaseCenter[1].x;
	BaseCoords1[0].v = (deFloat)BaseCenter[1].y;
	TopNormals[0] = Up;
	BotNormals[0] = Down;

	// initialize the Top index lists
	Top.positions = Points0;
	Top.normals = TopNormals;
	Top.uvcoords = BaseCoords0;
	Top.indices = TopIndices;
	Top.numpoints = Faces+1;
	Top.numindices = (Faces)*3;

	// initialize the Bottom index lists
	Bottom.positions = Points1;
	Bottom.normals = BotNormals;
	Bottom.uvcoords = BaseCoords1;
	Bottom.indices = BotIndices;
	Bottom.numpoints = Faces+1;
	Bottom.numindices = (Faces)*3;

	unsigned short x;
	// generate some indices for the caps
	for (x = 0; x < Faces-1; x++)
	{
		TopIndices[x*3+0] = 0;
		TopIndices[x*3+1] = (unsigned short)(x + (2 - Winding));
		TopIndices[x*3+2] = (unsigned short)(x + (1 + Winding));

		BotIndices[x*3+0] = 0;
		BotIndices[x*3+1] = (unsigned short)(x + (1 + Winding));
		BotIndices[x*3+2] = (unsigned short)(x + (2 - Winding));

		TopNormals[x+1] = Up;
		BotNormals[x+1] = Down;
	}
	// last point refers back to the first one
	{
		//x = Faces-1;
		if (Winding)
		{
			TopIndices[x*3+0] = 0;
			TopIndices[x*3+1] = (unsigned short)(x + 1);
			TopIndices[x*3+2] = (unsigned short)(1);

			BotIndices[x*3+0] = 0;
			BotIndices[x*3+1] = (unsigned short)(1);
			BotIndices[x*3+2] = (unsigned short)(x + 1);
		}
		else
		{
			TopIndices[x*3+0] = 0;
			TopIndices[x*3+1] = (unsigned short)(1);
			TopIndices[x*3+2] = (unsigned short)(x + 1);

			BotIndices[x*3+0] = 0;
			BotIndices[x*3+1] = (unsigned short)(x + 1);
			BotIndices[x*3+2] = (unsigned short)(1);
		}

		TopNormals[x+1] = Up;
		BotNormals[x+1] = Down;
	}

/* and for the sides
  0   2   4...
  +---+---+-
  |  /|  /|
  | / | / |
  |/  |/  |/
  +---+---+- 
  1   3   5...
*/

	// initialize the side index lists
	Sides.positions = SideVerts;
	Sides.normals = SideNorms;
	Sides.uvcoords = SideCoords;
	Sides.indices = SideIndices;
	Sides.numpoints = (Faces+1)*2;
	Sides.numindices = (Faces)*6;

	for (x = 0; x < Faces; x++)
	{
		// of course we need the positions themselves
		SideVerts[x*2] = Points0[x+1];
		SideVerts[x*2+1] = Points1[x+1];
	}
	{
		// x = Faces;
		SideVerts[x*2] = Points0[1];
		SideVerts[x*2+1] = Points1[1];
	}
	for (x = 0; x < Faces; x++)
	{
		SideIndices[x*6+0] = (unsigned short)(x*2);
		SideIndices[x*6+1] = (unsigned short)(x*2 + (2 - Winding));
		SideIndices[x*6+2] = (unsigned short)(x*2 + (1 + Winding));

		SideIndices[x*6+3] = (unsigned short)(x*2 + (1 + Winding));
		SideIndices[x*6+4] = (unsigned short)(x*2 + (2 - Winding));
		SideIndices[x*6+5] = (unsigned short)(x*2 + 3);
	}

	// send off our index lists to be turned into vertex buffers
	Sides.numtexstages = 1;
	Top.numtexstages = 1;
	Bottom.numtexstages = 1;
	ClearGeometry();
	m_UsingIndexedBuffer = deTRUE;
	m_IndexedBuffers[0].AddElement(Sides);
	m_IndexedBuffers[1].AddElement(Top);
	m_IndexedBuffers[2].AddElement(Bottom);
	FillIndexedBuffers();

	// set our world transform to the center of the cylinder
	m_WorldTransform = M;

	// clear up all the temporary arrays we have been using
	delete[] Points0;
	delete[] Points1;
	delete[] SideNorms;
	delete[] SideCoords;
	delete[] BaseCoords0;
	delete[] BaseCoords1;
	delete[] TopNormals;
	delete[] BotNormals;
	delete[] SideVerts;
	delete[] TopIndices;
	delete[] BotIndices;
	delete[] SideIndices;
}

deDouble deCylinderBrush::Dist(const deVec3d &LineP1, const deVec3d &LineP2, const deVec3d &P3)
{
	deVec3d LineVec, tempvec;
	LineVec = LineP1;
	LineVec -= LineP2;
	Vec3d_Normalize(LineVec);
	tempvec = LineP1 - P3;
	tempvec = Vec3d_Cross(LineVec, tempvec);
	return Vec3d_Length(tempvec);
}

deBrush * deCylinderBrush::CopyBrush()
{
	deCylinderBrush * b = NULL;
	b = new deCylinderBrush;
//	*b = *this;
	return b;
}

deBoolean deCylinderBrush::Serialize(IdeFile* FS)
{
	if (!deBrush::Serialize(FS))
		return deFALSE;
	return deTRUE;
}

deBoolean deCylinderBrush::DeSerialize(IdeFile* FS, long DataLength)
{
	if (!deBrush::DeSerialize(FS, DataLength))
		return deFALSE;
	return deTRUE;
}

