/***************************************************************************
*                                                                           *
*                           3DS Max Test Plugin                             *
*                                                                           *
 ***************************************************************************
*                                                                           *
* Version : 1.0ß, March 11th, 1998                                          *
*                                                                           *
* Written By Loic Baumann from Fatal Design, specially for Gamasutra        *
*                                                                           *
 ***************************************************************************
*                                                                           *
* This Plugin demonstrates how to write a simple 3DS MAX Plugin, using MFCs *
*                                                                           *
 ***************************************************************************/

#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 "Replicate.h"
#include "CvObject.h"

#pragma pack(push,8)
#include <dummy.h>
#pragma pack(pop)

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

ReplicatePanel g_replicatePanel;

////////////////////////////////////////////////////////////////////////////////
// PlugPanel Dialog Procedure to handle User and System Interactions
static BOOL CALLBACK ReplicatePanelProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	switch (msg) {
		case WM_INITDIALOG:						//Panel's extra initialization
			g_replicatePanel.Init(hWnd);
			break;
		case WM_DESTROY:						//Panel's extra destruction
			g_replicatePanel.Destroy(hWnd);
			break;
		case WM_COMMAND :
			switch (LOWORD(wParam)) {			//Handle Panel's controls
				case IDC_ABOUT: {
					CAbout dlg;
					dlg.DoModal();
					break;
				}
				case IDC_REPLICATE: {
					g_replicatePanel.Replicate();
					break;
				}							   
				case IDC_BAKE: {
					g_replicatePanel.Bake();
					break;
				}
			}
			break;

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

		default:
			return FALSE;
		}
	return TRUE;
}


////////////////////////////////////////////////////////////////////////////////
// Display the Plugin's Panel
void ReplicatePanel::BeginEditParams(Interface* ip, IUtil* iu)
{
	m_ip = ip;								//Get the Plugin's Interface
	m_iu = iu;								//Get the Utility Plugin's Interface
	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	HINSTANCE loc_hInstance = AfxGetResourceHandle ();

	//Display the plugin's Rollup Dialog
	m_hPanel = m_ip->AddRollupPage(loc_hInstance, MAKEINTRESOURCE(IDD_REP_PANEL),
		ReplicatePanelProc, GetString(IDS_REPLICATE_NAME),0);
}

////////////////////////////////////////////////////////////////////////////////
// Free the Plugins' Panel
void ReplicatePanel::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 ReplicatePanel::Init(HWND hWnd)
{
}

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



////////////////////////////////////////////////////////////////////////////////
// Called when the Optimize Button is pushed
void ReplicatePanel::Replicate()
{

	AFX_MANAGE_STATE(AfxGetStaticModuleState());
	// Notes for undo:
	// all I have to do is store the new
	// parent nodes pointer in the hold
	// the delete is already in the hold
	// cuz max is nice like that
	// all I have to do is delete my creation
	// and I can do that by keeping a pointer to 
	// the node and just delete it! 
	// Sticky point: Interaction between deleting 
	// while in restore mode, since deleting actualy
	// stores the undo info? hmmm no idea.

	INode*		theNode;
	

	//Display the HitByName dialog
	TestHitByNameDlgCallback myCallback;
	if (!m_ip->DoHitByNameDialog(&myCallback))	return;

	//Get the Selected Node Table and the nodes' number
	if (!myCallback.NodeTab.Count())			return;
	theNode = *myCallback.NodeTab.Addr(0);


	ThresholdDialog thresh_dialog;

	if (thresh_dialog.DoModal() == IDOK)
	{
		//Display the Node's Info Dialog

		// Get the threshold...
		JointEditDialog edit_dialog(theNode, m_ip);
		
		if (edit_dialog.DoModal() == IDOK)
		{
			theHold.SuperBegin();

			// no modal this time!
			// use the Max progress bar...  Interface::Progressthingamagiggy


			// keyframe reduction is fast enough so for now just use 
			// the joint count and how far it has gone.

			INode *new_copy = CopyMeAndMyChildren(theNode); // the root
			
			// copy the animation is now done in the copy routine
			CopyAnimation(theNode, new_copy, &edit_dialog);


			// try drawing first...
			// Redraw the display
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);

			
			
            // strip the temp bones and helpers out...
			StripBones(new_copy);


			if (thresh_dialog.m_deleteOriginal)
			{
				//delete original
				DeleteMeAndMyChildren(theNode);
			}


			int node_count = JointEditDialog::CountChildren(new_copy);

			LPVOID arg = NULL;
			m_ip->ProgressStart(_T("Optimizing Joints"), TRUE, fn, arg);
			
			int current_node = 0;
			OptimizeAnimation(new_copy, current_node, node_count,&edit_dialog);
			
			m_ip->ProgressEnd();


			// Move the new node over and tell it's kids to do the same.

			if (thresh_dialog.m_offsetModelFlag)
			{
				// make sure we are not animating this...oy how emarassing
				SuspendAnimate();
				AnimateOff();


				Matrix3 axis(MAT_IDENT);
				Point3 translate(thresh_dialog.m_offsetX, thresh_dialog.m_offsetY, thresh_dialog.m_offsetZ);
				new_copy->Move(0, axis, translate, FALSE, TRUE, PIV_NONE);

				ResumeAnimate();

			}



				// Close out the undo
			theHold.SuperAccept("Replicate");

			
			// Redraw the display
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);
		}
	}
}

void ReplicatePanel::DeleteMeAndMyChildren(INode *parent_node)
{

	// Recursuvly Delete all the children
	while (parent_node->NumberOfChildren() != 0)
	{
		INode *child_node = parent_node->GetChildNode(0);
		assert(child_node != NULL);
		DeleteMeAndMyChildren(child_node);
	}

	// delete me!
	parent_node->Delete(m_ip->GetTime(), true);

}

INode *
	ReplicatePanel::CopyMeAndMyChildren(INode *copy_node)
{

	//MessageBox(NULL, copy_node->GetName(), "Just say OK", MB_OK|MB_APPLMODAL );

	INode *new_node = NULL;

	if (!copy_node->IsRootNode())
	{
		new_node = CopyNode(copy_node);
		assert(new_node != NULL);

		//CopyAnimation(copy_node, new_node);
	}

	
	int maxcount = copy_node->NumberOfChildren();
	for (int i = 0; i < maxcount; i++)
	{
		INode *child_node = copy_node->GetChildNode(i);
		assert(child_node != NULL);
		INode *copy_child_node = CopyMeAndMyChildren(child_node);
		assert(copy_child_node != NULL);

		// attach the copy_child_node to new_node
		if (!copy_node->IsRootNode())
		{
			new_node->AttachChild(copy_child_node, 1);
		}
	}

	return new_node;
}

INode *
	ReplicatePanel::CopyNode(INode *copy_node)
{

	INode *new_node = NULL;

	SClass_ID		SCID;
	Class_ID		CID;
	ObjectState		os;
	os = copy_node->EvalWorldState(m_ip->GetTime());
	
	ViewExp *vpt = m_ip->GetViewport(NULL);

	// new node = copy_node
	if (os.obj)
	{
		SCID = os.obj->SuperClassID();	
		CID = os.obj->ClassID();

		switch (SCID) 
		{
			case GEOMOBJECT_CLASS_ID :
				// We support this!!!
				// I'm going to convert it to a mesh some day
				// but right now go to a dummie with a bound the same size...
				{


					GeomObject* obj = (GeomObject*)os.obj;
					GeomObject* cloned_obj = (GeomObject*)obj->Clone();

					assert(cloned_obj != NULL);
					new_node = m_ip->CreateObjectNode(cloned_obj);
						
					// clone the refrences...
					// damn this is a bitch...
					
					// Transfer the material.. All of the other refs are copied

					if (copy_node->GetMtl() != NULL)
					{
						new_node->SetMtl(copy_node->GetMtl());
					}
					// Transfer the wireframe color...
					new_node->SetWireColor(copy_node->GetWireColor());
					
					TSTR name(copy_node->GetName());
					name = "_"+name;
					//m_ip->MakeNameUnique(name);
					
					new_node->SetName(name);
					
					//Set the object offset transform
					Point3 offset_trans = copy_node->GetObjOffsetPos();
					Quat offset_rot_quat = copy_node->GetObjOffsetRot();
					
					new_node->SetObjOffsetPos(offset_trans);
					new_node->SetObjOffsetRot(offset_rot_quat);
					
					// Set world transform
					
					TimeValue t(0);
					
					Matrix3 position = copy_node->GetNodeTM(t);
					new_node->SetNodeTM(t, position);
					
				}
				break;

			//case SHAPE_CLASS_ID:
			//	2D Shapes not supported right now
			//	break;

			case HELPER_CLASS_ID: 
				{
					
					// copy dummies and turn Bones into dummies
					// We support this!!!
					// I'm going to convert it to a mesh some day
					// but right now go to a dummie with a bound the same size...
					
					// We only support bones and dummies...
					
					if (CID == Class_ID(DUMMY_CLASS_ID,0) || CID == Class_ID(BONE_CLASS_ID, 0))
					{
						
						DummyObject *dummy = (DummyObject *)m_ip->CreateInstance(
							HELPER_CLASS_ID,
							Class_ID(DUMMY_CLASS_ID, 0)
							);
						
						assert(dummy != NULL);
						new_node = m_ip->CreateObjectNode(dummy);
						
						dummy->EnableDisplay();
						
						GeomObject* obj = (GeomObject*)os.obj;
						
						Box3 bound_box;
						obj->GetLocalBoundBox(m_ip->GetTime(), copy_node, vpt, bound_box);
						
						dummy->SetBox(bound_box);
						
					}
					if (CID == SITEOBJECT_CLASS_ID)
					{
						
						
						Object* obj = (Object*)os.obj;
						Object* cloned_obj = (Object*)obj->Clone();
						
						assert(cloned_obj != NULL);
						new_node = m_ip->CreateObjectNode(cloned_obj);
						
						
						// clone the refrences...
						// damn this is a bitch...
						
						// Transfer the material.. All of the other refs are copied
						
						if (copy_node->GetMtl() != NULL)
						{
							new_node->SetMtl(copy_node->GetMtl());
						}
						// Transfer the wireframe color...
						new_node->SetWireColor(copy_node->GetWireColor());
						
					}
					if (CID == CVOBJECT_CLASS_ID)
					{
						
						
						CvObject* obj = (CvObject*)os.obj;
						CvObject* cloned_obj = (CvObject*)obj->Clone();
						
						assert(cloned_obj != NULL);
						new_node = m_ip->CreateObjectNode(cloned_obj);
						
						
						// clone the refrences...
						// damn this is a bitch...
						
						// Transfer the material.. All of the other refs are copied
						
						if (copy_node->GetMtl() != NULL)
						{
							new_node->SetMtl(copy_node->GetMtl());
						}
						// Transfer the wireframe color...
						new_node->SetWireColor(copy_node->GetWireColor());
						
						
					}
					
					TSTR name(copy_node->GetName());
					name = "_"+name;
					//m_ip->MakeNameUnique(name);
					
					new_node->SetName(name);
					
					//Set the object offset transform
					Point3 offset_trans = copy_node->GetObjOffsetPos();
					Quat offset_rot_quat = copy_node->GetObjOffsetRot();
					
					new_node->SetObjOffsetPos(offset_trans);
					new_node->SetObjOffsetRot(offset_rot_quat);
					
					// Set world transform
					
					TimeValue t(0);
					
					Matrix3 position = copy_node->GetNodeTM(t);
					new_node->SetNodeTM(t, position);								
				}
				break;

			default:
				assert(0);
				break;
		}
	}


	Verify(new_node != NULL);

	// copy the pref data over...
	JointEditDialog::CopyJointPrefrence(copy_node, new_node);

	return new_node;
}

void 
	ReplicatePanel::StripBones(INode * /*parent_node*/)
{
	
	// strip bones...
	
	// Only strip helper if it has geometry that can sub for it and that geometry doesn't 
	// have it's own animation.  If there is more than one geometry set directly attached to
	// the bone, don't disolve the bone since it is an optimization to animate two nodes with
	// one channel...Or combine the nodes, but only if they don't have animation.

	// Maybe don't disolve the bones if the artist is planing on having animation on both the bones
	// and the geometry attached to the bone.  Is that possible to do?  I'm not sure.
	
	// Anyway. To disolve a bone, detach the geometry from the bone and attach it to it's parent
	// Here is the tricky part.  Redefining the animation and Transform Matrix.  
	// Try not to change the animation, IE: attach the geometry to the same TM as the bone TM, then
	// make the object offset TM be the original objects offset transform times
	// the original objects local origin!!!!  =)  HEHEHEHEHE

	// IF not then we have to do weird stuff to the animation to make it correct again.


}

static void SetKeys(Control *control,DWORD tantype)
{
	Control *con = control->GetXController();
	IKeyControl *ikc = NULL;
	if (con) {
		ikc = GetKeyControlInterface(con);
		if (ikc)
		{
			int maxkeys=ikc->GetNumKeys();
			
			for (int x=0;x<maxkeys;x++)
			{
				IKey *key=(IKey *)new IBezFloatKey;
				ikc->GetKey(x,key);
				SetInTanType(key->flags,tantype);
				SetOutTanType(key->flags,tantype);
				ikc->SetKey(x,key);
			}
		}
	}
	con = control->GetYController();
	if (con) {
		ikc = GetKeyControlInterface(con);
		if (ikc)
		{
			int maxkeys=ikc->GetNumKeys();
			
			for (int x=0;x<maxkeys;x++)
			{
				IKey *key=(IKey *)new IBezFloatKey;
				ikc->GetKey(x,key);
				SetInTanType(key->flags,tantype);
				SetOutTanType(key->flags,tantype);
				ikc->SetKey(x,key);
			}
		}
	}
	con = control->GetZController();
	if (con) {
		ikc = GetKeyControlInterface(con);
		if (ikc)
		{
			int maxkeys=ikc->GetNumKeys();
			
			for (int x=0;x<maxkeys;x++)
			{
				IKey *key=(IKey *)new IBezFloatKey;
				ikc->GetKey(x,key);
				SetInTanType(key->flags,tantype);
				SetOutTanType(key->flags,tantype);
				ikc->SetKey(x,key);
			}
		}
	}
}

void 
	ReplicatePanel::CopyAnimation(INode * source_node, INode * target_node, JointEditDialog *dialog,int converttype)
{
	// Copyies controlers in order
	// If the controler is procedural or this is a bone
	// turn it into something we know and add the controler

	// Simplify the keyframes  future should ask the user which joints to optimize
	// or optimize procedural/bones and anything that has a one for one rate

	//afxDump << "Copying Animation : " << source_node->GetName() << "\n";
	//MessageBox(NULL, source_node->GetName(), "COPY ANIM", MB_OK|MB_APPLMODAL );


	// copy this anim...
	
	//If everything is known than just clone this puppy...

	JointPreference *pref = JointEditDialog::GetJointPrefrence(source_node);
	if (pref)
	{
		Verify(pref != NULL);



		Control *tm_controller = source_node->GetTMController();

		int include_joint = true;

		if (pref->m_excludeJoint == JointPreference::ExcludeJoint)
		{
			include_joint = false;
		}

		if (pref->m_excludeJoint == JointPreference::AutoJoint)
		{
			if(!dialog->NodeAnimated(source_node))
			{
				include_joint = false;
			}
		}

		if (tm_controller != NULL && include_joint)
		{

			if (pref->m_convertRotationJoint)
			{

				theHold.Begin();
				// replace controler pointer with new controller

				// if the type is known but the sub type isn't 
				// then convert that controller.
				
				// Example is RotationXYZ with a sub controler of SineWave or Noise

				Control *rotation_controler = NULL;

				if (converttype)
				{
					switch(pref->m_rotationConvertType)
					{
						case JointPreference::LinearKeyframeType:
							rotation_controler = (Control *)m_ip->CreateInstance(
								CTRL_ROTATION_CLASS_ID,
								Class_ID(LININTERP_ROTATION_CLASS_ID, 0)
						);
						break;
						case JointPreference::EulerXYZKeyframeType:
							rotation_controler = (Control *)m_ip->CreateInstance(
							CTRL_ROTATION_CLASS_ID,
							Class_ID(EULER_CONTROL_CLASS_ID, 0)
						);
							break;
						case JointPreference::PositionXYZKeyframeType:
							rotation_controler = (Control *)m_ip->CreateInstance(
							CTRL_ROTATION_CLASS_ID,
							Class_ID(EULER_CONTROL_CLASS_ID, 0)
						);
						break;

					}
				}
				else
				{
					rotation_controler = (Control *)m_ip->CreateInstance(
						CTRL_ROTATION_CLASS_ID,
						Class_ID(EULER_CONTROL_CLASS_ID, 0));
				}
				
				SuspendAnimate();
				AnimateOn();

				{
					
					Interval iv;
					int ticks_per_frame = GetTicksPerFrame();
					int num_of_frames = 1;

					TimeValue start =m_ip->GetAnimRange().Start();
					TimeValue end =m_ip->GetAnimRange().End();
					int delta = GetTicksPerFrame();

					for (int time_of_key=start; time_of_key<=end; time_of_key+=delta)
					{
						
						Quat rot;
						Point3 pos;
						
						// Get the pos and rot from the frame.

						// Compute the relative TM
						Matrix3 parentTM, nodeTM, localTM;
						nodeTM = source_node->GetNodeTM(time_of_key);
						parentTM = source_node->GetParentTM(time_of_key);
						localTM = nodeTM*Inverse(parentTM);

						AffineParts ap;
						decomp_affine(localTM, &ap);
						pos = ap.t;
						rot = ap.q;

						rotation_controler->SetValue(time_of_key,&rot,TRUE,CTRL_ABSOLUTE);
					}
				}

				ResumeAnimate();
				theHold.Accept(0);

				Control *target_master_control;
				target_master_control = target_node->GetTMController();
				target_master_control->SetRotationController(rotation_controler);
				SetKeys(rotation_controler,BEZKEY_LINEAR);
			}
			else
			{
				// clone the animation
				// Get the source control
				Control *source_master_control;
				Control *source_new_rotation_control = NULL;

				source_master_control = source_node->GetTMController();
				source_new_rotation_control = source_master_control->GetRotationController();
				assert(source_new_rotation_control != NULL);

				// Clone the source to the targets
				Control *target_master_control;
				Control *target_new_rotation_control = NULL;
				
				target_master_control = target_node->GetTMController();
				target_new_rotation_control = (Control *)source_new_rotation_control->Clone();


				// Paste them in
				// The help file said I didn't have to delete the old ones, but it doesn't
				// say if I should dereference them .  I'm assuming, for now, that the new 
				// position control is being deleted by the derefrencing method ( ie, nothing 
				// is left looking at them so they are deleted ).  But it doesn't say if I am 
				// the one to have to delete it.  I guess they are and I don't have to.
				assert(target_new_rotation_control != NULL);
				target_master_control->SetRotationController(target_new_rotation_control);

			}


			if (pref->m_convertTranslationJoint)
			{
				theHold.Begin();
				// replace controler pointer with new controller

				// if the type is known but the sub type isn't 
				// then convert that controller.
				
				// Example is PositionXYZ with a sub controler of SineWave or Noise

				Control *translation_controler = NULL;

				Control *posc = tm_controller->GetPositionController();
				if (posc)
				{
					Control *c=posc->GetXController();
					if (c)
						Class_ID cl = c->ClassID();
					Class_ID cl2 = posc->ClassID();
				}

				Class_ID cl1 = Class_ID(0x118f7e02,0xffee238a);

				if (converttype)
				{
					switch(pref->m_translationConvertType)
					{
						case JointPreference::LinearKeyframeType:
							translation_controler = (Control *)m_ip->CreateInstance(
							CTRL_POSITION_CLASS_ID,
							Class_ID(LININTERP_POSITION_CLASS_ID, 0)
						);
							break;
						case JointPreference::EulerXYZKeyframeType:
							translation_controler = 
							(Control *)m_ip->CreateInstance(
							CTRL_POSITION_CLASS_ID,
							cl1
						);
							break;
						case JointPreference::PositionXYZKeyframeType:
							//translation_controler = NewDefaultPoint3Controller();
							translation_controler = (Control *)m_ip->CreateInstance(
							CTRL_POSITION_CLASS_ID,
							cl1
						);
						break;

					}
				}
				else
				{
					translation_controler = 
						(Control *)m_ip->CreateInstance(
						CTRL_POSITION_CLASS_ID,
						cl1);
				}
				
				SuspendAnimate();
				AnimateOn();

				{
					
					Interval iv;
					int ticks_per_frame = GetTicksPerFrame();
					int num_of_frames = 1;

					TimeValue start =m_ip->GetAnimRange().Start();
					TimeValue end =m_ip->GetAnimRange().End();
					int delta = GetTicksPerFrame();

					for (int time_of_key=start; time_of_key<=end; time_of_key+=delta)
					{
						
						Quat rot;
						Point3 pos;
						
						// Get the pos and rot from the frame.

						// Compute the relative TM
						Matrix3 parentTM, nodeTM, localTM;
						nodeTM = source_node->GetNodeTM(time_of_key);
						parentTM = source_node->GetParentTM(time_of_key);
						localTM = nodeTM*Inverse(parentTM);

						AffineParts ap;
						decomp_affine(localTM, &ap);
						pos = ap.t;
						rot = ap.q;

						translation_controler->SetValue(time_of_key,&pos,TRUE,CTRL_ABSOLUTE);
					}
				}

				ResumeAnimate();
				theHold.Accept(0);

				Control *target_master_control;
				target_master_control = target_node->GetTMController();
				target_master_control->SetPositionController(translation_controler);
				SetKeys(translation_controler,BEZKEY_LINEAR);
			}
			else
			{
				// clone the animation
				// Get the source control
				Control *source_master_control;
				Control *source_new_position_control = NULL;

				source_master_control = source_node->GetTMController();
				source_new_position_control = source_master_control->GetPositionController();
				assert(source_new_position_control != NULL);
				

				// Clone the source to the targets
				Control *target_master_control;
				Control *target_new_position_control = NULL;
				
				target_master_control = target_node->GetTMController();
				target_new_position_control = (Control *)source_new_position_control->Clone();
				

				// Paste them in
				// The help file said I didn't have to delete the old ones, but it doesn't
				// say if I should dereference them .  I'm assuming, for now, that the new 
				// position control is being deleted by the derefrencing method ( ie, nothing 
				// is left looking at them so they are deleted ).  But it doesn't say if I am 
				// the one to have to delete it.  I guess they are and I don't have to.
				assert(target_new_position_control != NULL);
				target_master_control->SetPositionController(target_new_position_control);
			}
		
		}

}
	// do my children..
	assert(source_node->NumberOfChildren() == target_node->NumberOfChildren());

	for (int i = 0; i < source_node->NumberOfChildren(); i++)
	{
		INode *child_node_source = source_node->GetChildNode(i);
		assert(child_node_source != NULL);
		
		INode *child_node_target = target_node->GetChildNode(i);
		assert(child_node_target != NULL);
		
		CopyAnimation(child_node_source, child_node_target, dialog,converttype);	
	}

}


////////////////////////////////////////////////////////////////////////////////

void
	ReplicatePanel::OptimizeAnimation(INode *copy_node, int &current_node, int total_nodes,  JointEditDialog *dialog)
{

	
	Control *position_control = NULL;
	Control *rotation_control = NULL;

	Control *tm_controller = copy_node->GetTMController();

	if (tm_controller != NULL)
	{

		position_control = tm_controller->GetPositionController();
		rotation_control = tm_controller->GetRotationController();

		JointPreference *pref = JointEditDialog::GetJointPrefrence(copy_node);
		Verify(pref != NULL);

		int include_joint = true;

		if (pref->m_excludeJoint == JointPreference::ExcludeJoint)
		{
			include_joint = false;
		}

		if (pref->m_excludeJoint == JointPreference::AutoJoint)
		{
			if(!dialog->NodeAnimated(copy_node))
			{
				include_joint = false;
			}
		}

		if (include_joint)
		{
			if (pref->m_optimizeRotation)
			{
				if (rotation_control != NULL)
				{
					int num_keys = rotation_control->NumKeys();

					if (num_keys > 0)
					{

						TimeValue start = rotation_control->GetKeyTime(0);
						TimeValue end = rotation_control->GetKeyTime(num_keys-1);

						if (pref->m_rotationRange)
						{
							start = (TimeValue)pref->m_rotationSampleStartFrame * GetTicksPerFrame();
							end = (TimeValue)pref->m_rotationSampleEndFrame * GetTicksPerFrame();
						}

						Interval range;
						start = (rotation_control->GetKeyTime(0) > start)   ? rotation_control->GetKeyTime(0):start;
						end = (rotation_control->GetKeyTime(num_keys-1) < end) ? rotation_control->GetKeyTime(num_keys-1):end;
						
						
						
						// adjust the optimization to not include the last frame... tehehehehehe
						if ((end - GetTicksPerFrame()) > start)
						{
							end -= GetTicksPerFrame();
						}
						range.Set(start , end);

						RepReduceStatus rot_progress;
						
						ApplyKeyReduction_MW4(
								rotation_control, 
								range, 
								pref->m_rotationThreshold * Stuff::Radians_Per_Degree, 
								GetTicksPerFrame(), 
								&rot_progress
							);
					}
				}
			}

			if (pref->m_optimizeTranslation)
			{
				if (position_control != NULL)
				{
					int num_keys = position_control->NumKeys();
				
					if (num_keys > 0)
					{
						TimeValue start = position_control->GetKeyTime(0);
						TimeValue end = position_control->GetKeyTime(num_keys-1);

						if (pref->m_translationRange)
						{
							start = (TimeValue)pref->m_translationSampleStartFrame * GetTicksPerFrame();
							end = (TimeValue)pref->m_translationSampleEndFrame * GetTicksPerFrame();
						}

						Interval range;
						start = (position_control->GetKeyTime(0) > start)   ? position_control->GetKeyTime(0):start;
						end = (position_control->GetKeyTime(num_keys-1) < end) ? position_control->GetKeyTime(num_keys-1):end;
						
						// adjust the optimization to not include the last frame... tehehehehehe
						if ((end - GetTicksPerFrame()) > start)
						{
							end -= GetTicksPerFrame();
						}
						range.Set(start , end);
						
						RepReduceStatus tran_progress;
						
						ApplyKeyReduction_MW4(
								position_control, 
								range, 
								pref->m_translationThreshold, 
								GetTicksPerFrame(), 
								&tran_progress
							);
					}
				}
			}
		}
	}
	
	++current_node;
	
	int total_done = (int)(((float)current_node/(float)total_nodes)*100.0f);
	m_ip->ProgressUpdate(total_done);
		

	for (int i = 0; i < copy_node->NumberOfChildren(); i++)
	{
		INode *child_node = copy_node->GetChildNode(i);
		assert(child_node != NULL);
		OptimizeAnimation(child_node, current_node, total_nodes, dialog);	
	}

	return;
}

static void FixNames(INode *pNode)
{

	CString str = pNode->GetName();
	if ('_' == str[0])
	{
		str.Delete(0,1);
		pNode->SetName((char *)(LPCTSTR)str);
	}
	for (int x=0;x<pNode->NumberOfChildren();x++)
	{
		INode *pChild = pNode->GetChildNode(x);
		FixNames(pChild);
	}
}

void ReplicatePanel::Bake()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState());

	INode* theNode =m_ip->GetRootNode();



//	ThresholdDialog thresh_dialog;

//	if (thresh_dialog.DoModal() == IDOK)
	{
		//Display the Node's Info Dialog

		// Get the threshold...
//		JointEditDialog edit_dialog(theNode, m_ip);
		
//		if (edit_dialog.DoModal() == IDOK)
		{
			theHold.SuperBegin();

			// no modal this time!
			// use the Max progress bar...  Interface::Progressthingamagiggy


			// keyframe reduction is fast enough so for now just use 
			// the joint count and how far it has gone.

			int count = theNode->NumberOfChildren();
			for (int x=0;x<count;x++)
			{
				INode *pChild = theNode->GetChildNode(x);
				JointEditDialog edit_dialog(theNode, m_ip);
				edit_dialog.AddJointPreference(pChild);

				INode *new_copy = CopyMeAndMyChildren(pChild); // the root
			
				// copy the animation is now done in the copy routine
				//CopyAnimation(theNode, new_copy, &edit_dialog);
				CopyAnimation(pChild, new_copy, &edit_dialog,0);


				// try drawing first...
				// Redraw the display
				m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
				m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
				m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);

				
				
				// strip the temp bones and helpers out...
				StripBones(new_copy);


			//	if (thresh_dialog.deleteOriginal)
				{
					//delete original
					DeleteMeAndMyChildren(pChild);
				}

				FixNames(new_copy);
			}
			// TODO: Rename the nodes to get the names right

			// Move the new node over and tell it's kids to do the same.
/*
			if (thresh_dialog.offsetModelFlag)
			{
				// make sure we are not animating this...oy how emarassing
				SuspendAnimate();
				AnimateOff();


				Matrix3 axis(MAT_IDENT);
				Point3 translate(thresh_dialog.offsetX, thresh_dialog.offsetY, thresh_dialog.offsetZ);
				new_copy->Move(0, axis, translate, FALSE, TRUE, PIV_NONE);

				ResumeAnimate();

			}
*/

				// Close out the undo
			theHold.SuperAccept("Replicate");

			
			// Redraw the display
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_BEGIN);
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_NORMAL);
			m_ip->RedrawViews(m_ip->GetTime(),REDRAW_END);
		}
	}
}