//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#include "MAX.H"
#include "DECOMP.H"
#include "STDMAT.H"
#include "ANIMTBL.H"
#include "istdplug.h"
#include "phyexp.h"
#include "BonesPro.h"

#include "vweightexprc.h"
#include "vweightexp.h"
#include "vweightimp.h"


// Save for use with dialogs
static HINSTANCE hInstance;

// We just need one of these to hand off to 3DSMAX.
static VWeightExportClassDesc VWeightExportCD;
static VWeightImportClassDesc VWeightImportCD;


//===================================================================
// Required plug-in export functions
//
BOOL WINAPI DllMain( HINSTANCE hinstDLL, ULONG fdwReason, LPVOID lpvReserved) 
{	
	static int fFirstTimeHere = TRUE;
	if (fFirstTimeHere)
	{
		fFirstTimeHere = FALSE;
		hInstance = hinstDLL;
	}
	return TRUE;
}

	
EXPORT_THIS int LibNumberClasses(void)
{
	return 2;
}

	
EXPORT_THIS ClassDesc *LibClassDesc(int iWhichClass)
{
	switch(iWhichClass)
	{
		case 0: return &VWeightExportCD;
		case 1: return &VWeightImportCD;
		default: return 0;
	}
}

	
EXPORT_THIS const TCHAR *LibDescription()
{
	return _T("Valve VVW Plug-in.");
}

	
EXPORT_THIS ULONG LibVersion()
{
	return VERSION_3DSMAX;
}



//===================================================================
// Utility functions
//

int AssertFailedFunc(char *sz)
{
	MessageBox(GetActiveWindow(), sz, "Assert failure", MB_OK);
	int Set_Your_Breakpoint_Here = 1;
	return 1;
}


//=================================================================
// Methods for CollectModelTEP
//
Modifier *FindPhysiqueModifier (INode *nodePtr)
{
	// Get object from node. Abort if no object.
	Object *ObjectPtr = nodePtr->GetObjectRef();
	if (!ObjectPtr) return NULL;

	// Is derived object ?
	if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
	{
		// Yes -> Cast.
		IDerivedObject *DerivedObjectPtr = static_cast<IDerivedObject*>(ObjectPtr);

		// Iterate over all entries of the modifier stack.
		int ModStackIndex = 0;
		while (ModStackIndex < DerivedObjectPtr->NumModifiers())
		{
			// Get current modifier.
			Modifier *ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);

			// Is this Physique ?
			if (ModifierPtr->ClassID() == Class_ID(	PHYSIQUE_CLASS_ID_A, PHYSIQUE_CLASS_ID_B) )
			{
				// Yes -> Exit.
				return ModifierPtr;
			}
			// Next modifier stack entry.
			ModStackIndex++;
		}
	}
	// Not found.
	return NULL;
}

Modifier *FindBonesProModifier (INode *nodePtr)
{
	// Get object from node. Abort if no object.
	Object *ObjectPtr = nodePtr->GetObjectRef();
	if (!ObjectPtr) return NULL;

	// Is derived object ?
	if (ObjectPtr->SuperClassID() == GEN_DERIVOB_CLASS_ID)
	{
		// Yes -> Cast.
		IDerivedObject *DerivedObjectPtr = static_cast<IDerivedObject*>(ObjectPtr);

		// Iterate over all entries of the modifier stack.
		int ModStackIndex = 0;
		while (ModStackIndex < DerivedObjectPtr->NumModifiers())
		{
			// Get current modifier.
			Modifier *ModifierPtr = DerivedObjectPtr->GetModifier(ModStackIndex);

			// Is this Bones Pro OSM?
			if (ModifierPtr->ClassID() == BP_CLASS_ID_OSM )
			{
				// Yes -> Exit.
				return ModifierPtr;
			}
			// Is this Bones Pro WSM?
			if (ModifierPtr->ClassID() == BP_CLASS_ID_WSM )
			{
				// Yes -> Exit.
				return ModifierPtr;
			}
			// Next modifier stack entry.
			ModStackIndex++;
		}
	}
	// Not found.
	return NULL;
}



//========================================================================
// Utility functions for getting/setting the personal "node index" property.
// NOTE: I'm storing a string-property because I hit a 3DSMax bug in v1.2 when I
// NOTE: tried using an integer property.
// FURTHER NOTE: those properties seem to change randomly sometimes, so I'm
// implementing my own.

typedef struct
{
	char	szNodeName[MAX_NAME_CHARS];
} NAMEMAP;
const int MAX_NAMEMAP = 512;
static NAMEMAP g_NameMap[MAX_NAMEMAP];
static int g_cNameMap = 0;

void ResetINodeMap( void )
{
	g_cNameMap = 0;
}

int BuildINodeMap(INode *pnode)
{
	if (!FUndesirableNode(pnode))
	{
		AddINode(pnode);
	}

	// For each child of this node, we recurse into ourselves
	// until no more children are found.
	for (int c = 0; c < pnode->NumberOfChildren(); c++)
	{
		BuildINodeMap(pnode->GetChildNode(c));
	}
	
	return g_cNameMap;
}


int GetIndexOfNodeName(char *szNodeName, BOOL fAssertPropExists)
{
	for (int inm = 0; inm < g_cNameMap; inm++)
	{
		if (FStrEq(g_NameMap[inm].szNodeName, szNodeName))
		{
			return inm;
		}
	}

	return -1;
}

int GetIndexOfINode( INode *pnode, BOOL fAssertPropExists )
{
	return GetIndexOfNodeName( pnode->GetName(), fAssertPropExists );
}
	
void AddINode( INode *pnode )
{
	TSTR strNodeName(pnode->GetName());
	for (int inm = 0; inm < g_cNameMap; inm++)
	{
		if (FStrEq(g_NameMap[inm].szNodeName, (char*)strNodeName))
		{
			return;
		}
	}

	ASSERT_MBOX(g_cNameMap < MAX_NAMEMAP, "NAMEMAP is full");
	strcpy(g_NameMap[g_cNameMap++].szNodeName, (char*)strNodeName);
}


//=============================================================
// Returns TRUE if a node should be ignored during tree traversal.
//
BOOL FUndesirableNode(INode *pnode)
{
	// Get Node's underlying object, and object class name
	Object *pobj = pnode->GetObjectRef();

	if (!pobj)
		return TRUE;
	// Don't care about lights, dummies, and cameras
	if (pobj->SuperClassID() == CAMERA_CLASS_ID)
		return TRUE;
	if (pobj->SuperClassID() == LIGHT_CLASS_ID)
		return TRUE;
	if (!strstr(pnode->GetName(), "Bip01" ))
		return TRUE;

	return FALSE;

	// Actually, if it's not selected, pretend it doesn't exist!
	//if (!pnode->Selected())
	//	return TRUE;
	//return FALSE;
}


//=============================================================
// Returns TRUE if a node has been marked as skippable
//
BOOL FNodeMarkedToSkip(INode *pnode)
{
	return (::GetIndexOfINode(pnode) == UNDESIRABLE_NODE_MARKER);
}

	

//=============================================================
// gets a weighted value for the current system of nodes
//
void GetUnifiedCoord( Point3 &p1, MaxVertWeight pweight[], MaxNode maxNode[], int cMaxNode )
{
	float flMin = 1E20f;
	for (int iNode = 0; iNode < cMaxNode; iNode++)
	{
		float f = (p1 - maxNode[iNode].mat3NodeTM.GetTrans()).LengthSquared();
		if (f > 0 && f < flMin)
			flMin = f;
		pweight[iNode].flDist = f;
	}

	float flTotal = 0;
	float flInvMin = 1.0 / flMin;
	for (iNode = 0; iNode < cMaxNode; iNode++)
	{
		if (pweight[iNode].flDist > 0)
		{
			pweight[iNode].flDist = flInvMin / pweight[iNode].flDist;
		}
		else
		{
			pweight[iNode].flDist = 10.0;
		}
		flTotal += pweight[iNode].flDist;
	}

	float flInvTotal;
	if (flTotal > 0)
	{
		flInvTotal = 1.0 / flTotal;
	}
	else
	{
		flInvTotal = 1.0;
	}

	for (iNode = 0; iNode < cMaxNode; iNode++)
	{
		pweight[iNode].flDist = pweight[iNode].flDist * flInvTotal;
		// fprintf(m_pfile, "%8.4f ", pweight[iNode].flDist );
	}
}


int GetBoneWeights( IPhyContextExport *mcExport, int iVertex, MaxVertWeight *pweight)
{
	float fTotal = 0;
	IPhyVertexExport *vtxExport = mcExport->GetVertexInterface(iVertex);

	if (vtxExport)
	{
		if (vtxExport->GetVertexType() & BLENDED_TYPE)
		{
			IPhyBlendedRigidVertex *pBlend = ((IPhyBlendedRigidVertex *)vtxExport);

			for (int i = 0; i < pBlend->GetNumberNodes(); i++)
			{
				int index = GetIndexOfINode( pBlend->GetNode( i ) );
				if (index >= 0)
				{
					pweight[index].flWeight = pBlend->GetWeight( i );
					fTotal += pweight[index].flWeight;
				}
			}
		}
		else 
		{
			INode *Bone = ((IPhyRigidVertex *)vtxExport)->GetNode();
			int index = GetIndexOfINode(Bone);
			if (index >= 0)
			{
				pweight[index].flWeight = 100.0;
			}
		}
		mcExport->ReleaseVertexInterface(vtxExport);
	}
	return (fTotal > 0);
}


int GetBoneWeights( Modifier * bonesProMod, int iVertex, MaxVertWeight *pweight)
{
	int iTotal = 0;
	int nb = bonesProMod->SetProperty( BP_PROPID_GET_N_BONES, NULL );

	for ( int iBone = 0; iBone < nb; iBone++)
	{
		BonesPro_BoneVertex bv;
		bv.bindex = iBone;
		bv.vindex = iVertex;
		bonesProMod->SetProperty( BP_PROPID_GET_BV, &bv );

		if (bv.included > 0 && bv.forced_weight >= 0)
		{
			BonesPro_Bone bone;
			bone.t = BP_TIME_ATTACHED;
			bone.index = iBone;
			bonesProMod->SetProperty( BP_PROPID_GET_BONE_STAT, &bone );

			if (bone.node != NULL)
			{
				int index = GetIndexOfINode( bone.node );

				if (index >= 0)
				{
					pweight[index].flWeight = bv.forced_weight;
					iTotal++;
				}
			}
		}
	}
	return iTotal;
}


void SetBoneWeights( Modifier * bonesProMod, int iVertex, MaxVertWeight *pweight)
{
	int nb = bonesProMod->SetProperty( BP_PROPID_GET_N_BONES, NULL );

	// FILE *fp = fopen("bone2.txt", "w");

	for ( int iBone = 0; iBone < nb; iBone++)
	{
		BonesPro_Bone bone;
		bone.t = BP_TIME_ATTACHED;
		bone.index = iBone;
		bonesProMod->SetProperty( BP_PROPID_GET_BONE_STAT, &bone );

		/*
		if (GetIndexOfINode( bone.node ) >= 0)
		{
			fprintf( fp, "\"%s\" %d\n", bone.name, GetIndexOfINode( bone.node ) );
		}
		else
		{
			fprintf( fp, "\"%s\"\n", bone.name );
		}
		*/

		BonesPro_BoneVertex bv;
		bv.bindex = iBone;
		bv.vindex = iVertex;
		bv.included = 0;
		bv.forced_weight = -1;

		if (bone.node != NULL)
		{
			int index = GetIndexOfINode( bone.node );

			if (index >= 0 && pweight[index].flWeight >= 0)
			{
				bv.included = 1;
				bv.forced_weight = pweight[index].flWeight;
			}
		}
		bonesProMod->SetProperty( BP_PROPID_SET_BV, &bv );
	}
	//fclose(fp);
	//exit(1);
}