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

		DXF.CPP

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

		Definitions shared by the C++ files and the resource definition files (.r)


		Author: Pierre-E

		Date:	7/16/99

		Revision History

		when		who			what & why
		==========	==========	======================================================
		08/31/99	cph			fix to use new import/export API and fix GetFMesh() error
		08/20/99	cph			Put in Extension3DInit() and Extension3DCleanup() methods
		7/28/99		Pierre-E	changed a declaration for Mac compilation...

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

#include <math.h>
#include "DXF.h"

#include "MCColorRGBA.h"
#include "I3DShScene.h"
#include "I3DShGroup.h"
#include "I3DShInstance.h"
#include "ISHUtilities.h"
#include "MCBasicTypes.h"
#include "PublicUtilities.h"
#include "I3DShCamera.h"
#include "I3DShLightSource.h"
#include "I3DShObject.h"
#include "MCColor16.h"
#include "ColorRGB214.h"
#include "IShColorConversionUtilities.h"
#include "COMSafeUtilities.h"
#include "I3DShTreeElement.h"

#define kManyFacetsError 0x2BC
#define kStandardFacetsError 0x1000
#define kFewfacetsError	0x2000
#define SQR(X) (X*X)


const MCGUID CLSID_DXF(R_CLSID_DXF);



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 object COM
	TBasicUnknown* res = NULL;
	
	if (classId == CLSID_DXF) res = new TDXFExporter;
	
	return res;
}


int16 QuickFixColorToInt16(real32 col)
{
	real64 db;
	
	if (col>1.0) col = 1.0;
	db=(real64)col;
	db = db * 32767; // TGray16::kMax.ToReal();  
	return (int16) db;
}

uint16 ToUint16(int16 x) 
{
	return x*2;
}





int16 ConvertToDXFColor(const TMCColorRGB& col)
{
	uint16 dxfColor;
	uint16 hue,sat,val;
	TGray16 r(QuickFixColorToInt16(col.red));
	TGray16 g(QuickFixColorToInt16(col.green));
	TGray16 b(QuickFixColorToInt16(col.blue));
	TColorRGB214 rgb(r,g,b);
	TColorHSV214 hsv;
	TMCCountedPtr<IShColorConversionUtilities> colorConvert;

	
	gShellUtilities->QueryInterface(IID_IShColorConversionUtilities,(void**)&colorConvert);

	colorConvert->PixelRGBToHSV(rgb,hsv);

	hue = ToUint16((hsv.Hue()).ToFixed214());
	sat = ToUint16((hsv.Sat()).ToFixed214());
	val = ToUint16((hsv.Val()).ToFixed214());
	
	
	uint16 sextant = hue / 10922; 
	if ((((double(hue)/ 10922) - sextant) < 0.1) && (sat > 60000))  //standard colors
		dxfColor = sextant + 1;
	else if (sat <3000)
	{ //grays 
		int16 grayBrightness = (int16)((int32)(4680L + (int32)val ) / 9362);  
		switch (grayBrightness)
		{   
		case 0: //black
			dxfColor = 0;
			break;
		case 7: //white 
			dxfColor = 7;
			break;
		default://real grays 
			dxfColor = 249 + grayBrightness;
		}
	}
	else
	{ 
		dxfColor = 10 +	hue/273 + 2*(uint16)(((uint16)65535-val)/13107);
		if (sat<32767) dxfColor++;
		dxfColor = ((dxfColor>255)?255:dxfColor); 
	}                                             // but here it should not go further than 249
	return (int16) dxfColor;
}



//########################################################################################

struct DXFWriteFacetStruct
{
	I3DShPrimitive* primitive;
	TMCfstream* stream;
	TTreeTransform* transform;
	char* name;
	uint32 count;
	int16 color;
};


void WriteVertex(TMCfstream* stream, TVector3 vertex, int16 id)
{
	(*stream)<<(int32)1;
	(*stream)<<(int32)id;
	(*stream)<<'\n';
	(*stream)<<(real64)(vertex[0]);
	(*stream)<<'\n';
	
	(*stream)<<(int32)2;
	(*stream)<<(int32)id;
	(*stream)<<'\n';
	(*stream)<<(real64)(vertex[1]);
	(*stream)<<'\n';
	
	(*stream)<<(int32)3;
	(*stream)<<(int32)id;
	(*stream)<<'\n';
	(*stream)<<(real64)(vertex[2]);
	(*stream)<<'\n';
}


boolean IsFacetDirect(TFacet3D* facet)
{
	TVector3		NN;
	TVector3		AB;
	TVector3		AC;
	real norm;
	real vectProd;
	
	for (int i=0; i<3; i++)
	{
		NN[i] = facet->fVertices[0].fNormal[i] + facet->fVertices[1].fNormal[i] + facet->fVertices[2].fNormal[i];
		AB[i] = facet->fVertices[1].fVertex[i] - facet->fVertices[0].fVertex[i];
		AC[i] = facet->fVertices[2].fVertex[i] - facet->fVertices[0].fVertex[i];
	}
	norm=sqrt(SQR(AB[0]) + SQR(AB[1]) + SQR(AB[2]));
	AB[0]/=norm;
	AB[1]/=norm;
	AB[2]/=norm;
	//AB./*Pseudo*/Normalize();
	norm=sqrt(SQR(AC[0]) + SQR(AC[1]) + SQR(AC[2]));
	AC[0]/=norm;
	AC[1]/=norm;
	AC[2]/=norm;
	//AC./*Pseudo*/Normalize();
	
	vectProd =(AB[1]*AC[2] - AB[2]*AC[1]) * NN[0];
	vectProd+=(AB[2]*AC[0] - AB[0]*AC[2]) * NN[1];
	vectProd+=(AB[0]*AC[1] - AB[1]*AC[0]) * NN[2];
	return (vectProd >= 0.0);
}	// end 3.0.3


void WriteFacets(TFacet3D* facet, void* privData)
{
	TVector3 vertex;
	DXFWriteFacetStruct* data = (DXFWriteFacetStruct*)privData;
	TMCfstream* stream = data->stream;
	
	(*stream)<<"0\n";
	(*stream)<<"3DFACE\n";
	
	// The name and color HAS to be put in each 3dFace!!
	(*stream)<<"8\n";
	(*stream)<<(data->name);
	(*stream)<<(int32)data->count;	 // To make sure the name is different for each instance of the same object
	(*stream)<<'\n';
	
	(*stream)<<"62\n";
	(*stream)<<(int32)data->color;
	(*stream)<<'\n';
	
	// Output the vertices in the direct trigonometric order
	if (IsFacetDirect(facet))
	{
		for (int16 v=0; v<=2; v++)
		{
			(*data->transform).Transform(facet->fVertices[v].fVertex,vertex);
			WriteVertex(stream, vertex, v);
		}
	}
	else
	{
		(*data->transform).Transform(facet->fVertices[0].fVertex,vertex);
		WriteVertex(stream, vertex, 0);
		(*data->transform).Transform(facet->fVertices[2].fVertex,vertex);
		WriteVertex(stream, vertex, 2);
		(*data->transform).Transform(facet->fVertices[1].fVertex,vertex);
		WriteVertex(stream, vertex, 1);
	}
	// Fourth point equal to third
	WriteVertex(stream, vertex, 3);
}

void WriteInstance(TMCfstream* stream, I3DShInstance* anInstance, uint32 count)
{
	// Make sure this is not a light or camera instance
	TMCCountedPtr<I3DShCamera>		cameraInstance;
	TMCCountedPtr<IMCUnknown>		lightInstance;
	if (anInstance->QueryInterface(IID_I3DShCamera, (void**) &cameraInstance) == MC_S_OK
	||	anInstance->QueryInterface(IID_I3DShLightsource, (void**) &lightInstance) == MC_S_OK)
	{
		return; // we do NOT export lights or cameras
	}

	TMCCountedPtr<I3DShPrimitive>	primitive;
	TMCCountedPtr<I3DShObject>		object;
	anInstance->Get3DObject(&object);
	object->QueryInterface(IID_I3DShPrimitive, (void**) &primitive);
	if (primitive)
	{
		TMCCountedPtr<I3DShTreeElement>	tree;
		anInstance->QueryInterface(IID_I3DShTreeElement, (void**) &tree);
		// Calc Name
		TMCString1023 name;
		tree->GetName(name);

		int8 p[15];
		name.ToCPtr(p);
		int i;
		for (i=0; i<12 && p[i]!=0; i++)
		{	// Make the name AutoCAD-friendly
			if (((p[i]<'a') || (p[i]>'z')) &&
				((p[i]<'A') || (p[i]>'Z')) &&
				((p[i]<'0') || (p[i]>'9')))
			{
				p[i] = '_';
			}
		}
		p[i] = '\0';
		
		// Calc color
		int16 dxfColor;
		TMCColorRGBA col1, col2;
		anInstance->GetMainColors(col1, col2);
		dxfColor = ConvertToDXFColor(col1);
		
		// Fill data structure for call back
		DXFWriteFacetStruct ws;

		TTreeTransform transform;
		ws.primitive = primitive;
		ws.stream = stream;
		tree->GetGlobalTreeTransform(transform);

		ws.transform = &transform;
		ws.name = p;
		ws.count = count;
		ws.color = dxfColor;
		
		TMCCountedPtr<I3DShObject> anObj;
		anInstance->GetDeformed3DObject(&anObj);
		TMCCountedPtr<I3DShPrimitive> prim;
		anObj->QueryInterface(IID_I3DShPrimitive, (void **) &prim);
		if (prim)
		{
			TMCCountedPtr<FacetMesh> fmesh;
			prim->GetFMesh(0.0,&fmesh);
			FacetMeshFacetIterator facetIterator;
			facetIterator.Initialize(fmesh);
			TFacet3D facet;
			
			for (facetIterator.First(); facetIterator.More(); facetIterator.Next())
			{
				facet = facetIterator.GetFacet();
				WriteFacets(&facet, (void*)&ws);
			}	
		}
	}
}


//########################################################################################
void WriteDXFBegin(TMCfstream* stream)
{
	(*stream)<<"0\n";
	(*stream)<<"SECTION\n";
	(*stream)<<"2\n";
	(*stream)<<"HEADER\n";
	(*stream)<<"0\n";
	(*stream)<<"ENDSEC\n";
	
	(*stream)<<"999\n";                   
	(*stream)<<"Created using Carrara(R) SDK Sample\n";
	
	(*stream)<<"0\n";
	(*stream)<<"SECTION\n";
	(*stream)<<"2\n";
	(*stream)<<"ENTITIES\n";
}

void WriteDXFEnd(TMCfstream* stream)
{
	(*stream)<<"0\n";
	(*stream)<<"ENDSEC\n";
	(*stream)<<"0\n";
	(*stream)<<"EOF\n";
}


//########################################################################################

MCCOMErr TDXFExporter::DoExport(IMCFile * file, I3DShScene* scene, I3DShTreeElement* fatherTree)
{
	TMCfstream* stream = NULL;
	uint32 nbInst = 0;

	TMCCountedPtr<IMCUnknown> progressKey;
	try
	{
		nbInst =scene->GetInstanceListCount();

		// Progress bar
		TMCString255 progressMsg;
		void* oldResources = NULL;
		gResourceUtilities->SetupComponentResources('3Dou', 'COEA', &oldResources);
		gResourceUtilities->GetIndString(progressMsg, 131, 1);		// string  1 of STR# 131 (see DXF.r)
		gResourceUtilities->RestoreComponentResources(oldResources);

		gShellUtilities->BeginProgress(progressMsg, &progressKey, nbInst);

		// Open the file
		TMCString1023 fullPathName;
		file->GetFileFullPathName(fullPathName);
		stream = new TMCfstream(fullPathName.StrGet(),kAnsiTranscoder,TMCiostream::out);
		ThrowIfNil(stream);	
	
		stream->SetInfo(gShellUtilities->GetCreator(), IDTYPE('T', 'E', 'X', 'T'));
		
		WriteDXFBegin(stream);
		
		TMCCountedPtr<I3DShInstance> instance; 
		for (uint32 i=0; i<nbInst; ++i)
		{
			scene->GetInstanceByIndex(&instance,i);
			WriteInstance(stream, instance, i);
			gShellUtilities->IncrementProgress(1, progressKey);
		} 
		
		WriteDXFEnd(stream);
		
		delete stream;
		gShellUtilities->EndProgress(progressKey);
	}
	catch (TMCException& exception)
	{
		delete stream;
		gShellUtilities->EndProgress(progressKey);
		throw exception;
	}
	return MC_S_OK;
}