/*=========================================================================

	EXPMAIN.CPP

	Author:  Tim Swann @ CLIMAX
	Created:
	Project:
	Purpose:

	Copyright (c) 1998 Climax Development Ltd

===========================================================================*/

/*----------------------------------------------------------------------
	Includes
	-------- */

#include "Asciiexp.h"
#include "ExpFileIO.h"


/*	Std Lib
	------- */

/*	Glib
	---- */

/*	Local
	----- */

/*	Graphics
	-------- */

/*----------------------------------------------------------------------
	Tyepdefs && Defines
	------------------- */

#define NINJAEXP_CLASS_ID	Class_ID(0x689c7996, 0x13531d0d)

/*----------------------------------------------------------------------
	Structure defintions
	-------------------- */

/*----------------------------------------------------------------------
	Positional Vars
	--------------- */

/*----------------------------------------------------------------------
	Function Prototypes
	------------------- */

/*----------------------------------------------------------------------
	Vars
	---- */

static HINSTANCE			hInstance;
static AsciiExpClassDesc	AsciiExpDesc;
static bool					controlsInit = FALSE;


/*----------------------------------------------------------------------
	Data
	---- */

/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) 
{
	hInstance = hinstDLL;

	// Initialize the custom controls. This should be done only once.
	if (!controlsInit)
	{
		controlsInit = TRUE;
		InitCustomControls(hInstance);
		InitCommonControls();
	}

	return (TRUE);
}


/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

__declspec( dllexport ) const TCHAR* LibDescription() 
{
	return GetString(IDS_LIBDESCRIPTION);
}


__declspec( dllexport ) int LibNumberClasses() 
{
	return 1;
}


__declspec( dllexport ) ClassDesc* LibClassDesc(int i) 
{
	switch(i) {
	case 0: return GetAsciiExpDesc();
	default: return 0;
	}
}


__declspec( dllexport ) ULONG LibVersion() 
{
	return VERSION_3DSMAX;
}


/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

ClassDesc * GetAsciiExpDesc(void)
{
	return &AsciiExpDesc;
}


TCHAR * GetString(int id)
{
	static TCHAR buf[256];

	if (hInstance)
		return LoadString(hInstance, id, buf, sizeof(buf)) ? buf : NULL;

	return NULL;
}


/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

AsciiExp::AsciiExp()
{
}


AsciiExp::~AsciiExp()
{
}


int AsciiExp::ExtCount()
{
	return 1;
}


const TCHAR * AsciiExp::Ext(int n)
{
	switch(n)
	{
	case 0:
		return _T("GIN");
	}
	return _T("");
}


const TCHAR * AsciiExp::LongDesc()
{
	return GetString(IDS_LONGDESC);
}


const TCHAR * AsciiExp::ShortDesc()
{
	return GetString(IDS_SHORTDESC);
}


const TCHAR * AsciiExp::AuthorName() 
{
	return _T("Tim Swann / Mike Armstrong");
}


const TCHAR * AsciiExp::CopyrightMessage() 
{
	return GetString(IDS_COPYRIGHT);
}


const TCHAR * AsciiExp::OtherMessage1() 
{
	return _T("");
}


const TCHAR * AsciiExp::OtherMessage2() 
{
	return _T("");
}


unsigned int AsciiExp::Version()
{
	return 100;
}


static BOOL CALLBACK AboutBoxDlgProc(HWND hWnd, UINT msg, 
	WPARAM wParam, LPARAM lParam)
{
	switch (msg) {
	case WM_INITDIALOG:
		CenterWindow(hWnd, GetParent(hWnd)); 
		break;
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:
			EndDialog(hWnd, 1);
			break;
		}
		break;
		default:
			return FALSE;
	}
	return TRUE;
}       


void AsciiExp::ShowAbout(HWND hWnd)
{
//	DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutBoxDlgProc, 0);
}


static BOOL CALLBACK ExportDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	AsciiExp * exp = (AsciiExp*)GetWindowLong(hWnd, GWL_USERDATA); 

	switch (msg)
	{
	case WM_INITDIALOG:
		exp = (AsciiExp*)lParam;
		SetWindowLong(hWnd,GWL_USERDATA,lParam); 
		CenterWindow(hWnd, GetParent(hWnd)); 
//		CheckDlgButton(hWnd, IDC_ANIMCHECK, exp->GetExportAnimFlag());
		break;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDOK:
//			exp->SetExportAnimFlag(IsDlgButtonChecked(hWnd, IDC_ANIMCHECK));
			EndDialog(hWnd, 1);
			break;

		case IDCANCEL:
			EndDialog(hWnd, 0);
			break;
		}
		break;
		default:
			return FALSE;
	}
	return TRUE;
}


// Dummy function for progress bar
DWORD WINAPI fn(LPVOID arg)
{
	return(0);
}


/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

#if MAX_RELEASE == 2500
int AsciiExp::DoExport(const TCHAR * name, ExpInterface * ei, Interface * i, BOOL suppressPrompts)
#else
int AsciiExp::DoExport(const TCHAR * name, ExpInterface * ei, Interface * i, BOOL suppressPrompts,DWORD options)
#endif
{
	int		numChildren;
//	NodeCount = 0;


	// GRAB THE INTERFACE POINTER
	ip = i;
	MAX_hWnd = ip->GetMAXHWnd();
//	SetExportAnimFlag(0);
//	if (!suppressPrompts && !DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ASCIIEXPORT_DLG), MAX_hWnd, ExportDlgProc, (LPARAM)this)) return 1;

	tempStream = NULL;
	expStream = NULL;
	nbChunks = 0;


	// GET SCENE FILENAME
	sprintf( SceneName, "%s", FixupName(ip->GetCurFileName()) );
	_strupr( SceneName );


	// OPEN DEBUG TEXT FILE
	tempStream = _tfopen("C:\\ExpDebug.txt",_T("wt"));
	if (!tempStream)	return 0;

	fprintf( tempStream, "TEST\n" );

	// OPEN OUTPUT FILE
	_splitpath(name, drive, dir, fname, ext);
	sprintf(filename, "%s%s%s.GIN", drive, dir, fname);
	_strlwr(filename);
	expStream = _tfopen(filename, _T("wb"));
	if (!expStream)		return 0;

	// WRITE FILE HEADER
	if (WriteChunkHdr( (char*)FILE_ID, 0 ))
	{
		// STARTUP THE PROGRESS BAR
//		if (GetExportAnimFlag())
//			ip->ProgressStart("Exporting with Anims", TRUE, fn, NULL);
//		else
			ip->ProgressStart("Exporting without Vertex Anims", TRUE, fn, NULL);


		Weights = NULL;
		nTotalNodeCount = 0;
		nCurNode = 0;
		nCurObj = 0;
		nbFrames = 0;
		totFrames = 0;
		nbWeights = 0;
		NbEmmitters = 0;

		GetNoFrames();
		fwrite(&totFrames, sizeof(int), 1, expStream);

		WriteChunkHdr( (char*)VERSION_ID, 0);
		int MyVer = VERSION;
		fwrite(&MyVer, sizeof(int), 1, expStream);

		OrigProcess(ip->GetRootNode());
		FfdProcess(ip->GetRootNode(), nTotalNodeCount);

		ExportTree();

		numChildren = ip->GetRootNode()->NumberOfChildren();
		for (int idx=0; idx<numChildren; idx++)
		{
			if (ip->GetCancel())	break;
			nodeEnum( ip->GetRootNode()->GetChildNode(idx));
		}

		ExportMaterialList();
	}

	// WRITE FILE TERMINATOR HEADER
	WriteChunkHdr( (char*)TERMINATOR_ID, 0 );


	// Close the streams
	if (tempStream)	fclose(tempStream);
	if (expStream)	fclose(expStream);
	if (Weights)	free(Weights);

	// FILL IN CHUNK SIZES
	CalcChunkSizes();

	// WE'RE DONE. FINISH THE PROGRESS BAR.
	ip->ProgressEnd();

/*	if (!DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_ENDDIALOG), MAX_hWnd, ExportDlgProc, (LPARAM)this))
	{
		return 1;
	}*/


	return 1;
}


/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

BOOL AsciiExp::nodeEnum(INode* node) 
{
	nCurNode++;

	ip->ProgressUpdate((int)((float)nCurNode/nTotalNodeCount*100.0f)); 

	if (ip->GetCancel())	return FALSE;

	ExportNode(node);

	for (int c = 0; c < node->NumberOfChildren(); c++)
	{
		if (!nodeEnum(node->GetChildNode(c)))	return FALSE;
	}

	return TRUE;
}


void AsciiExp::BoneProcess(INode* node, int& nodeCount)
{
	nodeCount++;

	// GET WEIGHTS BEFORE BONES ARE EXPORTED
	ExportWeights( node );

	for (int c = 0; c < node->NumberOfChildren(); c++)
	{
		BoneProcess(node->GetChildNode(c), nodeCount);
	}
}


void AsciiExp::FfdProcess(INode* node, int& nodeCount)
{
	nodeCount++;

	// GET WEIGHTS BEFORE BONES ARE EXPORTED
//	ExportFFD( node );
	Control *cont = node->GetTMController();
	if (cont) {

		if ((cont->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) ||
			(cont->ClassID() == BIPBODY_CONTROL_CLASS_ID) ||
			(cont->ClassID() == FOOTPRINT_CLASS_ID))
		{
			IBipedExport *BipIface = (IBipedExport *) cont->GetInterface(I_BIPINTERFACE);
			if (BipIface)
			{
				//BipIface->RemoveNonUniformScale(1);
			}
		}
	}

	for (int c = 0; c < node->NumberOfChildren(); c++)
	{
		FfdProcess(node->GetChildNode(c), nodeCount);
	}
}

void AsciiExp::OrigProcess(INode *node)
{
	char TempName[256];
	sprintf(TempName, "%s", node->GetName());
	_strupr(TempName);
	if (strcmp(TempName, "ORIGIN")==0) {
		Matrix3		tm = node->GetObjTMAfterWSM( ip->GetAnimRange().Start() );
		AffineParts	ap;
		decomp_affine(tm, &ap);
		WriteChunkHdr( (char*)ORIG_ID, 0);
		fwrite(&ap.t.x, sizeof(float), 1, expStream);
		fwrite(&ap.t.z, sizeof(float), 1, expStream);
		fwrite(&ap.t.y, sizeof(float), 1, expStream);
	}
	for (int c = 0; c < node->NumberOfChildren(); c++)
	{
		OrigProcess(node->GetChildNode(c));
	}
}


void AsciiExp::GetNoFrames( void )
{
	TimeValue	start = ip->GetAnimRange().Start();
	TimeValue	end = ip->GetAnimRange().End();
	TimeValue	t;
	int			delta = GetTicksPerFrame();
	Object *	obj = ip->GetRootNode()->EvalWorldState(ip->GetAnimRange().Start()).obj;

	totFrames = 0;
	for (t=start; t<=end; t+=delta)		totFrames++;
}


/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

void AsciiExp::ExportNode(INode* node) 
{
	ObjectState os = node->EvalWorldState(ip->GetAnimRange().Start());

	OutputDebugString(node->GetName());

	if (os.obj)
	{
		switch(os.obj->SuperClassID())
		{
		case GEOMOBJECT_CLASS_ID:
			ip->ProgressStart("Export Model", TRUE, fn, NULL);
			ExportModel( node );
			break;
		case HELPER_CLASS_ID:
			ip->ProgressStart("Export Helper", TRUE, fn, NULL);
			break;
		case CAMERA_CLASS_ID: 
			ip->ProgressStart("Export Camera", TRUE, fn, NULL);
			ExportCamera( node );
			break;
		case LIGHT_CLASS_ID:
			ip->ProgressStart("Export Light", TRUE, fn, NULL);
			ExportLight(node);
			break;
		}
	}
	ExportProp(node);
}

/*----------------------------------------------------------------------
	Function:	Misc Utility functions
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

/*----------------------------------------------------------------------
	Function:	Misc Utility functions
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

static Point3 basic_tva[3] =
{
	Point3(0.0,0.0,0.0),Point3(1.0,0.0,0.0),Point3(1.0,1.0,0.0)
};

static Point3 basic_tvb[3] =
{
	Point3(1.0,1.0,0.0),Point3(0.0,1.0,0.0),Point3(0.0,0.0,0.0)
};

static int nextpt[3] = {1,2,0};
static int prevpt[3] = {2,0,1};



BOOL AsciiExp::TMNegParity(Matrix3 &m)
{
	return (DotProd(CrossProd(m.GetRow(0),m.GetRow(1)),m.GetRow(2))<0.0)?1:0;
}


TriObject * AsciiExp::GetTriObjectFromNode(INode *node, TimeValue t, int &deleteIt)
{
	deleteIt = FALSE;

	Object *obj = node->EvalWorldState(t).obj;

	ObjectState	os = obj->Eval(t);
	obj = os.obj;

	if (obj && obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)))
	{
		TriObject *tri = (TriObject *) obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0));
		if (obj != tri) deleteIt = TRUE;

		return tri;
	}
	else
	{
		return NULL;
	}
}

TriObject * AsciiExp::GetTriObjectFromObject(Object *obj, TimeValue t, int &deleteIt)
{
	deleteIt = FALSE;

	if (obj && obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)))
	{
		TriObject *tri = (TriObject *) obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0));
		if (obj != tri) deleteIt = TRUE;

		return tri;
	}
	else
	{
		return NULL;
	}
}

void AsciiExp::make_face_uv(Face *f, Point3 *tv)
{
	int na,nhid,i;
	Point3 *basetv;

	/* make the invisible edge be 2->0 */
	nhid = 2;
	if (!(f->flags&EDGE_A))  nhid=0;
	else if (!(f->flags&EDGE_B)) nhid = 1;
	else if (!(f->flags&EDGE_C)) nhid = 2;
	na = 2-nhid;
	basetv = (f->v[prevpt[nhid]]<f->v[nhid]) ? basic_tva : basic_tvb;
	for (i=0; i<3; i++)
	{
		tv[i] = basetv[na];
		na = nextpt[na];
	}
}

/*----------------------------------------------------------------------
	Function:	
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

bool AsciiExp::WriteChunkHdr( char* ChunkID, long Size )
{

	SizeTab[nbChunks] = ftell( expStream );
	if (SizeTab[nbChunks] & 0x3)	assert(0);
	//SizeTab[nbChunks] /= 4;

	int sl = strlen(ChunkID) + 1;
	if (sl&3) {
		sl += 4 - (sl & 3);
	}

	fwrite( ChunkID, sl, 1, expStream );
	fwrite( &Size, sizeof(Uint32), 1, expStream );

	nbChunks++;
	if(nbChunks >= MAX_FILE_CHUNKS)
	{
		MessageBox( MAX_hWnd, "INCREASE MAX_FILE_CHUNKS", "ERROR", MB_OK);
		return	false;
	}
	return true;
}


void AsciiExp::CalcChunkSizes( void )
{
	int		hFile;
	long	filelen;
	char *	filebuf;


	hFile = _open(filename, _O_RDONLY | _O_BINARY);

	if(hFile != -1)
	{
		filelen = FIO_FileLen(filename);		// length of file
		filebuf = (char *)malloc(filelen);		// allocate mainmemory

		_read(hFile, filebuf, filelen);			// read in file
		_close(hFile);							// file close
	}
	else
	{
		assert(0);
	}

	for (int i=0;i<(nbChunks-1);i++)
	{
		long	offset;
		long	filepos;

		filepos = SizeTab[i];
		int sl = strlen(&filebuf[filepos]) + 1;
		if (sl&3) {
			sl += 4 - (sl & 3);
		}
		filepos += sl;
		offset = (SizeTab[i+1] - filepos - 4);
		//offset -= 8;							// DON'T INCLUDE CHUNK HDR IN CHUNK SIZE

		*(long*)&filebuf[filepos] = offset;
	}

	FIO_Save( filename, (unsigned char *)filebuf, filelen);

	free( filebuf );

	if(expStream)	fclose( expStream);
}


/*----------------------------------------------------------------------
	Function:	String manipulation functions
	Purpose:
	Params:
	Returns:
  ---------------------------------------------------------------------- */

#define CTL_CHARS  40
#define SINGLE_QUOTE 39

TCHAR* AsciiExp::FixupName(TCHAR* name)
{
	static char buffer[256];
	TCHAR* cPtr;
	
    _tcscpy(buffer, name);
    cPtr = buffer;
	
    while(*cPtr)
	{
		if (*cPtr <= CTL_CHARS || *cPtr == '-')
			*cPtr = _T('_');
        cPtr++;
    }
	
	return buffer;
}


/*===========================================================================
 end */