#pragma warning( disable:4800 )

#include <map>
#include "NifProps.h"
#include "NifStrings.h"
#include "NifPlugins.h"
#include "NifGui.h"
#include "bhkRigidBodyInterface.h"

using namespace std;

#define PBLOCK_REF	0
const Class_ID BHKRIGIDBODYMODIFIER_CLASS_ID = Class_ID(0x398fd801, 0x303e44e5);
class PickObjectMode;

extern void BuildBox(Mesh&mesh, float l, float w, float h);
extern void BuildSphere(Mesh&mesh, float radius, int segs=32, int smooth=1, float startAng = 0.0f);
extern void BuildScubaMesh(Mesh &mesh, int segs, int smooth, int llsegs, 
                    float radius1, float radius2, float cylh);

void CalcAxisAlignedBox(Mesh& mesh, Box3& box)
{
   int nv = mesh.getNumVerts();
   box.IncludePoints(mesh.getVertPtr(0), nv, NULL);
}

// Calculate bounding sphere using minimum-volume axis-align bounding box.  Its fast but not a very good fit.
void CalcAxisAlignedSphere(Mesh& mesh, Point3& center, float& radius)
{
   //--Calculate center & radius--//

   //Set lows and highs to first vertex
   int nv = mesh.getNumVerts();
   
   Point3 lows = mesh.getVert(0);
   Point3 highs = mesh.getVert(0);

   //Iterate through the vertices, adjusting the stored values
   //if a vertex with lower or higher values is found
   for ( unsigned int i = 0; i < nv; ++i ) {
      const Point3 & v = mesh.getVert(i);

      if ( v.x > highs.x ) highs.x = v.x;
      else if ( v.x < lows.x ) lows.x = v.x;

      if ( v.y > highs.y ) highs.y = v.y;
      else if ( v.y < lows.y ) lows.y = v.y;

      if ( v.z > highs.z ) highs.z = v.z;
      else if ( v.z < lows.z ) lows.z = v.z;
   }

   //Now we know the extent of the shape, so the center will be the average
   //of the lows and highs
   center = (highs + lows) / 2.0f;

   //The radius will be the largest distance from the center
   Point3 diff;
   float dist2(0.0f), maxdist2(0.0f);
   for ( unsigned int i = 0; i < nv; ++i ) {
      const Point3 & v = mesh.getVert(i);

      diff = center - v;
      dist2 = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
      if ( dist2 > maxdist2 ) maxdist2 = dist2;
   };
   radius = sqrt(maxdist2);
}

// Calculate bounding sphere using average position of the points.  Better fit but slower.
void CalcCenteredSphere(Mesh& mesh, Point3& center, float& radius)
{
   int nv = mesh.getNumVerts();
   Point3 sum;
   for (int i=0; i<nv; ++i)
      sum += mesh.getVert(i);
   center = sum / float(nv);
   float radsq = 0.0f;
   for (int i=0; i<nv; ++i){
      Point3 diff = mesh.getVert(i) - center;
      float mag = diff.LengthSquared();
      radsq = max(radsq, mag);
   }
   radius = Sqrt(radsq);
}


class bhkValidatorClass : public PBValidator
{
public:
   class bhkRigidBodyModifier *mod;
private:
   BOOL Validate(PB2Value &v) 
   {
      INode *node = (INode*) v.r;
      if (node->TestForLoop(FOREVER,(ReferenceMaker *) mod)!=REF_SUCCEED) return FALSE;

      ObjectState os = node->EvalWorldState(0);
      //Allow only tri object derived objects
      if (os.obj->CanConvertToType(triObjectClassID)  && os.obj->SuperClassID() != SHAPE_CLASS_ID) {			
         return TRUE;
      }
      // and out objects which support the RigidBodyInterface
      //if (os.obj->SuperClassID() == HELPER_CLASS_ID && NULL != GetInterface(BHKRIGIDBODYINTERFACE_DESC)) {
      //   return TRUE;
      //}
      if (os.obj->SuperClassID() == HELPER_CLASS_ID && os.obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB() ) {
         return TRUE;
      }
      return FALSE;
   };
};

enum { bv_type_none, bv_type_box, bv_type_sphere, bv_type_capsule, bv_type_mesh };  // pblock ID

class bhkRigidBodyModifier : public Modifier, public bhkRigidBodyIfcHelper
{

public:

	bhkRigidBodyModifier();
	~bhkRigidBodyModifier();		

   void DeleteThis() { delete this; }
   void GetClassName(TSTR& s) { s = "bhkRigidBodyModifier"; }  
   virtual Class_ID ClassID() { return BHKRIGIDBODYMODIFIER_CLASS_ID; }		
   RefTargetHandle Clone(RemapDir& remap);
   TCHAR *GetObjectName() { return "bhkRigidBodyModifier"; }

   // From modifier
	ChannelMask ChannelsUsed()  {return PART_GEOM|PART_TOPO;}
	ChannelMask ChannelsChanged() {return PART_GEOM;}
//	ChannelMask ChannelsUsed()  { return 0; }
//   ChannelMask ChannelsChanged() { return 0; }
   Class_ID InputType() { return mapObjectClassID; }
   void ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *node);
   Interval LocalValidity(TimeValue t) { return GetValidity(t); }
   Interval GetValidity (TimeValue t);
   BOOL DependOnTopology(ModContext &mc) { return TRUE; }
	void NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc);

   void BeginEditParams (IObjParam  *ip, ULONG flags,Animatable *prev);
   void EndEditParams (IObjParam *ip,ULONG flags,Animatable *next);		

	int Display(TimeValue t, INode* inode, ViewExp *vpt, int flagst, ModContext *mc);


	void			SelectionSetChanged(Interface *ip,IUtil *iu);

	void			selectionChanged();
   void        saveState();
   INT_PTR	dlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

   // ParamBlock2 access:
   int NumParamBlocks () { return 1; }
   IParamBlock2* GetParamBlock(int i) 
   { 
      if (i == 0) return pblock;
      return NULL;
   }
   IParamBlock2* GetParamBlockByID(BlockID id) { 
      if (pblock->ID() == id) return pblock;
      return NULL; 
   }

   CreateMouseCallBack* GetCreateMouseCallBack() {return NULL;} 

   virtual	int NumRefs();
   virtual  RefTargetHandle GetReference(int i);
   virtual  void SetReference(int i, RefTargetHandle rtarg);
   virtual  RefResult   NotifyRefChanged(Interval,RefTargetHandle,PartID &,RefMessage);

   int NumSubs() {return 1;}
   Animatable* SubAnim(int i) { return GetReference(i); }
   TSTR SubAnimName(int i) {return GetString (IDS_PARAMS);}

   BOOL SetObject(INode *newNode);
   INode* GetObject();

	void UpdateBVDialogs();

   BaseInterface *GetInterface(Interface_ID id) {
      if (id == BHKRIGIDBODYINTERFACE_DESC)
         return this;
      return Modifier::GetInterface(id);
   }

public:
   friend class PickObjectMode;

   StdMat2* collMat;
	Mesh mesh;

   PickObjectMode *pickObMode;
	IParamBlock2 *pblock;
   IParamMap2 *pmapParam;
	IParamMap2 *pbvParams[4]; //box, sphere, capsule, proxy

	Interface		*mIP;
   Tab<INode*> mNodes;

	NpComboBox		mCbLayer;
	NpComboBox		mCbMaterial;
	NpComboBox		mCbMotionSystem;
	NpComboBox		mCbQualityType;
   bhkValidatorClass validator;
   ICustButton		*iPickButton;

	void enableGUI(BOOL object, BOOL hvk, BOOL anim);		
};

extern ClassDesc2* GetbhkRigidBodyModifierDesc();
static ParamBlockDesc2& get_havok_param_blk();

class bhkRigidBodyModifierClassDesc : public ClassDesc2
{
	public:
   bhkRigidBodyModifierClassDesc() {
      get_havok_param_blk().SetClassDesc(this);
   }
	int 			IsPublic() { return TRUE; }
	void *			Create(BOOL loading = FALSE) { 
      return new bhkRigidBodyModifier();
   }
	const TCHAR *	ClassName() { return GetString(IDS_RB_CLASS_NAME); }
	SClass_ID		SuperClassID() { return OSM_CLASS_ID; }
	Class_ID		ClassID() { return BHKRIGIDBODYMODIFIER_CLASS_ID; }
	const TCHAR* 	Category() { return GetString(IDS_CATEGORY); }

	const TCHAR*	InternalName() { return _T("bhkRigidBodyModifier"); }
	HINSTANCE		HInstance() { return hInstance; }
};



// Parameter and ParamBlock IDs
enum { havok_params, bv_box, bv_sphere, bv_capsule, bv_mesh};  // pblock ID
enum { PB_BOUND_TYPE, PB_MESHLIST, };
enum { havok_params_panel, };

class BVTypePBAccessor : public PBAccessor
{ 
public:

	void Set(PB2Value& v, ReferenceMaker* owner, ParamID id, int tabIndex, TimeValue t)    // set from v
	{
		bhkRigidBodyModifier* p = (bhkRigidBodyModifier*)owner;
		switch (id)
		{
		case PB_BOUND_TYPE:
			{
				switch (v.i)
				{ 
            case bv_type_none:
               // Delete mesh.
               break;

            case bv_type_box: // Box
               
               //BuildBox(mesh,,,)
               break;

            case bv_type_sphere: // Sphere
               //BuildSphere();
               break;

            case bv_type_capsule: // Capsule
               //BuildScubaMesh();
               break;

            case bv_type_mesh: 
               break;
				}
			}
		}
	}
};


static BVTypePBAccessor bv_type_accessor;


static ParamBlockDesc2 havok_param_blk ( 
   havok_params, _T("BoundingVolumes"),  0, NULL, P_AUTO_CONSTRUCT + P_AUTO_UI + P_MULTIMAP, PBLOCK_REF,
   //rollout
   2,
   havok_params,	IDD_RB_MOD_PANEL,  IDS_PARAMS, 0, 0, NULL, 
	bv_mesh,			IDD_RB_MOD_PANEL4, IDS_RB_MOD_PANEL4, 0, 0, NULL, 

	PB_BOUND_TYPE, 	_T("boundType"),	TYPE_INT, 0, IDS_BV_BOUNDING_TYPE,
		p_default, 		0, 
		p_range, 		0, 4, 
		p_ui, 			havok_params,	TYPE_RADIO, 5, IDC_RDO_NO_COLL, IDC_RDO_AXIS_ALIGNED_BOX, IDC_RDO_SPHERE, IDC_RDO_CAPSULE, IDC_RDO_PROXY_MESH,
		p_accessor,		&bv_type_accessor,
		end,

	PB_MESHLIST,   _T("meshList"),  TYPE_INODE_TAB,		0,	P_AUTO_UI|P_VARIABLE_SIZE,	IDS_MESHLIST,
		p_ui,       bv_mesh, TYPE_NODELISTBOX, IDC_LIST1,IDC_ADD,0,IDC_REMOVE,
		end,

#if 0
   // params
   PB_RB_LAYER, _T("Layer"), TYPE_INT, 0,	IDS_DS_LAYER,
      p_default,	NP_DEFAULT_HVK_LAYER,
   end,

   PB_RB_MATERIAL, _T("Material"), TYPE_INT, 0,	IDS_DS_MATERIAL,
      p_default,	NP_DEFAULT_HVK_MATERIAL,
   end,

   PB_RB_MASS, _T("mass"), TYPE_FLOAT, 0,	IDS_DS_MASS,
      p_default,	NP_DEFAULT_HVK_MASS,
      p_range,		0.0, 1000.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_MASS, IDC_SP_MASS, SPIN_AUTOSCALE,
   end,

   PB_RB_FRICTION, _T("friction"), TYPE_FLOAT, 0,	IDS_DS_FRICTION,
      p_default,	NP_DEFAULT_HVK_FRICTION,
      p_range,		0.0, 10.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_FRICTION, IDC_SP_FRICTION, SPIN_AUTOSCALE,
   end,

   PB_RB_RESTITUTION, _T("restitution"), TYPE_FLOAT, 0,	IDS_DS_RESTITUTION,
      p_default,	NP_DEFAULT_HVK_RESTITUTION,
      p_range,		0.0, 10.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_RESTITUTION, IDC_SP_RESTITUTION, SPIN_AUTOSCALE,
   end,

   PB_RB_LINEAR_DAMPING, _T("linear_damping"), TYPE_FLOAT, 0,	IDS_DS_LINEAR_DAMPING,
      p_default,	NP_DEFAULT_HVK_LINEAR_DAMPING,
      p_range,		0.0, 10.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_LINEAR_DAMPING, IDC_SP_LINEAR_DAMPING, SPIN_AUTOSCALE,
   end,

   PB_RB_ANGULAR_DAMPING, _T("angular_damping"), TYPE_FLOAT, 0,	IDS_DS_ANGULAR_DAMPING,
      p_default,	NP_DEFAULT_HVK_ANGULAR_DAMPING,
      p_range,		0.0, 10.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_ANGULAR_DAMPING, IDC_SP_ANGULAR_DAMPING, SPIN_AUTOSCALE,
   end,

   PB_RB_MAX_LINEAR_VELOCITY, _T("max_linear_velocity"), TYPE_FLOAT, 0,	IDS_DS_MAX_LINEAR_VELOCITY,
      p_default,	NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY,
      p_range,		0.0, 10.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_MAX_LINEAR_VELOCITY, IDC_SP_MAX_LINEAR_VELOCITY, SPIN_AUTOSCALE,
   end,

   PB_RB_MAX_ANGULAR_VELOCITY, _T("max_angular_velocity"), TYPE_FLOAT, 0,	IDS_DS_MAX_ANGULAR_VELOCITY,
      p_default,	NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY,
      p_range,		0.0, 10.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_MAX_ANGULAR_VELOCITY, IDC_SP_MAX_ANGULAR_VELOCITY, SPIN_AUTOSCALE,
   end,

   PB_RB_PENETRATION_DEPTH, _T("penetration_depth"), TYPE_FLOAT, 0,	IDS_DS_PENETRATION_DEPTH,
      p_default,	NP_DEFAULT_HVK_PENETRATION_DEPTH,
      p_range,		0.0, 10.0,
      p_ui, TYPE_SPINNER, EDITTYPE_UNIVERSE,  IDC_ED_PENETRATION_DEPTH, IDC_SP_PENETRATION_DEPTH, SPIN_AUTOSCALE,
   end,

   PB_RB_MOTION_SYSTEM, _T("motion_system"), TYPE_INT, 0, IDS_DS_MOTION_SYSTEM,
      p_default,	NP_DEFAULT_HVK_MOTION_SYSTEM,
   end,

   PB_RB_QUALITY_TYPE, _T("quality_type"), TYPE_INT, 0,	IDS_DS_QUALITY_TYPE,
      p_default,	NP_DEFAULT_HVK_QUALITY_TYPE,
   end,
#endif


   //PB_HK_NODE, 		_T("node"), 		TYPE_INODE, 	0,		IDS_DS_NODE,
   //   p_ui, 			TYPE_PICKNODEBUTTON,    IDC_PICK_NODE, 
   //   p_prompt,      IDS_PICKNODE_PROMPT,
   //   //p_validator, &validator,
   end,
   end
   );

static bhkRigidBodyModifierClassDesc bhkRigidBodyModifierDesc;
ClassDesc2* GetbhkRigidBodyModifierDesc() { return &bhkRigidBodyModifierDesc; }
ParamBlockDesc2& get_havok_param_blk() {
   return havok_param_blk;
}


class PickObjectMode : 
   public PickModeCallback,
   public PickNodeCallback {
public:		
   bhkRigidBodyModifier *mod;

   BOOL HitTest(IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags);
   BOOL Pick(IObjParam *ip,ViewExp *vpt);
   void EnterMode(IObjParam *ip);
   void ExitMode(IObjParam *ip);
   BOOL RightClick(IObjParam *ip,ViewExp *vpt)	{return TRUE;}
   BOOL Filter(INode *node);		
   PickNodeCallback *GetFilter() {return this;}
};

//--- PickObjectMode ------------------------------------------------

BOOL PickObjectMode::Filter(INode *node)
{
   if (node) {
      node->BeginDependencyTest();
      mod->NotifyDependents(FOREVER,0,REFMSG_TEST_DEPENDENCY);
      if (node->EndDependencyTest()) {		
         return FALSE;
      } 
      ////added code for looptest
      //if (node->TestForLoop(FOREVER,(ReferenceMaker *) mod)!=REF_SUCCEED)
      //   return FALSE;

      for (int i = 0;i < mod->pblock->Count(PB_MESHLIST); i++) {
         INode *tnode = NULL;
         mod->pblock->GetValue(PB_MESHLIST,0,tnode,FOREVER,i);	
         if  (node == tnode)
            return FALSE;
      }

      ObjectState os = node->EvalWorldState(0);
      //added code such that lines are not selected
      if ( (os.obj->IsSubClassOf(triObjectClassID) || os.obj->CanConvertToType(triObjectClassID))
         && (os.obj->SuperClassID() != SHAPE_CLASS_ID) 
         ) 
      {			
         return TRUE;
      }
      if (os.obj->SuperClassID() == HELPER_CLASS_ID && os.obj->ClassID().PartB() == BHKRIGIDBODYCLASS_DESC.PartB() ) {
         return TRUE;
      }
   }
   return FALSE;
}

BOOL PickObjectMode::HitTest(
                             IObjParam *ip,HWND hWnd,ViewExp *vpt,IPoint2 m,int flags)
{
   INode *node = mod->mIP->PickNode(hWnd,m, this);	//added "this" argument such that the Filter above is used
   return node?TRUE:FALSE;
}

BOOL PickObjectMode::Pick(IObjParam *ip,ViewExp *vpt)
{
   BOOL rv = FALSE;
   if (INode *node = vpt->GetClosestHit()) {
      theHold.Begin();
      ObjectState os = node->EvalWorldState(0);
      if (os.obj->CanConvertToType(triObjectClassID)) {
         mod->pblock->Append(PB_MESHLIST,1,&node,1);
         rv = TRUE;
      }
      theHold.Accept(GetString(IDS_ADD_MESH));
   }
   return rv;
}

void PickObjectMode::EnterMode(IObjParam *ip)
{mod->iPickButton->SetCheck(TRUE);}

void PickObjectMode::ExitMode(IObjParam *ip)
{mod->iPickButton->SetCheck(FALSE);}

static PickObjectMode thePickMode;

class bhkRigidBodyModifierDlgProc : public ParamMap2UserDlgProc {
public:
   bhkRigidBodyModifier *mod;
   bhkRigidBodyModifierDlgProc(bhkRigidBodyModifier* m) {mod = m;}		
   INT_PTR DlgProc(TimeValue t,IParamMap2 *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);		
   void DeleteThis() {delete this;}		
};

INT_PTR bhkRigidBodyModifierDlgProc::DlgProc (TimeValue t,IParamMap2 *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
   switch (msg) 
   {
   case WM_INITDIALOG:
      //mod->iPickButton = GetICustButton(GetDlgItem(hWnd, IDC_ADD));
      //mod->iPickButton->SetType(CBT_CHECK);
      //mod->iPickButton->SetHighlightColor(GREEN_WASH);
      break;

   case WM_DESTROY:
      if (mod && mod->iPickButton != NULL) {
         ReleaseICustButton(mod->iPickButton);
         mod->iPickButton = NULL;
      }
      break;

   case WM_COMMAND:
      switch (LOWORD(wParam))
      {
      case IDC_ADD:
         {
            thePickMode.mod = mod;
            GetCOREInterface()->SetPickMode(&thePickMode);
            break;
         }
      }
      break;

   default:
      return FALSE;
   }

   return TRUE;
}

//--- bhkRigidBodyModifier -------------------------------------------------------

bhkRigidBodyModifier::bhkRigidBodyModifier()
{
	mIP = NULL;	
	pblock = NULL;
   pmapParam = NULL;
   pickObMode = NULL;
   validator.mod = this;
   iPickButton = NULL;
   bhkRigidBodyModifierDesc.MakeAutoParamBlocks(this);

   collMat = NewDefaultStdMat();
   collMat->SetDiffuse(Color(1.0f, 0.0f, 0.0f), 0);
   collMat->SetWire(TRUE);
}

bhkRigidBodyModifier::~bhkRigidBodyModifier()
{
   havok_param_blk.SetUserDlgProc();
#if 0
   if (NULL != pickObMode) {
      delete pickObMode;
      pickObMode = NULL;
   }
#endif
   if (pmapParam) {
      DestroyCPParamMap2 (pmapParam);
      pmapParam = NULL;
   }
}

RefTargetHandle bhkRigidBodyModifier::Clone(RemapDir& remap) {
   bhkRigidBodyModifier *mod = new bhkRigidBodyModifier();
   mod->ReplaceReference(0,remap.CloneRef(GetReference(0)));
   mod->ReplaceReference(1,remap.CloneRef(GetReference(1)));
   BaseClone(this, mod, remap);
   return mod;
}


int bhkRigidBodyModifier::NumRefs()
{
   return 2;
}

RefTargetHandle bhkRigidBodyModifier::GetReference(int i)
{
   if (i==0) return pblock;
   if (i==1) return GetRBBlock();
   return NULL;
}

void bhkRigidBodyModifier::SetReference(int i, RefTargetHandle rtarg)
{
   if (i==0) pblock = (IParamBlock2*)rtarg;
}

RefResult bhkRigidBodyModifier::NotifyRefChanged(
   Interval changeInt, 
   RefTargetHandle hTarget, 
   PartID& partID, 
   RefMessage message)
{
   switch (message) 
   {
   case REFMSG_CHANGE:
      ParamID changing_param = pblock->LastNotifyParamID();
      havok_param_blk.InvalidateUI(changing_param);
      if (changing_param == PB_MESHLIST)
      {
         if (partID == PART_TOPO) {
            NotifyDependents(FOREVER,GEOM_CHANNEL,REFMSG_CHANGE);
         }
      }
      break;
   }
   return REF_SUCCEED;
}

Interval bhkRigidBodyModifier::GetValidity (TimeValue t) {
   Interval ret = FOREVER;
   pblock->GetValidity (t, ret);
   return ret;
}

void bhkRigidBodyModifier::ModifyObject (TimeValue t, ModContext &mc, ObjectState *os, INode *inode) 
{
OutputDebugString("bhkRigidBodyModifier::ModifyObject\n");

	Box3 box; box.Init();
   if (TriObject *tri = (TriObject *)os->obj->ConvertToType(t, Class_ID(TRIOBJ_CLASS_ID, 0)))
   {
      CalcAxisAlignedBox(tri->GetMesh(), box);
      BuildBox(mesh, box.Max().x-box.Min().x, box.Max().y-box.Min().y, box.Max().z-box.Min().z);
   }
   //Modifier::ModifyObject(t, mc, os, inode);
}
void bhkRigidBodyModifier::NotifyInputChanged(Interval changeInt, PartID partID, RefMessage message, ModContext *mc)
{
	OutputDebugString("bhkRigidBodyModifier::NotifyInputChanged\n");
   Modifier::NotifyInputChanged(changeInt, partID, message, mc);

}

struct ParamMapInfo
{
	MapID mapid;
	DWORD idd;
	DWORD ids;
};
static ParamMapInfo pminfo[] = {
	{bv_box,		IDD_RB_MOD_PANEL1,	IDS_RB_MOD_PANEL1},
	{bv_sphere,	IDD_RB_MOD_PANEL2,	IDS_RB_MOD_PANEL2},
	{bv_capsule,IDD_RB_MOD_PANEL3,	IDS_RB_MOD_PANEL3},
	{bv_mesh,	IDD_RB_MOD_PANEL4,	IDS_RB_MOD_PANEL4},
};

void bhkRigidBodyModifier::UpdateBVDialogs()
{
	//if (NULL != pmapParam)
	//{
	//	int bvType = pblock2->GetInt(PB_BOUND_TYPE);
	//	if (bvType > 0) // none
	//	{
	//		int adjBvType = bvType - 1; //adjusted bvtype (ignoring none).
	//		for (int i=0; i<_countof(pminfo), ++i){
	//			if (pbvParams[i] != NULL && adjBvType != i)  {
	//				DestroyCPParamMap2( pbvParams[i] );
	//				pbvParams[i] = 0;
	//			}
	//		}
	//		Interface *ifc = GetCOREInterface();
	//		if (pbvParams[adjBvType] == NULL) {
	//			pbvParams[adjBvType] = CreateCPParamMap2( pminfo[adjBvType], pblock2, ifc, hInstance, 
	//				MAKEINTRESOURCE(pminfo[adjBvType]), GetString([adjBvType]), 0);
	//		}
	//	}
	//}
	//else
	//{
	//	// hide em all
	//	for (int i=0; i<_countof(pminfo), ++i){
	//		if (pbvParams[i] != NULL)  {
	//			DestroyCPParamMap2( pbvParams[i] );
	//			pbvParams[i] = 0;
	//		}
	//	}
	//}
}

void bhkRigidBodyModifier::BeginEditParams(IObjParam  *ip, ULONG flags,Animatable *prev)
{
   Modifier::BeginEditParams(ip, flags, prev);
	mIP = ip;

   TimeValue t = ip->GetTime();
   NotifyDependents(Interval(t,t), PART_ALL, REFMSG_BEGIN_EDIT);
   NotifyDependents(Interval(t,t), PART_ALL, REFMSG_MOD_DISPLAY_ON);
   SetAFlag(A_MOD_BEING_EDITED);
   
   bhkRigidBodyModifierDesc.BeginEditParams(ip,this,flags,prev);
   //havok_param_blk.SetUserDlgProc(bv_mesh, new bhkRigidBodyModifierDlgProc(this));

	pmapParam = pblock->GetMap(havok_params);
	UpdateBVDialogs();
	//pmapParam->GetIRollup()->Hide(1);
	//pmapParam->GetIRollup()->Hide(3);

   BeginEditRBParams(ip, flags, prev);

   mNodes.ZeroCount();
}
	
void bhkRigidBodyModifier::EndEditParams (IObjParam *ip,ULONG flags,Animatable *next)
{
   Modifier::EndEditParams(ip,flags,next);
   //saveState();
   mNodes.ZeroCount();
	mIP = NULL;

   // NOTE: This flag must be cleared before sending the REFMSG_END_EDIT
   ClearAFlag(A_MOD_BEING_EDITED);
   TimeValue t = ip->GetTime();
   NotifyDependents(Interval(t,t), PART_ALL, REFMSG_END_EDIT);
   NotifyDependents(Interval(t,t), PART_ALL, REFMSG_MOD_DISPLAY_OFF);

   havok_param_blk.SetUserDlgProc();

   if (flags&END_EDIT_REMOVEUI) {
		pmapParam = NULL;
		UpdateBVDialogs();
	}

   if (iPickButton != NULL) {
      ReleaseICustButton(iPickButton);
      iPickButton = NULL;
   }

   // For PB2 we ask the ClassDesc2 to take care of the EndEditParams - NH
   bhkRigidBodyModifierDesc.EndEditParams(ip,this,flags,next);
   EndEditRBParams(ip, flags, next);
}

class CollisionWireMtl: public Material 
{
public:
   CollisionWireMtl();
};
static CollisionWireMtl swMtl;
CollisionWireMtl::CollisionWireMtl():Material() 
{	
   Ks[0] = Kd[0] = 1.0f;
   Ks[1] = Kd[1] = 0.0f;
   Ks[2] = Kd[2] = 0.0f;
   shininess = (float)0.0;
   shadeLimit = GW_WIREFRAME|GW_BACKCULL;
   selfIllum = (float)1.0;
}

int bhkRigidBodyModifier::Display(TimeValue t, INode* inode, ViewExp *vpt, int flagst, ModContext *mc)
{
   //OutputDebugString("bhkRigidBodyModifier::Display\n");
   if (!Modifier::IsEnabled())
      return 0;

	Matrix3 m;
	GraphicsWindow *gw = vpt->getGW();

   int wrgb = inode->GetWireColor();
   inode->SetWireColor( RGB(255,0,0) );

   Material *mtl = &swMtl;
   //gw->setTransform(mat);
   if (inode->Selected()) 
      gw->setColor( LINE_COLOR, GetSelColor());
   else if(!inode->IsFrozen())
      gw->setColor(LINE_COLOR,GetUIColor(COLOR_SPACE_WARPS));

	m = inode->GetObjectTM(t);
	gw->setTransform(m);
	DWORD rlim = gw->getRndLimits();

   DWORD newrlim = GW_WIREFRAME/*|GW_Z_BUFFER*/;
#if VERSION_3DSMAX >= ((5000<<16)+(15<<8)+0) // Version 5+
   newrlim |= GW_EDGES_ONLY;
#endif
   gw->setRndLimits(newrlim);

	//UpdateMesh(t);
   gw->setMaterial(swMtl);
   gw->setColor(LINE_COLOR, 1.0f, 0.0f, 0.0f);
   gw->setColor(FILL_COLOR, 1.0f, 0.0f, 0.0f);
	mesh.render( gw, mtl, NULL, COMP_ALL);	

   inode->SetWireColor(wrgb);
	gw->setRndLimits(rlim);

	return 0;
}