/****************************************************************************************

		TreePrim.cpp

		Copyright (c)1990 - 1999 MetaCreations. All rights reserved.

		Author:	Jerome Bignon
		Date:	05/17/99

		Revision History

		when		who			what & why
		==========	==========	======================================================
		08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods
		08/20/99	cph			updated to remove IExtension
		08/20/99	cph			updated to remove COM prefix
		06/27/99	cph			CanBePlit() uses BasicPrimitive implementation now; 
								added include for MCCountedPtrHelper.h
		06/11/99	Jerome		Added animation and saving
		05/17/99	Jerome		Creation
		07/22/99	Mael 		added GetPolygonInfo

*****************************************************************************************/

#include "math.h"

#include "TreePrim.h"
#include "ITreePrim.h"
#include "Modeler.h"
#include "ModelerView.h"
#include "COMUtilities.h"
#include "COM3DUtilities.h"
#include "COMSafeUtilities.h"
#include "IShUtilities.h"
#include "MCException.h"
#include "MCCountedPtrHelper.h"
#include "MCBasicTypes.h"
#include "MiscComUtilsImpl.h"

static int16 gBran = 0;
static int16 gStep = 0;
static real	gAngl = 0;
static int16 gTreeIndex = 0;

const MCGUID CLSID_TreePrim ( R_CLSID_TreePrim );
const MCGUID IID_ITreePrim ( R_IID_ITreePrim );


void Init3DUtilities(IMCUnknown* utilities);

//-- Methods for the creation 

void Extension3DInit(IMCUnknown* utilities)
{
	// Perform your dll initialization here
}

void Extension3DCleanup()
{
	// Perform any nec clean-up here
}

TBasicUnknown* MakeCOMObject(const MCCLSID& classId)	// This method instanciate
{														// the objects for Modeler
	TBasicUnknown* res = NULL;

	if (classId == CLSID_TreePrim) res = new TreePrim;
	if (classId == CLSID_Modeler) res = new Modeler;
	if (classId == CLSID_ModelerView) res = new ModelerView;

	return res;
}


//-- TreeData methods implementation ----------------------------------------------------

TreeData::TreeData()
{
	fNbBranches = 3;
	fAngle =  kRealFourthPI;
	fRecursiv = 4;
}

TreeData::~TreeData()
{
}

void TreeData::RegisterParams()
{
	TCountedTimeBased::RegisterParams();

	TMCString255 name;

	// Read the names from resource
	// DO NOT USE HARD-CODED STRINGS ! Unless your extension won't be easily localizable
	// Example : instead of TMCString name = "Number of branches"
	// you should use a resource 'STR#' and put "Number of branches" in there
	// jsut like the following lines show.

	CWhileInCompResFile resFile(kRID_GeometricPrimitiveFamilyID, kModelerID);
	
	gResourceUtilities->GetIndString(name, 136, 1);		// string  1 of STR# 136 (see Modeler.r)
	gBran = SetParam(&fNbBranches, name.StrGet(), 'Bran', kInt16ValueType);

	gResourceUtilities->GetIndString(name, 136, 2);		// string  2 of STR# 136 (see Modeler.r)
	gStep = SetParam(&fRecursiv, name.StrGet(), 'Step', kInt16ValueType);

	gResourceUtilities->GetIndString(name, 136, 3);		// string  3 of STR# 136 (see Modeler.r)
	gAngl = SetParam(&fAngle, name.StrGet(), 'Angl', kReal32ValueType);

	SetAnimated(true);
}



//-- TreePrim methods implementation -------------------------------------------------

TreePrim::TreePrim()
{
	fData = new TreeData();
	fDataChanged = true;

	fTreeBox.fMin = 
	fTreeBox.fMax = TVector3::kZero;
}

MCCOMErr TreePrim::QueryInterface(const MCIID& riid, void** ppvObj)
{
	if (MCIsEqualIID(riid, IID_I3DExAnimated))
	{
		TMCCountedGetHelper<I3DExAnimated>	result(ppvObj);
		result = this;
		return MC_S_OK;
	}

	else if (MCIsEqualIID(riid, IID_IExStreamIO))
	{
		TMCCountedGetHelper<IExStreamIO> result(ppvObj);
		result = this;
		return MC_S_OK;
	}

	else if (MCIsEqualIID(riid, IID_ITreePrim))
	{
		TMCCountedGetHelper<ITreePrim>	result(ppvObj);
		result = this;
		return MC_S_OK;
	}
	else
		return TBasicPrimitive::QueryInterface(riid, ppvObj);
}

uint32 TreePrim::AddRef()
{
	return TBasicPrimitive::AddRef();
}

  
MCCOMErr TreePrim::ExtensionDataChanged()
{
	return MC_S_OK;
}

int16 TreePrim::GetResID()
{
	return -1; // always return -1, if you do not have a view
}

boolean TreePrim::CanBeSplit()
{
	return TBasicPrimitive::CanBeSplit();
}

MCCOMErr TreePrim::SplitPrimitive(TMCCountedPtrArray<I3DExGeometricPrimitive>& subParts, TMCArray<TTransform3D>& subPartPositions)
{
	return TBasicPrimitive::SplitPrimitive(subParts, subPartPositions);
}


boolean	TreePrim::AutoSwitchToModeler() const 
{
	return true;
};

TVector3 TreePrim::Rotate( TVector3 &Vertex , TVector3 &angle )
{
	TVector3 VertexRot;

	VertexRot[0] = Vertex[0];
	VertexRot[1] = Vertex[2] * sin( angle[0] ) + Vertex[1] * cos( angle[0] );
	VertexRot[2] = Vertex[2] * cos( angle[0] ) - Vertex[1] * sin( angle[0] );
	
	Vertex[2] = VertexRot[0] * sin( angle[1] ) + VertexRot[2] * cos( angle[1] );
	Vertex[0] = VertexRot[0] * cos( angle[1] ) - VertexRot[2] * sin( angle[1] );
	Vertex[1] = VertexRot[1];	

	VertexRot[0] = Vertex[1] * sin( angle[2] ) + Vertex[0] * cos( angle[2] );
	VertexRot[1] = Vertex[1] * cos( angle[2] ) - Vertex[0] * sin( angle[2] );
	VertexRot[2] = Vertex[2];

	return VertexRot;
}  

TVector3 TreePrim::Normal( TVertex3D	*fVertices )
{
	TVector3 v1,v2,normal;
	int16 j;

	// Normal vector calculation
	for (j=0; j<3; j++)
	{
		v1[j]=fVertices[1].fVertex[j]-fVertices[0].fVertex[j];
		v2[j]=fVertices[2].fVertex[j]-fVertices[0].fVertex[j];
		normal[j]=v2[(j+1)%3]*v1[(j+2)%3] - v2[(j+2)%3]*v1[(j+1)%3];
	}

	real norm=sqrt(normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2]);
	normal[0] /= norm;
	normal[1] /= norm;
	normal[2] /= norm; // Normalization
		
	return normal;
}

/////////////////////////////////////////////////////////////////
// Cube :
// Construct TreePrim for a cube
// size     : size of the cube in the z axis.
// angle    : rotation around (0,0,0).
// translate: vector to translate the cube after the rotation.
// return   : the final translation vector.
/////////////////////////////////////////////////////////////////

#define VERTEXNUMBER 14
#define VERTICESNUMBER 9

const int16 vertex_index[VERTEXNUMBER][3] = {
	{ 0 , 1 , 4 },
	{ 1 , 5 , 4 },
	{ 1 , 2 , 5 },
	{ 2 , 6 , 5 },
	{ 2 , 3 , 6 },
	{ 3 , 7 , 6 },
	{ 3 , 0 , 7 },
	{ 0 , 4 , 7 },
	{ 0 , 2 , 1 },
	{ 0 , 3 , 2 },	
	{ 4 , 7 , 8 },
	{ 8 , 5 , 4 },
	{ 8 , 6 , 5 },
	{ 6 , 7 , 8 }
	};

const real vertex[VERTICESNUMBER][3] = {
	{  -0.0300f  , -0.0300f , 0.00000f },
	{   0.0300f  , -0.0300f , 0.00000f },
	{   0.0300f  ,  0.0300f , 0.00000f },
	{  -0.0300f  ,  0.0300f , 0.00000f },
	{   0.0000f  , -0.0300f , 1.00000f },
	{   0.0300f  , -0.0300f , 1.00000f },
	{   0.0300f  ,  0.0300f , 1.00000f },
	{  -0.0300f  ,  0.0300f , 1.00000f },
	{   0.0000f  ,  0.0000f , 1.10000f }
	};


TVector3 TreePrim::Cube(FacetMeshAccumulator* accum		, 
										 real SizeZ     , 
										 real SizeXY    ,
										 TVector3 &angle , 
										 TVector3 &translate)
{
	int16 i,j;
	TFacet3D  TreeCube;	 
	TVector3 VectorTemp;
	TVector3 Vertices[VERTICESNUMBER];

	// 3D coordinates calculation
	for(i=0;i<VERTICESNUMBER;i++)
	{
		VectorTemp[0] = (vertex[i][0]) ;//+ SizeXY ; // *** BUG With the Bounding BOX
		VectorTemp[1] = (vertex[i][1]) ;//+ SizeXY ; 
		VectorTemp[2] = (vertex[i][2]) * SizeZ / 2 ;

		VectorTemp = Rotate( VectorTemp , angle );
		
		Vertices[i][0] =  VectorTemp[0] +  translate[0] ;
		Vertices[i][1] =  VectorTemp[1] +  translate[1] ;
		Vertices[i][2] =  VectorTemp[2] +  translate[2] ;			

		UpdateBoundingBox( &(Vertices[i]) );
	}

	TreeCube.fUVSpace=0;

	// Insert 3D coordinates -> vertex
	for(i=0;i<VERTEXNUMBER;i++)
	{				
		for(j=0;j<3;j++)
		{			
			TreeCube.fVertices[j].fUV[0]  = 0;
			TreeCube.fVertices[j].fUV[1]  = 0;		 
			TreeCube.fVertices[j].fVertex = Vertices[vertex_index[i][j]];  
		}	
			
		//Same normal for each of 3 Vertices
		TreeCube.fVertices[0].fNormal = 
		TreeCube.fVertices[1].fNormal = 	
		TreeCube.fVertices[2].fNormal = Normal( &(TreeCube.fVertices[0]) );

		if (accum) accum->AccumulateFacet(&TreeCube);    
	}

	// return the last vertex calculated
	return TreeCube.fVertices[2].fVertex; 
}

/*
+-----------------------------------------------------------+
| GetAngleAndPoint                                          |
+-----------------------------------------------------------+
| from a 2d coordinates (pt) , and a numView , give angle   |
| and segment (pti - pt ) of the branche of the tree near it|
| return angle=0 if no branches were found near.            |
| else, return segment coordinates of the branche :         |
| - pti : initial point (base of the branche)               |	 
| - pt  : final point (near the given point)                |	 
+-----------------------------------------------------------+ */

real TreePrim::GetAngleAndPoint( TMCPoint *pt, TMCPoint *pti, int16 numView, real zoom )
{
	real RadCoef = kRealTwoPI / ((real)fData->fNbBranches);
	real rLevel = (real)fData->fRecursiv;
	real angle = 0.0;
	int16 i;
	TVector3 translate;
	TVector3 NewAngle,NewTranslate;		
	int32 delta=100,dist;	
	TMCPoint ptf,pt0;

	translate[0] = translate[1] = translate[2] = 0;
	translate = Cube( NULL, 6.0, 2.0, translate,  translate);

	*pti = TreePoint3Dto2D( &translate , numView, zoom ); // Initial point

	for(i=0;i<fData->fNbBranches;i++)
	{		
		NewAngle[0] = fData->fAngle * cos( RadCoef * (real) i );
		NewAngle[1] = fData->fAngle * sin( RadCoef * (real) i );
		NewAngle[2] = 0.0;

		NewTranslate = Cube( NULL , ( rLevel*3.5f + 2.0f ) / fData->fRecursiv , ( rLevel/10.0f) , NewAngle ,  translate );
		
		pt0 = TreePoint3Dto2D( &NewTranslate , numView , zoom );
		
		dist = (int32)(pt->y-pt0.y)*(int32)(pt->y-pt0.y) + (int32)(pt->x-pt0.x)*(int32)(pt->x-pt0.x);
		if ( dist < delta )
		{
			angle = NewAngle[0];
			delta = dist;
			ptf = pt0;
		}
	}
	
	*pt = ptf;

	return angle;
}


MCCOMErr TreePrim::GetFacetsTree(FacetMeshAccumulator* accum, int16 level , TVector3 angle , TVector3 translate )
{
	real RadCoef = kRealTwoPI / ((real)fData->fNbBranches);
	real rLevel = (real) level;
	int16 i;
	TVector3 NewAngle,NewTranslate;		

	level--;

	for(i=0;i<fData->fNbBranches;i++)
	{		
		NewAngle[0] = angle[0] + fData->fAngle * cos( RadCoef * (real) i );
		NewAngle[1] = angle[1] + fData->fAngle * sin( RadCoef * (real) i );
		NewAngle[2] = angle[2];

		NewTranslate = Cube( accum, ( rLevel*3.5f + 2.0f ) / fData->fRecursiv , ( rLevel/10.0f ) , NewAngle ,  translate );
				
		if ( level>0 ) GetFacetsTree( accum, level , NewAngle , NewTranslate );

	}
	return MC_S_OK;
}

MCCOMErr TreePrim::UpdateBoundingBox( TVector3 *Coordinate )
{
	int16 i;
		
	for(i=0;i<3;i++)
	{
		fTreeBox.fMin[i] = (fTreeBox.fMin[i] < (*Coordinate)[i] ) ? fTreeBox.fMin[i] : (*Coordinate)[i];
		fTreeBox.fMax[i] = (fTreeBox.fMax[i] > (*Coordinate)[i] ) ? fTreeBox.fMax[i] : (*Coordinate)[i];			
	}

	return MC_S_OK;
}

void TreePrim::GetBoundingBox(TBBox3D& bbox)
{	
	bbox = fTreeBox;
}


MCCOMErr TreePrim::EnumPatches(EnumPatchesCallback callback, void* privData, boolean& closed)
{
	return TBasicPrimitive::EnumPatches(callback, privData, closed);
}

MCCOMErr TreePrim::GetNbrLOD(int16 &nbrLod)
{
	nbrLod = 1;
	return MC_S_OK;
}

MCCOMErr TreePrim::GetFacetMesh(uint32 index, FacetMesh** outMesh)
{
	TVector3 angle,translate;

	angle[0]=angle[1]=angle[2]=0;
	translate = angle;

	fTreeBox.fMin[0]= 
	fTreeBox.fMax[0]= 
	fTreeBox.fMin[1]= 
	fTreeBox.fMax[1]= 
	fTreeBox.fMin[2]= 
	fTreeBox.fMax[2]= 0.0;

	FacetMeshAccumulator accum;
	translate = Cube( &accum, 6.0 , 2.0 , angle ,  translate );

	GetFacetsTree( &accum, fData->fRecursiv  , angle , translate );
	accum.MakeFacetMesh(outMesh);

	return MC_S_OK;	
}



// -- Shading calls for the TreePrim -------------------------------------------------

uint32 TreePrim::GetUVSpaceCount()
{
	return 1; // the tree is describe with only 1 UV-Space
}

MCCOMErr TreePrim::GetUVSpace(uint32 uvSpaceID, UVSpaceInfo* uvSpaceInfo)
{
	if (uvSpaceID == 0)
	{ 
		uvSpaceInfo->fWraparound[0] = false;	// No Wrap around
		uvSpaceInfo->fWraparound[1] = false;
	}
	return MC_S_OK;
}

MCCOMErr TreePrim::GetUVSpaceRDS5(uint32 uvSpaceID, UVSpaceInfoRDS5* uvSpaceInfoRDS5)
{
	return TBasicPrimitive::GetUVSpaceRDS5(uvSpaceID,uvSpaceInfoRDS5);
}

MCCOMErr TreePrim::UV2XYZ(TVector2* uv, uint32 uvSpaceID, TVector3* resultPosition, boolean* inUVSpace)
{
	return TBasicPrimitive::UV2XYZ(uv,uvSpaceID,resultPosition,inUVSpace);
}

MCCOMErr TreePrim::AppendToRenderables(
										const TTransform3D& 	worldFromModelTfm,
										TRenderableAndTfmArray& renderableAndTfm )
{
	return TBasicPrimitive::AppendToRenderables(worldFromModelTfm, renderableAndTfm);
}

//-- End of the shading calls for the TreePrim ---------------------------------------

//-- Data handling for the TreePrim --------------------------------------------------

/* SetData : 
return :
1 : data changed
0 : data are same
*/
boolean TreePrim::SetData(const TreeData* data)
{
	if (data->fNbBranches!=fData->fNbBranches)
	{
		fData->fNbBranches = data->fNbBranches;
		fData->MarkParamAsModified(gBran);
		fDataChanged = true;
	}
	else if (data->fAngle!=fData->fAngle)
	{
		fData->fAngle = data->fAngle;
		fData->MarkParamAsModified(gAngl);
		fDataChanged = true;
	}
	else if (data->fRecursiv!=fData->fRecursiv)
	{
		fData->fRecursiv = data->fRecursiv;
		fData->MarkParamAsModified(gStep);
		fDataChanged = true;
	}
	return fDataChanged;
}

void TreePrim::GetData(TreeData** data)
{
	TMCCountedGetHelper<TreeData> result(data);

	fData->CheckParamForRead(gBran);
	fData->CheckParamForRead(gAngl);
	fData->CheckParamForRead(gStep);

	result = fData;
};

boolean TreePrim::GetIfDataChanged()
{
	if (fDataChanged)
	{ 
		fDataChanged= false; 
		return true; 
	}
	return false;		
}
//-- End of the data Handling for the TreePrim ---------------------------------------

//-- I3DExAnimated Methods for the TreePrim ------------------------------------------

MCCOMErr TreePrim::RegisterParams()
{
	TMCCountedPtr<ITimeBased> timeBased;
	QueryInterface(IID_ITimeBased, (void**)&timeBased);
	fData->SetAnimated(true);
	fTimeBasedData = timeBased->GetTimeBasedDataNoAddRef();
	gTreeIndex = fTimeBasedData->SetParamGroup(fData, "Tree***", 'Tree');
	return MC_S_OK;
}

MCCOMErr TreePrim::InvalidateCaches(int32 itsID)
{
	return 0;
}

void TreePrim::Clone(IExDataExchanger** res, IMCUnknown* pUnkOuter)
{
	TMCCountedCreateHelper<IExDataExchanger>	result(res);

	TreePrim* theClone = new TreePrim();
	ThrowIfNil(theClone);
	theClone->SetControllingUnknown(pUnkOuter);
	CopyData(theClone);

	result = (IExDataExchanger*) theClone;
}


void TreePrim::CopyData(TreePrim* dest) const
{
	dest->fData->fNbBranches = fData->fNbBranches;
	dest->fData->fAngle = fData->fAngle;
	dest->fData->fRecursiv = fData->fRecursiv;
}


MCCOMErr TreePrim::CopyTimeData(IMCUnknown* dest)
{
// 1. Verify that the dest object supports the TreePrim Interface.

//	TMCCountedPtr<ITreePrim> destTree;

//	dest->QueryInterface(IID_ITreePrim, (void**) &destTree);
//	ThrowIfNil(destTree);

// 2. Needs the IExDataExchanger to get the internal data.

	TMCCountedPtr<IExDataExchanger> i3DDataExchanger;

	dest->QueryInterface(IID_IExDataExchanger, (void**) &i3DDataExchanger);
	ThrowIfNil(i3DDataExchanger);

// 3. Copy the data
	fData->CopyTimeData(kWithAnim, (TreeData*) i3DDataExchanger->GetExtensionDataBuffer() );

	

 	return MC_S_OK;

}


//-- IExStreamIO methods to save and read ---------------------------------------------

MCCOMErr TreePrim::Write(IShTokenStream* stream)
{
	// We write our attributes in this order Angle, Branche nbr and Step

	stream->Indent();
	stream->PutKeyword('Angl');
	stream->PutQuickFix(fData->fAngle);

	stream->Indent();
	stream->PutKeyword('Bran');
	stream->PutInt32(fData->fNbBranches);

	stream->Indent();
	stream->PutKeyword('Step');
	stream->PutInt32(fData->fRecursiv);

	gShell3DUtilities->WriteTimeBased(stream, IDTYPE('T','r','e','e'), fData);

	return MC_S_OK;
}


MCCOMErr TreePrim::Read(IShTokenStream* stream, ReadAttributeProc readUnknown, void* privData)
{
	// the keyword has been read
	int8 token[256];
	real* Angle = 0;
	int32* Branches = 0;
	int32* Recurs = 0;

	stream->GetBegin();

	try
	{
		do
		{
			int16 err=stream->GetNextToken(token);
			if(err!=0) throw TMCException(err);

			if (!stream->IsEndToken(token))
			{
				int32 keyword;
	
				stream->CompactAttribute(token, &keyword);

				switch (keyword)
				{
					case IDTYPE('T','r','e','e'): 
						gShell3DUtilities->ReadTimeBased(stream, fData);
						break;
					case IDTYPE('A','n','g','l'):
						stream->GetQuickFix(&fData->fAngle);
						break;
					case IDTYPE('B', 'r', 'a', 'n'):
						 stream->GetInt32((int32*)&fData->fNbBranches);
						break;
					case IDTYPE('S', 't', 'e', 'p'):
						stream->GetInt32((int32*)&fData->fRecursiv);
						break;
					default:
						readUnknown(keyword, stream, privData);
				}

			}
		}
		while (!stream->IsEndToken(token));
	}
	catch (TMCException& exception)
	{
		throw exception;
	}

	return MC_S_OK;
}