///////////////////////////////////////////////////////////////////////////////
/// @file ExtrudedBrush.cpp
///
/// @brief Creation of extruded brushes
///
/// @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.h>
#include "deMath.hpp"
//#include "octree.hpp"

WorldObjectClassImp(deExtrudedBrush);

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

deExtrudedBrush::deExtrudedBrush()
:deBrush(Brush_Extruded, s_ExtrudedQuantities)
{
	m_SourceLine = NULL;
	m_ExtrudeLine = NULL;
	m_Solid = true;
}

deExtrudedBrush::~deExtrudedBrush()
{
	m_SourceLine->Release();
	m_ExtrudeLine->Release();
}

void deExtrudedBrush::SetSourceLine(IdeLine * line)
{
	if (m_SourceLine)
		m_SourceLine->Release();
	m_SourceLine = line->Copy();
	m_Changed = deTRUE;
}

void deExtrudedBrush::SetExtrudeLine(IdeLine * line)
{
	if (m_ExtrudeLine)
		m_ExtrudeLine->Release();
	m_ExtrudeLine = line->Copy();
	m_Changed = deTRUE;
}

void deExtrudedBrush::MakeSolid(deBoolean IsSolid)
{
#pragma todo (unfinished function: MakeSolid)
	IsSolid = deFALSE;
}

void deExtrudedBrush::UpdateBrush()
{
	Extrude();
}

#define EXTRUDE_TRANSFORM (1)
#define EXTRUDE_PROFILE (0)
#if EXTRUDE_PROFILE
#include "tsc.h"
#endif

void deExtrudedBrush::Extrude()
{
#if EXTRUDE_PROFILE
	pushTSC();
#endif
	m_PolyList.EmptyList();
//	m_Octree->Clear();
	if (m_SourceLine == NULL || m_ExtrudeLine == NULL)
		return;

	int i;
	deVec3d Offset, Orientation;
	deTexCoord FaceCoords[3][4];
	deDouble PercentX, PercentY;
	m_ExtrudeLine->GetPointOnLine(0, Offset);
	m_ExtrudeLine->GetTangentAtPoint(0, Orientation);
	deDouble OneOverLOD = 1.0 / m_LOD;
	deDouble OneOverLOD2 = 1.0 / m_LOD2;
	int LengthE = m_ExtrudeLine->GetLength();
	int LengthS = m_SourceLine->GetLength();
	int NumPoints = LengthS * m_LOD + 1;
	deBPoly p;

	deVec3d *Points0, *Points1, *Points2;
	Points0 = new deVec3d[NumPoints];
	Points1 = new deVec3d[NumPoints];
	Points2 = new deVec3d[NumPoints];
	deVec3d *Norms0,*Norms1, *Norms2;
	Norms0 = new deVec3d[NumPoints];
	Norms1 = new deVec3d[NumPoints];
	Norms2 = new deVec3d[NumPoints];
	deTexCoord *SideCoords1, *SideCoords2;
	SideCoords1 = new deTexCoord[NumPoints];
	SideCoords2 = new deTexCoord[NumPoints];

	for (i = 0; i < 4; i++)
	{
		FaceCoords[0][i] = m_FaceList[0][i];
	}
	for (i = 0; i < 3; i++)
	{
		FaceCoords[1][i] = m_FaceList[1][i];
		FaceCoords[2][i] = m_FaceList[2][i];
	}

	// the points that we will be using must be transformed
	// to local coords so they can then be transformed into the correct position
	deTransform x, xminus;
	{
		deVec3d L,U={0,1,0},I;
		m_ExtrudeLine->GetTangentAtPoint(0, I);
		Vec3d_Normalize(I);
		if (Vec3d_Compare(U,I))
		{
			U.Y = 0; U.X = -1;
		}
		L = Vec3d_Cross(U,I);
		Vec3d_Normalize(L);
		Vec3d_Normalize(I);
		U = Vec3d_Cross(I,L);
		x.SetRightUpIn(L,U,I);
//		x.RotateZ(DE_PI);
		x.SetTranslation(Offset);
		x.GetTranspose(xminus);
	}
	// now we get all the point on the line we are going to use
	int k = 0;
	int j = 0;
	deDouble f = 0;
	deVec3d LinePoint;
#if EXTRUDE_TRANSFORM
	deVec3d Tangent, Norm;
#endif
	// count integers
	for (j = 0; j < LengthS; j++)
	{	// iterate through each constituent line (groups only)
		// count fractions
		while (k < (j+1)*m_LOD+1)
		{	// sample the line at various places
			m_SourceLine->GetPointOnLine(f+j, LinePoint);
			//Distance = Dist(m_CVList[0], m_CVList[1], LinePoint);
			Points1[k] = LinePoint;
#if EXTRUDE_TRANSFORM
			m_ExtrudeLine->GetTangentAtPoint(0, LinePoint);
			m_SourceLine->GetTangentAtPoint(f+j, Tangent);
			Norm = Vec3d_Cross(Tangent, LinePoint);
			Vec3d_Normalize(Norm);
			Norms1[k] = Norm;
#endif
			k++;
			f += OneOverLOD;
			if (f > 1)
				f = 1;
		}
		f = OneOverLOD;
	}
	int CountCoord;
	for (CountCoord = 0; CountCoord < NumPoints; CountCoord++)
	{
		PercentX = ((deDouble)CountCoord)/(NumPoints-1);
		SideCoords1[CountCoord] = (FaceCoords[0][3] - FaceCoords[0][2])*((deFloat)PercentX) + FaceCoords[0][2];
	}
	// put the beginning points in the right place
	xminus.TransformArray(Points1, Points0, NumPoints);
#if EXTRUDE_TRANSFORM
	xminus.TransformArray(Norms1, Norms0, NumPoints);
#else
	{
		deDouble dbl;
		deVec3d I, vec, norm;
		int count = 0;
		m_ExtrudeLine->GetTangentAtPoint(0, I);
		//Vec3d_Normalize(I);
		for (dbl = 0; count < LengthS*m_LOD+1; dbl += OneOverLOD)
		{
			m_SourceLine->GetTangentAtPoint(dbl, vec);
			norm = Vec3d_Cross(vec, I);
			Vec3d_Normalize(norm);
			Norms1[count] = deMake_Vec3d(norm);
			count++;
		}
	}
#endif
	int ptcount;
	deBoolean LineComplete = Vec3d_Compare(Points0[0],Points0[NumPoints-1]);
	// and let;s go through each step of the LOD and connect the dots
	for (j = 0; j < LengthE; j++)
	{
		f = OneOverLOD2;	// we start here because we already have f=0
		for (k = 0; k < m_LOD2; k++)
		{
			// this would be cleaner if i could do away with the ge<->de stuff
			deVec3d L,U,I;
			m_ExtrudeLine->GetTangentAtPoint(f+j, I);
			//Vec3d_Normalize(I);
#if !EXTRUDE_TRANSFORM
			{
				deDouble dbl;
				deVec3d vec, norm;
				int count = 0;
				for (dbl = 0; count < LengthS*m_LOD+1; dbl += OneOverLOD)
				{
					m_SourceLine->GetTangentAtPoint(dbl, vec);
					norm = Vec3d_Cross(vec, I);
					Vec3d_Normalize(norm);
					Norms2[count] = geVec(norm);
					count++;
				}
			}
#endif
			for (CountCoord = 0; CountCoord < NumPoints; CountCoord++)
			{
				PercentY = (j+f)/LengthE;
				PercentX = ((deDouble)CountCoord)/(NumPoints-1);
				SideCoords2[CountCoord] = ((FaceCoords[0][3] - FaceCoords[0][2])*((deFloat)PercentX) + FaceCoords[0][2])*((deFloat)(1-PercentY));
				SideCoords2[CountCoord] = SideCoords2[CountCoord] + ((FaceCoords[0][1] - FaceCoords[0][0])*((deFloat)PercentX) + FaceCoords[0][0])*((deFloat)PercentY);
			}

			x.GetUp(U);
			L = Vec3d_Cross(U,I);
			Vec3d_Normalize(L);
			Vec3d_Normalize(I);
			U = Vec3d_Cross(I,L);
			x.SetRightUpIn(L,U,I);
//			x.RotateZ(DE_PI);
			deVec3d temp;
			m_ExtrudeLine->GetPointOnLine(f+j, temp);
			x.SetTranslation(temp);
			f += OneOverLOD2;
			if (f > 1)
				f = 1;
			// transform into points2 because points1 carries over from before
			x.TransformArray(Points0, Points2, NumPoints);
#if EXTRUDE_TRANSFORM
			x.TransformArray(Norms0, Norms2, NumPoints);
#endif

			for (ptcount = 0; ptcount < NumPoints-1; ptcount++)
			{
				MakeTri(p, Points1[ptcount], Points1[ptcount+1], Points2[ptcount],
					Norms1[ptcount], Norms1[ptcount+1], Norms2[ptcount],
					SideCoords1[ptcount], SideCoords1[ptcount+1], SideCoords2[ptcount]);
				m_PolyList.AddElement(p);
//				m_Octree->AddPoly(p);
				MakeTri(p, Points2[ptcount+1], Points2[ptcount], Points1[ptcount+1], 
					Norms2[ptcount+1], Norms2[ptcount], Norms1[ptcount+1],
					SideCoords2[ptcount+1], SideCoords2[ptcount], SideCoords1[ptcount+1]);
				m_PolyList.AddElement(p);
//				m_Octree->AddPoly(p);
			}
			
			// if we want solid and the line doesn't complete itself
			if (m_Solid && !LineComplete)
			{
				MakeTri(p, Points1[NumPoints-1], Points1[0], Points2[NumPoints-1]);
				m_PolyList.AddElement(p);
//				m_Octree->AddPoly(p);
				MakeTri(p, Points2[0], Points2[NumPoints-1], Points1[0]);
				m_PolyList.AddElement(p);
//				m_Octree->AddPoly(p);
			}
			

			// probably need to add some stuff to make the extrude solid

			void * p;
			p = Points1;
			Points1 = Points2;
			Points2 = (deVec3d*)p;
			p = Norms1;
			Norms1 = Norms2;
			Norms2 = (deVec3d*)p;
			p = SideCoords1;
			SideCoords1 = SideCoords2;
			SideCoords2 = (deTexCoord*)p;
		}
	}

	delete[] Points0;
	delete[] Points1;
	delete[] Points2;
	delete[] Norms0;
	delete[] Norms1;
	delete[] Norms2;

#if EXTRUDE_PROFILE
	pushTSC();
#endif

//	m_Octree->Generate();

#if EXTRUDE_PROFILE
	double diff1 = popTSC();
	double diff2 = popTSC();
	diff1 = diff2;
	/*
	transform
	0.0038305044987147
	0.0040119511568123
	0.0037322840616967
	not transform
	0.0049570057840617
	0.0049917622107969
	0.0046664884318766
	*/
#endif
}

deBrush * deExtrudedBrush::CopyBrush()
{
	deExtrudedBrush * b = NULL;
	b = new deExtrudedBrush;
	b->m_SourceLine = m_SourceLine;
	b->m_ExtrudeLine = m_ExtrudeLine;
	b->m_Solid = m_Solid;
	return b;
}

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

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

