/**********************************************************************
*<
FILE: bhkRigidBodyInterface.cpp

DESCRIPTION:	Collision RigidBody Object Implementation

CREATED BY: tazpn (Theo)

HISTORY: 
  V1.0 - Derived from 3ds max prim sphere example

*>	Copyright (c) 2006, All Rights Reserved.
**********************************************************************/
#pragma warning( disable:4800 )
#include <max.h>
#include "MAX_Mem.h"
#include <map>
#include "NifProps.h"
#include "iparamm.h"
#include "Simpobj.h"
#include "notify.h"
#include "macroRec.h"
#include "bhkRigidBodyInterface.h"
#include "NifPlugins.h"
#include "NifGui.h"
#include "NifStrings.h"
//--- Parameter map/block descriptors -------------------------------


FPInterfaceDesc thebhkRigidBodyInterface(
   BHKRIGIDBODYINTERFACE_DESC, _T("bhkRigidBody"), -1/*IDS_OPS*/, 0, FP_CORE,
   properties,
      bhkRigidBodyInterface::get_material, bhkRigidBodyInterface::set_material, _T("Material"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_material,
      bhkRigidBodyInterface::get_layer, bhkRigidBodyInterface::set_layer, _T("Layer"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_layer,
      bhkRigidBodyInterface::get_mass, bhkRigidBodyInterface::set_mass, _T("Mass"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_friction, bhkRigidBodyInterface::set_friction, _T("Friction"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_restitution, bhkRigidBodyInterface::set_restitution, _T("Restitution"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_lineardamping, bhkRigidBodyInterface::set_lineardamping, _T("LinearDamping"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_angulardamping, bhkRigidBodyInterface::set_angulardamping, _T("AngularDamping"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_maxlinearvelocity, bhkRigidBodyInterface::set_maxlinearvelocity, _T("MaxLinearVelocity"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_maxangularvelocity, bhkRigidBodyInterface::set_maxangularvelocity, _T("MaxAngularVelocity"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_penetrationdepth, bhkRigidBodyInterface::get_penetrationdepth, _T("PenetrationDepth"), 0, TYPE_FLOAT,
      bhkRigidBodyInterface::get_motionsystem, bhkRigidBodyInterface::set_motionsystem, _T("MotionSystem"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_motionsystem,
      bhkRigidBodyInterface::get_qualitytype, bhkRigidBodyInterface::set_qualitytype, _T("QualityType"), 0, TYPE_ENUM, bhkRigidBodyInterface::enum_qualitytype,
   enums,
      bhkRigidBodyInterface::enum_material, 31,
         "Stone", 0,
         "Cloth", 1,
         "Dirt", 2,
         "Glass", 3,
         "Grass", 4,
         "Metal", 5,
         "Organic", 6,
         "Skin", 7,
         "Water", 8,
         "Wood", 9,
         "Heavy Stone", 10, 
         "Heavy Metal", 11,
         "Heavy Wood", 12,
         "Chain", 13,
         "Snow", 14,
         "Stone Stairs", 15,
         "Cloth Stairs", 16,
         "Dirt Stairs", 17,
         "Glass Stairs", 18,
         "Grass Stairs", 19,
         "Metal Stairs", 20,
         "Organic Stairs", 21,
         "Skin Stairs", 22,
         "Water Stairs", 23,
         "Wood Stairs", 24,
         "Heavy Stone Stairs", 25,
         "Heavy Metal Stairs", 26,
         "Heavy Wood Stairs", 27,
         "Chain Stairs", 28,
         "Snow Stairs", 29,
         "Elevator", 30,
      bhkRigidBodyInterface::enum_layer, 57,
         "Unidentified", 0,
         "Static", 1,
         "AnimStatic", 2,
         "Transparent", 3,
         "Clutter", 4,
         "Weapon", 5,
         "Projectile", 6,
         "Spell", 7,
         "Biped", 8,
         "Tree", 9,
         "Prop", 10,
         "Water", 11,
         "Trigger", 12,
         "Terrain", 13,
         "Trap", 14,
         "NonCollidable", 15,
         "CloudTrap", 16,
         "Ground", 17,
         "Portal", 18,
         "Stairs", 19,
         "CharController", 20,
         "AvoidBox", 21,
         "Unknown(22)", 22,
         "Unknown(23)", 23,
         "CameraPick", 24,
         "ItemPick", 25,
         "LineOfSight", 26,
         "PathPick", 27,
         "CustomPick1", 28,
         "CustomPick2", 29,
         "SpellExplosion", 30,
         "DroppingPick", 31,
         "Other", 32,
         "Head", 33,
         "Body", 34,
         "Spine1", 35,
         "Spine2", 36,
         "LUpperArm", 37,
         "LForeArm", 38,
         "LHand", 39,
         "LThigh", 40,
         "LCalf", 41,
         "LFoot", 42,
         "RUpperArm", 43,
         "RForeArm", 44,
         "RHand", 45,
         "RThigh", 46,
         "RCalf", 47,
         "RFoot", 48,
         "Tail", 49,
         "SideWeapon", 50,
         "Shield", 51,
         "Quiver", 52,
         "BackWeapon", 53,
         "BackWeapon", 54,
         "PonyTail", 55,
         "Wing", 56,
      bhkRigidBodyInterface::enum_motionsystem, 10,
         "Keyframed(0)", 0,
         "Box(1)", 1,
         "Sphere(2)", 2,
         "Sphere(2)", 3,
         "Box(4)", 4,
         "Box(5)", 5,
         "Keyframed(6)", 6,
         "Keyframed(7)", 7,
         "Box(8)", 8,
         "Keyframed(9)", 9,
      bhkRigidBodyInterface::enum_qualitytype, 9,
         "Moving", 0,
         "Fixed", 1,
         "Keyframed", 2,
         "Moving(3)", 3,
         "Moving(4)", 4,
         "Critical", 5,
         "Bullet", 6,
         "User", 7,
         "Null", 8,
   end);

FPInterfaceDesc *bhkRigidBodyInterface::GetDesc() {
   return &thebhkRigidBodyInterface;
}

FPInterfaceDesc* GetbhkRigidBodyInterfaceDesc()
{
   return &thebhkRigidBodyInterface;
}

//
//
// Parameters

static ParamUIDesc descRigidBodyParam[] = {

   // Mass
   ParamUIDesc(
   PB_RB_MASS,
   EDITTYPE_UNIVERSE,
   IDC_ED_MASS,IDC_SP_MASS,
   0, 1000,
   SPIN_AUTOSCALE),	

   // Friction
   ParamUIDesc(
   PB_RB_FRICTION,
   EDITTYPE_UNIVERSE,
   IDC_ED_FRICTION,IDC_SP_FRICTION,
   0, 10,
   SPIN_AUTOSCALE),

   // Restitution
   ParamUIDesc(
   PB_RB_RESTITUTION,
   EDITTYPE_UNIVERSE,
   IDC_ED_RESTITUTION,IDC_SP_RESTITUTION,
   0, 10,
   SPIN_AUTOSCALE),

   // Linear Damping
   ParamUIDesc(
   PB_RB_LINEAR_DAMPING,
   EDITTYPE_UNIVERSE,
   IDC_ED_LINEAR_DAMPING,IDC_SP_LINEAR_DAMPING,
   0, 10,
   SPIN_AUTOSCALE),

   // Angular Damping
   ParamUIDesc(
   PB_RB_ANGULAR_DAMPING,
   EDITTYPE_UNIVERSE,
   IDC_ED_ANGULAR_DAMPING,IDC_SP_ANGULAR_DAMPING,
   0, 10,
   SPIN_AUTOSCALE),

   // Max Linear Velocity
   ParamUIDesc(
   PB_RB_MAX_LINEAR_VELOCITY,
   EDITTYPE_UNIVERSE,
   IDC_ED_MAX_LINEAR_VELOCITY,IDC_SP_MAX_LINEAR_VELOCITY,
   0, 10,
   SPIN_AUTOSCALE),

   // Max Angular Velocity
   ParamUIDesc(
   PB_RB_MAX_ANGULAR_VELOCITY,
   EDITTYPE_UNIVERSE,
   IDC_ED_MAX_ANGULAR_VELOCITY,IDC_SP_MAX_ANGULAR_VELOCITY,
   0, 10,
   SPIN_AUTOSCALE),

   // Penetration Depth
   ParamUIDesc(
   PB_RB_PENETRATION_DEPTH,
   EDITTYPE_UNIVERSE,
   IDC_ED_PENETRATION_DEPTH,IDC_SP_PENETRATION_DEPTH,
   0, 10,
   SPIN_AUTOSCALE),
};
const int descRigidBodyParamLength = _countof(descRigidBodyParam);

static ParamBlockDescID gRigidBlockParamDesc[] = {
   { TYPE_INT, NULL, FALSE, PB_RB_MATERIAL },
   { TYPE_INT, NULL, FALSE, PB_RB_LAYER },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_MASS },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_FRICTION },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_RESTITUTION },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_LINEAR_DAMPING },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_ANGULAR_DAMPING },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_MAX_LINEAR_VELOCITY },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_MAX_ANGULAR_VELOCITY },
   { TYPE_FLOAT, NULL, FALSE, PB_RB_PENETRATION_DEPTH },
   { TYPE_INT, NULL, FALSE, PB_RB_MOTION_SYSTEM },
   { TYPE_INT, NULL, FALSE, PB_RB_QUALITY_TYPE },
};
const int descRigidBodyDescIDLength = _countof(gRigidBlockParamDesc);

class RigidBodyParamDlgProc : public ParamMapUserDlgProc {
public:
   bhkRigidBodyInterface *so;
   HWND thishWnd;
   NpComboBox		mCbLayer;
   NpComboBox		mCbMaterial;
   NpComboBox		mCbMotionSystem;
   NpComboBox		mCbQualityType;

   RigidBodyParamDlgProc(bhkRigidBodyInterface *s) {so=s;thishWnd=NULL;}
   BOOL DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam);
   void Update(TimeValue t);
   void DeleteThis() {delete this;}

   //--- ParamDlgProc --------------------------------
   void TurnSpinner(HWND hWnd,int SpinNum,BOOL ison)
   {	
      ISpinnerControl *spin2 = GetISpinner(GetDlgItem(hWnd,SpinNum));
      if (ison) spin2->Enable();else spin2->Disable();
      ReleaseISpinner(spin2);
   };

};

void RigidBodyParamDlgProc::Update(TimeValue t)
{
   if (!thishWnd) 
      return;

   
   mCbMaterial.select(max(0, min(so->GetMaterial(t), mCbMaterial.count()-1)));
   mCbLayer.select(max(0, min(so->GetLayer(t), mCbLayer.count()-1)));
   mCbMotionSystem.select(max(0, min(so->GetMotionSystem(t), mCbMotionSystem.count()-1)));
   mCbQualityType.select(max(0, min(so->GetQualityType(t), mCbQualityType.count()-1)));
   return;
}

BOOL RigidBodyParamDlgProc::DlgProc(TimeValue t,IParamMap *map,HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
   thishWnd=hWnd;
   switch (msg) 
   {
   case WM_INITDIALOG: 
      {
         mCbMaterial.init(GetDlgItem(hWnd, IDC_CB_MATERIAL));
         for (const char **str = NpHvkMaterialNames; *str; ++str)
            mCbMaterial.add(*str);

         mCbLayer.init(GetDlgItem(hWnd, IDC_CB_LAYER));
         for (const char **str = NpHvkLayerNames; *str; ++str)
            mCbLayer.add(*str);

         mCbMotionSystem.init(GetDlgItem(hWnd, IDC_CB_MOTION_SYSTEM));         
         for (const char **str = NpHvkMotionSystems; *str; ++str)
            mCbMotionSystem.add(*str);

         mCbQualityType.init(GetDlgItem(hWnd, IDC_CB_QUALITY_TYPE));
         for (const char **str = NpHvkQualityTypes; *str; ++str)
            mCbQualityType.add(*str);

         Update(t);
         break;
      }
   case WM_COMMAND:
      switch (LOWORD(wParam))
      {
      case IDC_CB_MATERIAL:
         if (HIWORD(wParam)==CBN_SELCHANGE) {
            so->SetMaterial( mCbMaterial.selection(), t );
         }
         break;

      case IDC_CB_LAYER:
         if (HIWORD(wParam)==CBN_SELCHANGE) {
            so->SetLayer( mCbLayer.selection(), t );
         }
         break;

      case IDC_CB_MOTION_SYSTEM:
         if (HIWORD(wParam)==CBN_SELCHANGE) {
            so->SetMotionSystem( mCbMotionSystem.selection(), t );
         }
         break;

      case IDC_CB_QUALITY_TYPE:
         if (HIWORD(wParam)==CBN_SELCHANGE) {
            so->SetQualityType( mCbQualityType.selection(), t );
         }
         break;
      }
      break;
   }
   return FALSE;
}

//--- RigidBody methods -------------------------------

IParamMap *bhkRigidBodyIfcHelper::rbpmapParam = NULL;
IObjParam *bhkRigidBodyIfcHelper::rbip = NULL;

bhkRigidBodyIfcHelper::bhkRigidBodyIfcHelper()
{
   rbpblock = CreateParameterBlock(gRigidBlockParamDesc, descRigidBodyDescIDLength, 1);
   rbpblock->SetValue(PB_RB_MATERIAL,0,NP_DEFAULT_HVK_MATERIAL);
   rbpblock->SetValue(PB_RB_LAYER,0,NP_DEFAULT_HVK_LAYER);
   rbpblock->SetValue(PB_RB_MASS,0,NP_DEFAULT_HVK_MASS);
   rbpblock->SetValue(PB_RB_FRICTION,0,NP_DEFAULT_HVK_FRICTION);
   rbpblock->SetValue(PB_RB_RESTITUTION,0,NP_DEFAULT_HVK_RESTITUTION);
   rbpblock->SetValue(PB_RB_LINEAR_DAMPING,0,NP_DEFAULT_HVK_LINEAR_DAMPING);
   rbpblock->SetValue(PB_RB_ANGULAR_DAMPING,0,NP_DEFAULT_HVK_ANGULAR_DAMPING);
   rbpblock->SetValue(PB_RB_MAX_LINEAR_VELOCITY,0,NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY);
   rbpblock->SetValue(PB_RB_MAX_ANGULAR_VELOCITY,0,NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY);
   rbpblock->SetValue(PB_RB_PENETRATION_DEPTH,0,NP_DEFAULT_HVK_PENETRATION_DEPTH);
   rbpblock->SetValue(PB_RB_MOTION_SYSTEM,0,NP_DEFAULT_HVK_MOTION_SYSTEM);
   rbpblock->SetValue(PB_RB_QUALITY_TYPE,0,NP_DEFAULT_HVK_QUALITY_TYPE);

}

bhkRigidBodyIfcHelper::~bhkRigidBodyIfcHelper()
{
   if (rbpmapParam) {
      rbpmapParam->SetUserDlgProc(NULL);
      DestroyCPParamMap(rbpmapParam);
      rbpmapParam  = NULL;
   }
}

IParamBlock* bhkRigidBodyIfcHelper::GetRBBlock()
{
   return rbpblock;
}

void bhkRigidBodyIfcHelper::BeginEditRBParams(IObjParam *ip,ULONG flags,Animatable *prev)
{
   // Gotta make a new one.
   if (NULL == rbpmapParam) 
   {
      rbpmapParam = CreateCPParamMap(
         descRigidBodyParam,descRigidBodyParamLength,
         rbpblock,
         ip,
         hInstance,
         MAKEINTRESOURCE(IDD_RigidBody),
         GetString(IDS_RB_RIGIDBODY_PARAM),
         0);
   }
   this->rbip = ip;

   if(rbpmapParam) {
      // A callback for the type in.
      rbpmapParam->SetUserDlgProc(new RigidBodyParamDlgProc(this));
   }
}

void bhkRigidBodyIfcHelper::EndEditRBParams( IObjParam *ip, ULONG flags,Animatable *next )
{
   if (rbpmapParam && flags&END_EDIT_REMOVEUI ) {
      rbpmapParam->SetUserDlgProc(NULL);
      DestroyCPParamMap(rbpmapParam);
      rbpmapParam = NULL;
   }
}


void bhkRigidBodyIfcHelper::SetMaterial(int value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_MATERIAL,time,value);
}

int bhkRigidBodyIfcHelper::GetMaterial(TimeValue time, Interval& valid) const
{
   int value = NP_DEFAULT_HVK_MATERIAL;
   rbpblock->GetValue(PB_RB_MATERIAL,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetLayer(int value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_LAYER,time,value);
}
int bhkRigidBodyIfcHelper::GetLayer(TimeValue time, Interval& valid) const 
{
   int value = NP_DEFAULT_HVK_LAYER;
   rbpblock->GetValue(PB_RB_LAYER,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetMass(float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_MASS,time,value);
}
float bhkRigidBodyIfcHelper::GetMass(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_MASS;
   rbpblock->GetValue(PB_RB_MASS,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetFriction(float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_FRICTION,time,value);
}

float bhkRigidBodyIfcHelper::GetFriction(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_FRICTION;
   rbpblock->GetValue(PB_RB_FRICTION,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetRestitution (float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_RESTITUTION,time,value);
}

float bhkRigidBodyIfcHelper::GetRestitution (TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_RESTITUTION;
   rbpblock->GetValue(PB_RB_RESTITUTION,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetLinearDamping(float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_LINEAR_DAMPING,time,value);
}

float bhkRigidBodyIfcHelper::GetLinearDamping(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_LINEAR_DAMPING;
   rbpblock->GetValue(PB_RB_LINEAR_DAMPING,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetAngularDamping(float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_ANGULAR_DAMPING,time,value);
}

float bhkRigidBodyIfcHelper::GetAngularDamping(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_ANGULAR_DAMPING;
   rbpblock->GetValue(PB_RB_ANGULAR_DAMPING,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetMaxLinearVelocity(float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_MAX_LINEAR_VELOCITY,time,value);
}

float bhkRigidBodyIfcHelper::GetMaxLinearVelocity(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_MAX_LINEAR_VELOCITY;
   rbpblock->GetValue(PB_RB_MAX_LINEAR_VELOCITY,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetMaxAngularVelocity(float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_MAX_ANGULAR_VELOCITY,time,value);
}
float bhkRigidBodyIfcHelper::GetMaxAngularVelocity(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_MAX_ANGULAR_VELOCITY;
   rbpblock->GetValue(PB_RB_MAX_ANGULAR_VELOCITY,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetPenetrationDepth(float value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_PENETRATION_DEPTH,time,value);
}
float bhkRigidBodyIfcHelper::GetPenetrationDepth(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_PENETRATION_DEPTH;
   rbpblock->GetValue(PB_RB_PENETRATION_DEPTH,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetMotionSystem(int value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_MOTION_SYSTEM,time,value);
}
int bhkRigidBodyIfcHelper::GetMotionSystem(TimeValue time, Interval& valid) const 
{
   int value = NP_DEFAULT_HVK_MOTION_SYSTEM;
   rbpblock->GetValue(PB_RB_MOTION_SYSTEM,time,value,valid);
   return value;
}

void bhkRigidBodyIfcHelper::SetQualityType(int value, TimeValue time)
{
   rbpblock->SetValue(PB_RB_QUALITY_TYPE,time,value);
}
int bhkRigidBodyIfcHelper::GetQualityType(TimeValue time, Interval& valid) const 
{
   float value = NP_DEFAULT_HVK_QUALITY_TYPE;
   rbpblock->GetValue(PB_RB_QUALITY_TYPE,time,value,valid);
   return value;
}