//===========================================================================//
// File:	CvTool.cpp                                                 
// Project: MechWarrior 4
// Contents: Plugin to add Collision Volumes to mech 
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
// 06/10/99  jkyle  Initial coding, 
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Fasa Interactive, Inc.
// All Rights reserved worldwide
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//===========================================================================//


#include "stdafx.h"

#ifdef STRICT							// Already defined by stdafx,
#undef STRICT							// so we avoid warning linking msg
#endif
#ifdef _MBCS							// The same as above
#undef _MBCS
#endif

#include "CvTool.hpp"
#include "cvobject.h"
#include "mw4.hpp"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

extern int CALLBACK BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData);

//////////////////////////////////////////////////////////////////////
// Global Variables
CvToolPanel g_cvToolPanel;


//////////////////////////////////////////////////////////////////////
// ProtoTypes

////////////////////////////////////////////////////////////////////////////////
// CvToolPanel Dialog Procedure to handle User and System Interactions
static BOOL CALLBACK CvToolPanelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	
	switch (msg) 
	{
	case WM_INITDIALOG:						//Panel's extra initialization
		g_cvToolPanel.Init(hWnd);
		break;
	case WM_DESTROY:						//Panel's extra destruction
		g_cvToolPanel.Destroy(hWnd);
		break;
	case WM_COMMAND :
		{   //Handle Panel's controls
			HWND hwndCtrl = (HWND)lParam;
			switch (LOWORD(wParam)) 
			{
			case IDC_EXECUTE:
				{
					//g_cvToolPanel.CvToolExecute();

					theHold.SuperBegin();

					g_cvToolPanel.BuildCollisionVolume(g_cvToolPanel.m_ip->GetRootNode());

					theHold.SuperAccept("CollisionTool");

					g_cvToolPanel.m_ip->RedrawViews(g_cvToolPanel.m_ip->GetTime(),REDRAW_BEGIN);
					g_cvToolPanel.m_ip->RedrawViews(g_cvToolPanel.m_ip->GetTime(),REDRAW_NORMAL);
					g_cvToolPanel.m_ip->RedrawViews(g_cvToolPanel.m_ip->GetTime(),REDRAW_END);

				}
			}
			break;
		}
	////////////////////////////////////////////////////////////////////////
	//The following lines are not necessary for MAX2 Plugins
	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MOUSEMOVE:
		g_cvToolPanel.m_ip->RollupMouseMessage(hWnd, msg, wParam, lParam);
		break;
	////////////////////////////////////////////////////////////////////////

	default:
		return FALSE;
	}
	return TRUE;
}




////////////////////////////////////////////////////////////////////////////////
// Display the Plugin's Panel
void CvToolPanel::BeginEditParams(Interface* ip, IUtil* iu)
{
	m_ip = ip;								//Get the Plugin's Interface
	m_iu = iu;								//Get the Utility Plugin's Interface

	//Display the plugin's Rollup Dialog
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	HINSTANCE loc_hInstance = AfxGetResourceHandle ();

	m_hPanel = ip->AddRollupPage(loc_hInstance, MAKEINTRESOURCE(IDD_CVTOOL_PANEL),
		CvToolPanelProc, GetString(IDS_CVOBJECT_NAME),0);
}

////////////////////////////////////////////////////////////////////////////////
// Free the Plugins' Panel
void CvToolPanel::EndEditParams(Interface *ip, IUtil *iu)
{
	m_iu = NULL;
	m_ip = NULL;

	//Remove the Rollup Dialog
	ip->DeleteRollupPage(m_hPanel);
	m_hPanel = NULL;
}

////////////////////////////////////////////////////////////////////////////////
// Panel Initialization callback
void CvToolPanel::Init(HWND hWnd)
{

}

////////////////////////////////////////////////////////////////////////////////
// Panel Destruction callback
void CvToolPanel::Destroy(HWND hWnd)
{

}




#if 0




void CvToolPanel::CvToolExecute()
{
	INode *pRoot = ip->GetINodeByName("joint_WORLD");
	if (pRoot)
	{
		// We are creating a mech Collision Volume
		//AddCvNode(pRoot,true);
		AddCv(pRoot);
	}
	else
	{
		// We are creating a building Collision Volume
		pRoot = ip->GetRootNode();
		AddCv(pRoot,false);
	}
	// Redraw the views
	ip->RedrawViews(ip->GetTime());	
}

void CvToolPanel::AddCv(INode *pNode,BOOL bCheckName)
{
	BOOL bValid = false;
	BOOL bIsJoint = false;
	if (bCheckName)
	{
		CString strName = pNode->GetName();
		if (0==strnicmp(strName,"joint_",6))
		{
			bIsJoint = true;
		}
		BOOL bHasGeometry = false;
		for (int x=0;(x<pNode->NumberOfChildren())&&(!bHasGeometry);x++)
		{
			CString strChildName = pNode->GetChildNode(x)->GetName();
			if (0!=strnicmp(strChildName,"joint_",6))
				//if (0!=strnicmp(strChildName,"handle_",7))
					bHasGeometry = true;
		}

		if (bIsJoint && bHasGeometry)
			bValid = true;
	}
	else
	{
		CString strName = pNode->GetName();
		if (0!=strnicmp(strName,"cv_",3))
		{
			bValid = bIsJoint = true;
		}
	}
	
	if (bIsJoint)
		AddCvNode(pNode,bValid);
	for (int x=0;x<pNode->NumberOfChildren();x++)
	{
		AddCv(pNode->GetChildNode(x),bCheckName);
	}
}

void CvToolPanel::AddCvNode(INode *pNode,BOOL bValid)
{
	if (!bValid)
		return;
	/* *******************************
	Code from the Max SDK solution #1981
	**********************************/

	// Create a new object through the CreateInstance() API
	Object *obj = (Object*)ip->CreateInstance(
		HELPER_CLASS_ID,
		CVOBJECT_CLASS_ID);
	assert(obj);
	
	// Get a hold of the parameter block
	IParamArray *iParams = obj->GetParamBlock();
	assert(iParams);

	// Set the parameters
	// Set to a default material
	iParams->SetValue(CV_MATERIAL,TimeValue(0),bValid?MechWarrior4::SteelMaterial:MechWarrior4::MaterialCount);

	// Set the initial size to the size of the node's bounding box
	Box3 box;
	if (bValid)
	{
		Object *pObj = pNode->GetObjectRef();
		if (pObj)
		{
			pObj->GetLocalBoundBox(TimeValue(0),pNode,ip->GetActiveViewport(),box);
			box = Box3(Point3(0.0,0.0,0.0),box.Width());
			for (int x=0;x<pNode->NumberOfChildren();x++)
			{
				Box3 newbox;
				INode *pChild = pNode->GetChildNode(x);
				if (0!=strnicmp(pChild->GetName(),"site_",5))
				{
					pChild->GetObjectRef()->GetLocalBoundBox(TimeValue(0),pChild,ip->GetActiveViewport(),newbox);
					box += Box3(Point3(0.0,0.0,0.0),newbox.Width());
				}
			}
			if (box.IsEmpty())
				box = Box3(Point3(1,1,1),Point3(1,1,1));
		}
	}
	else
		box = Box3(Point3(0,0,0),Point3(0,0,0));

	// Swap the y and z for some reason
	Point3 pt = box.Width();
	iParams->SetValue(CV_SIZE,TimeValue(0),Point3(pt.x,pt.z,pt.y));
		
	// Create a node in the scene that references the object
	INode *node = ip->CreateObjectNode(obj);
	
	// Name the node and make the name unique.
	CString strName = pNode->GetName();
	if (strnicmp(strName,"joint_",6)==0)
	{
		strName = strName.Right(strName.GetLength()-6);
	}
	CString rcName;
	rcName.LoadString(IDS_DB_CVOBJECT);
	strName = rcName+strName;

	TSTR name(_T(strName));

	ip->MakeNameUnique(name);
	node->SetName(name);
	
	// Add node to the heiarchy and move it to the parent's position
	pNode->AttachChild(node,0);

	// Eval the node's object (exclude WSMs)
	Object *oldObj = node->GetObjectRef();
	ObjectState os = oldObj->Eval(ip->GetTime());
	
	// Set the result to be the node's new object (make a clone)
	Object *newobj = (Object*)os.obj->Clone();
	
	newobj->SetSubSelState(0); 
                    // Reset the selection level to object level
	oldObj->SetAFlag(A_LOCK_TARGET);
	node->SetObjectRef(newobj);
	node->NotifyDependents(FOREVER,0,REFMSG_SUBANIM_STRUCTURE_CHANGED);

	oldObj->ClearAFlag(A_LOCK_TARGET);
	oldObj->MaybeAutoDelete();
}




#endif




void CvToolPanel::BuildCollisionVolume(INode *inode)
{

	if (inode == NULL)
		return;

	CString nodename = inode->GetName();
	nodename.MakeLower();
	if (nodename.Find("cv_")>=0 || nodename.Find("light")>=0)
		return;

	// depth first so I don't see my new child of the collision box
	for (int c = 0; c < inode->NumberOfChildren(); c++) 
	{
		BuildCollisionVolume(inode->GetChildNode(c));
	}

	// add a collision volume for myself
	// if I am a joint and have a child with geometry...

	SClass_ID		SCID;
	Class_ID		CID;
	ObjectState		os;
	os = inode->EvalWorldState(0);
	
	if (os.obj)
	{
		SCID = os.obj->SuperClassID();	
		CID = os.obj->ClassID();

		if (SCID == GEOMOBJECT_CLASS_ID) 
		{

			theHold.Begin();

			Object *obj = (Object*)m_ip->CreateInstance(HELPER_CLASS_ID, CVOBJECT_CLASS_ID);
			Check_Pointer(obj);
	
			// Get a hold of the parameter block
			IParamArray *iParams = obj->GetParamBlock();
			Check_Pointer(iParams);

			// Set the parameters
			// Set to a default material
			iParams->SetValue(CV_MATERIAL,TimeValue(0),Adept::SteelMaterial);

			// Create the cv node
			INode *new_node = m_ip->CreateObjectNode(obj);			

			// Figure out the cv name
			{
				CString nodename = inode->GetName();
				int index = nodename.Find("_",0);
				if (index >=0)
					nodename.Delete(0,index+1);
				CString name = "cv_";
				name += nodename;
				new_node->SetName(name.GetBuffer(50));
			}

			TriObject *meshData;
			Verify(os.obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0)));
			meshData = (TriObject *) os.obj->ConvertToType(0,Class_ID(TRIOBJ_CLASS_ID, 0));
			meshData->mesh.buildBoundingBox();

			Box3 box;
			box = meshData->mesh.getBoundingBox();
			Point3 pt = box.Width();
			iParams->SetValue(CV_SIZE,TimeValue(0),pt);

			// figure out the correct offset
			Point3 offset;
			offset.x = box.pmin.x - (pt.x*-0.5f);
			offset.y = box.pmin.y - (pt.y*-0.5f);
			offset.z = box.pmin.z - (pt.z*-0.5f);			

			Point3 offset_trans = inode->GetObjOffsetPos();
			Quat   offset_rot   = inode->GetObjOffsetRot();

			//SPEW(("jerryeds", "obj-'%s'", inode->GetName()));
			//SPEW(("jerryeds", "qpvt - %f %f %f %f", offset_rot.x, offset_rot.y, offset_rot.z, offset_rot.w));
			//SPEW(("jerryeds", "pvt  - %f %f %f", offset_trans.x, offset_trans.y, offset_trans.z));
			//SPEW(("jerryeds", "pmin - %f %f %f", box.pmin.x, box.pmin.y, box.pmin.z));
			//SPEW(("jerryeds", "pmax - %f %f %f", box.pmax.x, box.pmax.y, box.pmax.z));
			//SPEW(("jerryeds", "pt   - %f %f %f", pt.x, pt.y, pt.z));
			//SPEW(("jerryeds", "ofst - %f %f %f", offset.x, offset.y, offset.z));
			
			offset.x += offset_trans.x;
			offset.y += offset_trans.y;
			offset.z += offset_trans.z;

			// Set transformation for the cv node
			Matrix3 node_tm;
			node_tm.IdentityMatrix();
			offset_rot.MakeMatrix(node_tm);
			node_tm.SetTrans(offset);
			
			new_node->SetNodeTM(0,node_tm);

			//find nearest joint_ parent node..
			INode *closest_parent = FindParentJoint(inode);
			
			if (closest_parent) {
				Check_Pointer(closest_parent);
				closest_parent->AttachChild(new_node,0);
				//SPEW(("jerryeds", "par-'%s'", closest_parent->GetName()));
			}			
			
			theHold.Accept(0);
		}
	}
}


INode *CvToolPanel::FindParentJoint(INode *inode)
{
	if (inode == NULL)
		return NULL;

	if (!strnicmp("joint_", inode->GetName(), 6))
	{
		//SPEW(("jerryeds", "FOUND - '%s'", inode->GetName()));
		return inode;
	}
	else
	{
		//SPEW(("jerryeds", "DEEPER - '%s'", inode->GetName()));
		return FindParentJoint(inode->GetParentNode());
	}



	//SPEW(("jerryeds", "END OF CHAIN - '%s'", inode->GetName()));

	return NULL;

}