#pragma once

#include "Mix/Dynamics/IHingeJoint.h"
#include "Mix/Private/Dynamics/Joint.h"

namespace Mix{ namespace Dynamics{

	class HingeJoint : public Mix::Dynamics::IHingeJoint, public Mix::Dynamics::Joint
	{
	private:
		enum PARAM_FLAGS
		{
			PF_LIMIT_SPRING = 0x00000001,
			PF_LIMIT_DAMPER = 0x00000002,
			PF_NORMAL_SPRING = 0x00000004,
		};

	public:
		static HingeJoint* CreateInstance(	Mix::Dynamics::IRigidBody* pRigidBodyA,
											const Mix::Vector3& pivotA,
											const Mix::Vector3& axis );

		static HingeJoint* CreateInstance(	Mix::Dynamics::IRigidBody* pRigidBodyA,
											Mix::Dynamics::IRigidBody* pRigidBodyB,
											const Mix::Vector3& pivotA,
											const Mix::Vector3& pivotB,
											const Mix::Vector3& axis );

	private:
		Mix::Dynamics::IRigidBody* m_pRigidBodyA;
		Mix::Dynamics::IRigidBody* m_pRigidBodyB;
		Mix::Vector3 m_PivotA;
		Mix::Vector3 m_PivotB;
		Mix::Vector3 m_Axis;

		//リミットを設定した際に角度の正規化( btNormalizeAngle )が行われ、
		//getLowerLimit getUpperLimit で返される値が変わる可能性があるので、
		//変数を用意して指定されている値を保存しておく
		Float32 m_LowerLimit;
		Float32 m_UpperLimit;

		btHingeConstraint* m_pObject;

		UInt32 m_ParamFlags;

	private:
		HingeJoint(	Mix::Dynamics::IRigidBody* pRigidBodyA,
							const Mix::Vector3& pivotA,
							const Mix::Vector3& axis );

		HingeJoint(	Mix::Dynamics::IRigidBody* pRigidBodyA,
							Mix::Dynamics::IRigidBody* pRigidBodyB,
							const Mix::Vector3& pivotA,
							const Mix::Vector3& pivotB,
							const Mix::Vector3& axis );

		virtual ~HingeJoint( void );

		void UpdatePivots( void );
		void UpdateFrames( void );

		void Bullet_ComputeFrames(	const btRigidBody* rbA,
									const btVector3& pivotA,
									const btVector3& axis,
									btTransform& frameA,
									btTransform& frameB );

		void Bullet_ComputeFrames(	const btRigidBody* rbA,
									const btRigidBody* rbB,
									const btVector3& pivotA,
									const btVector3& pivotB,
									const btVector3& axis,
									btTransform& frameA,
									btTransform& frameB );

	public:
		Boolean Initialize( const wchar_t* pDebugName );

	public:
		virtual Mix::Dynamics::IJoint::TYPE GetType( void ) const;

		virtual Boolean IsInWorld( void ) const;

		virtual Boolean IsEnabled( void ) const;
		virtual void SetEnabled( Boolean state );

		virtual Float32 GetBreakingImpulseThreshold( void ) const;
		virtual void SetBreakingImpulseThreshold( Float32 threshold );

		virtual Boolean IsSingle( void ) const;

		virtual void GetRigidBodyA( Mix::Dynamics::IRigidBody** ppRigidBody );
		virtual void GetRigidBodyB( Mix::Dynamics::IRigidBody** ppRigidBody );

		virtual const Mix::Vector3& GetPivotA( void ) const;
		virtual void SetPivotA( const Mix::Vector3& pivot );

		virtual const Mix::Vector3& GetPivotB( void ) const;
		virtual void SetPivotB( const Mix::Vector3& pivot );

	public:
		virtual UInt32 Debug_GetDrawFlags( void ) const;
		virtual void Debug_SetDrawFlags( UInt32 flags );

		virtual Float32 Debug_GetDrawFrameMinSize( void ) const;
		virtual void Debug_SetDrawFrameMinSize( Float32 minSize );

		virtual Float32 Debug_GetDrawLimitScaling( void ) const;
		virtual void Debug_SetDrawLimitScaling( Float32 scaling );

		virtual void Debug_Draw( Mix::Graphics::Utility::IPerspectiveRenderer* pPerspectiveRenderer, Float32 opacity );

	public:
		virtual const Mix::Vector3& GetAxis( void ) const;
		virtual void SetAxis( const Mix::Vector3& axis );

		virtual Float32 GetLowerLimit( void ) const;
		virtual void SetLowerLimit( Float32 limit );

		virtual Float32 GetUpperLimit( void ) const;
		virtual void SetUpperLimit( Float32 limit );

		virtual Boolean GetMotorEnabled( void ) const;
		virtual void SetMotorEnabled( Boolean state );
		virtual void SetMotorParams( Float32 velocity, Float32 maxImpulse );
		virtual void SetMotorParams( Float32 angle, Float32 dt, Float32 maxImpulse );
		virtual void SetMotorParams( const Mix::Quaternion& ab, Float32 dt, Float32 maxImpulse );

		virtual Float32 GetLimitSpring( void ) const;
		virtual void SetLimitSpring( Float32 spring );

		virtual Float32 GetLimitDamper( void ) const;
		virtual void SetLimitDamper( Float32 damper );

		virtual Float32 GetNormalSpring( void ) const;
		virtual void SetNormalSpring( Float32 spring );

		virtual Float32 GetAngle( void ) const;

	public:
		virtual btTypedConstraint* Bullet_GetTypedConstraintPtr( void ) const;

	public:
		static const wchar_t* FAILED_CREATE;

		static const Float32 DEBUG_DRAW_STEP;
	};

}}
