/**********************************************************************
DESCRIPTION:  .MOD file export 
CREATED BY: Boujemaa
**********************************************************************/

#include "StdAfx.h"
#include "resource.h"
#include "ModExp.h"
#include "modobject.h"


HINSTANCE hInstance;
int controlsInit = FALSE;

CModExport* g_Eporter = NULL;

TCHAR* GetString(int id)
{
	static TCHAR stBuf[ERROR_MSG_MAX_LEN];
	if (hInstance)
		return LoadString(hInstance, id, stBuf, ERROR_MSG_MAX_LEN) ? stBuf : NULL;
	return NULL;
}

static int Alert(int s1, int s2 = IDS_MODEXP, int option = MB_OK) {
	return MessageBox(g_Eporter->m_Gi->GetMAXHWnd(), GetString(s1), GetString(s2), option);
}

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

	// Hang on to this DLL's instance handle.
	hInstance = hinstDLL;

	if ( !controlsInit ) {
		controlsInit = TRUE;

		// Initialize 3ds Max's custom controls
		InitCustomControls(hInstance);

		// Initialize Windows controls 
		InitCommonControls();
	}

	return(TRUE);

}



static CModExpClassDesc _ModExpDesc;

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

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

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

// Return version so can detect obsolete DLLs
__declspec( dllexport ) ULONG LibVersion()
{
	return VERSION_3DSMAX;
}

inline const TCHAR* CModExpClassDesc::Category()
{
	return GetString(IDS_SCENEEXPORT);
}

inline SClass_ID CModExpClassDesc::SuperClassID()
{
	return SCENE_EXPORT_CLASS_ID;
}

inline Class_ID CModExpClassDesc::ClassID()
{
	return Class_ID(0x7d285801, 0x3c7b4aaa);
}

inline const TCHAR * CModExpClassDesc::ClassName()
{
	return GetString(IDS_UBIMOD);
}

inline int CModExpClassDesc::IsPublic()
{
	return 1;
}

inline void * CModExpClassDesc::Create(BOOL loading)
{
	g_Eporter = new CModExport;
	return g_Eporter;

}

CModExport::CModExport()
{
	m_exportSelected = 0;
	m_showPrompts = 1;
	m_Gi = NULL;
	m_ei = NULL;
}

CModExport::~CModExport()
{
}

int CModExport::GetExportOptions(DWORD _options, BOOL _suppressPrompts)
{
	m_showPrompts = _suppressPrompts ? FALSE : TRUE;
	m_exportSelected = (_options & SCENE_EXPORT_SELECTED) ? TRUE : FALSE;
	return 1;
}

int CModExport::DumpHeader(WorkFile &modFile, BOOL _isSpo)
{
	char currentCmd[MAX_MOD_COMMAND_LEN];

	{
		modFile.OutLine("; (c) Ubi Casablanca");
	}
	{
		char filename[MAX_FILE_NAME];
	
		_splitpath(modFile.GetFileName(), NULL, NULL, filename, NULL);

		if (_isSpo)
		{
			strcat(filename, ".SPO");
			sprintf(currentCmd, ";.SPO (graphic Module) : %s", filename);
		}
		else
		{
			strcat(filename, ".MOD");
			sprintf(currentCmd, ";.MOD (graphic Module) : %s", filename);
		}

		modFile.SetShortfName(filename);

		modFile.OutLine(currentCmd);
	}

	{
		assert(m_Gi != NULL);
		sprintf(currentCmd, ";Generated from file %s", m_Gi->GetCurFileName());
		modFile.OutLine(currentCmd);
	}

	{   
		struct tm *newtime;
		time_t aclock;
		time( &aclock );   // Get time in seconds
		newtime = localtime( &aclock );   // Convert time to struct tm form 
		/* Print local time as a string */
		sprintf( currentCmd, ";Creation date : %s", asctime( newtime ) );
		modFile.OutLine(currentCmd);
	}

	/*
	;Version directive, the version number is stored in file result 0
	$SetCurrentFileDouble(0,"5.20")

	;Unit directive, the unit is stored in file result 1
	;And correspond to the value of one unit exprimed in meter
	$SetCurrentFileDouble(1,1)
	*/
	modFile.OutLine(";Version directive, the version number is stored in file result 0");
	modFile.OutLine("$SetCurrentFileDouble(0,\"5.20\")\n");
	modFile.OutLine(";Unit directive, the unit is stored in file result 1");
	modFile.OutLine(";And correspond to the value of one unit exprimed in meter");
	modFile.OutLine("$SetCurrentFileDouble(1,1)\n");
 
	return 1;
}
int CModExport::DumpData(WorkFile &_modFile,WorkFile &_spoFile)
{

	// Make sure there are nodes we're interested in!
	// Ask the scene to enumerate all its nodes so we can determine if there are any we can use
	SceneEnumProc myScene(g_Eporter->m_Gi->GetTime());
	m_ei->theScene->EnumTree(&myScene);

	// Any useful nodes?
	if(myScene.Count() == 0)
	{
		if(m_showPrompts)
			Alert(IDS_NODATATOEXPORT);
		return 1;
	}

	myScene.DumpGeoms(_modFile);
	myScene.DumpMtls(_modFile);

	myScene.DumpRoot(_spoFile);

	return 1;
}

int CModExport::ExportFile(const TCHAR *_filename)
{

	TSTR LvlName;
	TSTR LvlPath;
	SplitFilename((TSTR)_filename, &LvlPath, &LvlName, NULL);
	strcat(LvlPath, "\\");
	strcat(LvlPath, LvlName);
	strcat(LvlPath, ".SPO");

	WorkFile modFile(_filename,"wt");
	WorkFile spoFile(LvlPath,"wt");
	
	DumpHeader(modFile);
	DumpHeader(spoFile, true);
	DumpData(modFile,spoFile);
		
	return 1;
}

int CModExport::DoExport(const TCHAR *filename,ExpInterface *ei,Interface *gi, BOOL suppressPrompts, DWORD options)
{

	int result;
	m_Gi = gi;
	m_ei = ei;
	result = GetExportOptions(options, suppressPrompts);
	if (!result)
		return 0;
	result = ExportFile(filename);
	
	return result;
}

void CModExport::ShowAbout(HWND hWnd)
{
	// Optional
}
unsigned int CModExport::Version()
{
	// Version number * 100 (i.e. v3.01 = 301)
	return 100;
}

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

const TCHAR * CModExport::Ext(int n)
{		// Extensions supported for import/export modules
	switch(n) {
		case 0:
			return _T("MOD");
		}
	return _T("");
}

const TCHAR * CModExport::LongDesc()
{			// Long ASCII description (i.e. "Targa 2.0 Image File")
	return GetString(IDS_MODSCENEFILE);
}
	
const TCHAR * CModExport::ShortDesc()
{			// Short ASCII description (i.e. "Targa")
	return GetString(IDS_MODFILE);
}

const TCHAR * CModExport::AuthorName()
{			// ASCII Author name
	return GetString(IDS_UBICASA);
}

const TCHAR * CModExport::OtherMessage1()
{		// Other message #1
	return _T("");
}

const TCHAR * CModExport::OtherMessage2()
{		// Other message #2
	return _T("");
}
const TCHAR * CModExport::CopyrightMessage()
{	// ASCII Copyright message
	return GetString(IDS_COPYRIGHT_UBI);
}

void WorkFile::OutLine(char* _txtLine)
{
	fprintf(stream, "%s\n", _txtLine);
}

SceneEnumProc::SceneEnumProc(TimeValue	time)
{
	m_time = time;
	m_count = 0;
	m_root = new CSuperObject(NULL);
}
SceneEnumProc::~SceneEnumProc()
{
	m_GeomList.clear();
	m_MatList.clear();
	delete m_root;
}

int SceneEnumProc::callback(INode *node)
{

	if(g_Eporter->m_exportSelected && node->Selected() == FALSE)
		return TREE_CONTINUE;

	Append(node);

	return TREE_CONTINUE;
}

void SceneEnumProc::Append(INode *_node)
{
	
	if (_node->IsRootNode())
		return;

	// find parent
	INode * Parentnode = _node->GetParentNode();

	CSuperObject *ParentSpo;
	
	if (Parentnode->IsRootNode())
		ParentSpo = m_root;
	else
		ParentSpo = m_root->findSpo(Parentnode);
	
	if (ParentSpo == NULL)
	{	
		ParentSpo = m_root;
	}
	
	if (ParentSpo == m_root)
	{
	
		char NodeName[MAX_NAME_OBJ];
		strcpy(NodeName,_node->GetName());
		strlwr(NodeName);
		if ((NodeName[0] != 's') ||
			(NodeName[1] != 'c') ||
			(NodeName[2] != 't'))
		{
			// object not linked to any dumy
			assert(0);
			return;
		}
	}

	// create sop
	CSuperObject *NewSpo = new CSuperObject(_node);

	ParentSpo->AddNode(NewSpo);

	Object *obj = _node->EvalWorldState(m_time).obj;
	if (obj->CanConvertToType(triObjectClassID))
	{
		m_GeomList.push_back(NewSpo);
		m_count ++;
	}

}
void SceneEnumProc::AppendMat(Mtl *_mtl)
{
	std::list <Mtl *>::iterator Iter;
	for ( Iter = m_MatList.begin( ); Iter != m_MatList.end( ); Iter++ )
	{
		Mtl *current = *Iter;
		if (current == _mtl)
		{
			return;
		}
	}
	m_MatList.push_back(_mtl);
}

void SceneEnumProc::DumpGeoms(WorkFile &modFile)
{
	std::list <CSuperObject *>::iterator Iter;
	for ( Iter = m_GeomList.begin( ); Iter != m_GeomList.end( ); Iter++ )
	{
		DumpOneGeom(modFile, *Iter);
	}
}

void SceneEnumProc::DumpRoot(WorkFile &_spoFile)
{
	m_root->DumpSpo(_spoFile, m_time);
}

void SceneEnumProc::DumpMtls(WorkFile &modFile)
{
	std::list <Mtl*>::iterator Iter;
	for ( Iter = m_MatList.begin( ); Iter != m_MatList.end( ); Iter++ )
	{
		DumpOneMtl(modFile, *Iter);
	}
}

void SceneEnumProc::DumpOneGeom(WorkFile &modFile, CSuperObject *_node)
{
	char currentCmd[MAX_MOD_COMMAND_LEN];
	int i = 0;
	std::vector <CModElem*>::iterator	IterElem;
	std::vector <CFace>::iterator		IterFace;
	std::vector <CUV>::iterator			IterUV;

	CModObject curObj(_node->m_maxNode, m_time);
	
	if (!curObj.m_ValidObj)
		return;

	int nbrElems = 0;
	for ( IterElem = curObj.m_elements.begin( ); IterElem != curObj.m_elements.end( ); IterElem++ )
	{
		CModElem* elem = *IterElem;
		if (elem->m_Faces.size() != 0)
		{
			SceneEnumProc::AppendMat(elem->m_material);
			nbrElems++;
		}
	}
	

	// set spo link
	sprintf(currentCmd, "Geometric(\"%s^Geometric:%s\")", modFile.GetShortfName(), curObj.m_name);
	_node->SetGeomLink(currentCmd);
	// geom
	sprintf(currentCmd, "{Geometric:%s(%d,0,%d)", curObj.m_name, curObj.m_nbrVrtx, nbrElems);
	modFile.OutLine(currentCmd);

	// vertxs
	
	for (i = 0; i < curObj.m_Vertxs.size(); i++)
	{
		sprintf(currentCmd, "\tAddVertex(%d,\"%g\",\"%g\",\"%g\",\"%g\",\"%g\",\"%g\",0,0,0)",
			i,
			curObj.m_Vertxs[i].x, curObj.m_Vertxs[i].y, curObj.m_Vertxs[i].z,
			curObj.m_Vertxs[i].nx, curObj.m_Vertxs[i].ny, curObj.m_Vertxs[i].nz);
		modFile.OutLine(currentCmd);
	}
	
	// Geomelem
	
	i=0;
	for ( IterElem = curObj.m_elements.begin( ); IterElem != curObj.m_elements.end( ); IterElem++ )
	{
		CModElem* elem = *IterElem;
		if (elem->m_Faces.size() != 0)
		{
			sprintf(currentCmd, "\tAddElement(%d,IndexedTriangles,\"*^ElementIndexedTriangles:%s_%s\")",i, curObj.m_name, elem->m_name);
			modFile.OutLine(currentCmd);
			i++;
		}
	}

	modFile.OutLine("}");

	// elems
	for ( IterElem = curObj.m_elements.begin( ); IterElem != curObj.m_elements.end( ); IterElem++ )
	{ 
		CModElem* elem = *IterElem;
		if (elem->m_Faces.size() != 0)
		{
			sprintf(currentCmd, "{ElementIndexedTriangles:%s_%s(%d,%d)", curObj.m_name, elem->m_name,elem->m_Faces.size(),elem->m_UVs.size());
			modFile.OutLine(currentCmd);
			sprintf(currentCmd, "\tMaterial(\"*^Material:%s\")", elem->m_name);
			modFile.OutLine(currentCmd);
			i = 0;
			for (IterFace = elem->m_Faces.begin(); IterFace != elem->m_Faces.end(); IterFace++)
			{
				CFace* face =&(*IterFace); 
				sprintf(currentCmd, "\tAddFaceUV(%d,%d,%d,%d,\"%g\",\"%g\",\"%g\",%d,%d,%d)",
					i,
					face->I, face->J,face->K,
					face->nx, face->ny,face->nz,
					face->IUV, face->JUV,face->KUV);
				modFile.OutLine(currentCmd);
				i++;
			}
			i = 0;
			for (IterUV = elem->m_UVs.begin(); IterUV != elem->m_UVs.end(); IterUV++)
			{
				CUV* uv =&(*IterUV); 
				sprintf(currentCmd, "\tAddUV(%d,\"%g\",\"%g\")", i, uv->U, uv->V);
				modFile.OutLine(currentCmd);
				i++;
			}
			modFile.OutLine("}");
		}
	}

}

void SceneEnumProc::DumpOneMtl(WorkFile &modFile, Mtl *_mtl)
{
	char currentCmd[MAX_MOD_COMMAND_LEN];

	sprintf(currentCmd, "{Material:%s", _mtl->GetName());
	modFile.OutLine(currentCmd);
	
	sprintf(currentCmd, "\tType(Gouraud)");
	modFile.OutLine(currentCmd);
	
	sprintf(currentCmd, "\tAmbientColor(\"%g\",\"%g\",\"%g\")", _mtl->GetAmbient().r, _mtl->GetAmbient().g, _mtl->GetAmbient().b);
	modFile.OutLine(currentCmd);
	
	sprintf(currentCmd, "\tDiffuseColor(\"%g\",\"%g\",\"%g\")", _mtl->GetDiffuse().r, _mtl->GetDiffuse().g, _mtl->GetDiffuse().b);
	modFile.OutLine(currentCmd);

	sprintf(currentCmd, "\tSpecularColor(\"%g\",\"%g\",\"%g\",\"%g\")", _mtl->GetSpecular().r, _mtl->GetSpecular().g, _mtl->GetSpecular().b, _mtl->GetShinStr());
	modFile.OutLine(currentCmd);

	{
		BitmapTex *tx = (BitmapTex*)_mtl->GetSubTexmap(ID_DI);//ID_DI  : diffuse
		char filename[MAX_FILE_NAME];
		char ext[5];
		_splitpath((char*)(tx->GetMapName()), NULL, NULL, filename, ext);
		sprintf(currentCmd, "\tTexture(\"%s%s\")",filename, ext);
		modFile.OutLine(currentCmd);
	}
	{
		if (((StdMat *)_mtl)->GetTwoSided())
			sprintf(currentCmd, "\tBackface(OFF)");
		else
			sprintf(currentCmd, "\tBackface(ON)");
		modFile.OutLine(currentCmd);
	}

	modFile.OutLine("}");

}
void CSuperObject::AddNode(CSuperObject *_node)
{
	m_childs.push_back(_node);
	_node->m_Parent = this;
}
CSuperObject::CSuperObject(INode *_node)
{
	if (_node != NULL)
	{
		sprintf(m_name, "SPO_%s", _node->GetName());
	}
	else
		strcpy(m_name, "Root");
	m_maxNode = _node;
	m_GeomLink = NULL;
	m_Parent = NULL;
}
CSuperObject::~CSuperObject()
{
	std::list <CSuperObject*>::iterator Iter;
	for ( Iter = m_childs.begin( ); Iter != m_childs.end( ); Iter++ )
	{
		CSuperObject *spo = *Iter;
		delete spo;
	}
	m_childs.clear();
	if (m_GeomLink)
		delete m_GeomLink;
}
void CSuperObject::SetGeomLink(char* _GeomLink)
{
	if (m_GeomLink == NULL)
		m_GeomLink = new char[MAX_MOD_COMMAND_LEN];
	
	strcpy(m_GeomLink, _GeomLink);
}

char* CSuperObject::GetGeomLink()
{
	return m_GeomLink;
}

CSuperObject* CSuperObject::findSpo(INode *_node)
{
	if (m_maxNode == _node)
		return this;
	else
	{
		std::list <CSuperObject*>::iterator Iter;
		for ( Iter = m_childs.begin( ); Iter != m_childs.end( ); Iter++ )
		{
			CSuperObject *spo = *Iter;
			CSuperObject *retSpo = spo->findSpo(_node);
			if (retSpo != NULL)
				return retSpo;
		}
	}
	
	return NULL;
}

void CSuperObject::DumpSpo(WorkFile &_spoFile, TimeValue _time)
{
	char currentCmd[MAX_MOD_COMMAND_LEN];
	std::list <CSuperObject*>::iterator Iter;

	// dump obj
	
	sprintf(currentCmd, "{SuperObject:%s", m_name);
	_spoFile.OutLine(currentCmd);

	sprintf(currentCmd, "\tPutMatrix(\"*^Matrix:%s\")", m_name);
	_spoFile.OutLine(currentCmd);
	
	if (GetGeomLink())
	{
		sprintf(currentCmd, "\t%s", GetGeomLink());
		_spoFile.OutLine(currentCmd);
	}
	
	for ( Iter = m_childs.begin( ); Iter != m_childs.end( ); Iter++ )
	{
		CSuperObject *spo = *Iter;
		sprintf(currentCmd, "\tAddChild(\"*^SuperObject:%s\")", spo->m_name);
		_spoFile.OutLine(currentCmd);
	}
	_spoFile.OutLine("}\n");

	// dump matrix

	sprintf(currentCmd, "{Matrix:%s", m_name);
	_spoFile.OutLine(currentCmd);

	Matrix3 NodMat;
	GetLocalMatrix(NodMat, _time);

	Point3 pos = NodMat.GetTrans();

	sprintf(currentCmd, "\tMatrixTranslation(\"%g\",\"%g\",\"%g\")", pos.x, pos.y, pos.z);
	_spoFile.OutLine(currentCmd);
/*
	Quat quat = node->GetObjOffsetRot();
	Matrix3 RotMat;
	quat.MakeMatrix(RotMat);
	ScaleValue scaleValue = node->GetObjOffsetScale();
*/
	_spoFile.OutLine("}\n");

	// dump childs
	for ( Iter = m_childs.begin( ); Iter != m_childs.end( ); Iter++ )
	{
		CSuperObject *spo = *Iter;
		spo->DumpSpo(_spoFile, _time);
	}

}
void CSuperObject::GetLocalMatrix(Matrix3 &_NodMat, TimeValue _time)
{
	if (m_maxNode != NULL)
	{
		_NodMat = m_maxNode->GetNodeTM(_time);
	}
	else
	{
		_NodMat = Matrix3(true);
	}
	if ((m_Parent != NULL) && (m_Parent->m_maxNode != NULL))
	{
		Matrix3 temp = _NodMat;
		Matrix3 ip = Inverse(m_Parent->m_maxNode->GetNodeTM(_time));
        _NodMat = ip * temp;
	}
}
