#ifndef CC_PHYSICS_H
#define CC_PHYSICS_H


		#include "BulletCollision/CollisionDispatch/btGhostObject.h"
		#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
		#include "BulletCollision/CollisionShapes/btCapsuleShape.h"
		#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
		#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
		#include "BulletCollision/CollisionDispatch/btCollisionWorld.h"
		#include "LinearMath/btDefaultMotionState.h"
		#include "BulletDynamics/Character/btCharacterControllerInterface.h"


//
class	btCustomCharacterController : public btCharacterControllerInterface
{
	btScalar	mHalfHeight;

	btPairCachingGhostObject *mGhostObject;
	btConvexShape	*mConvexShape;
	btConvexShape	*mStandingConvexShape;
	btConvexShape	*mDuckingConvexShape;

	btCollisionWorld *mCollisionWorld;

	btVector3	mStepVelocity;

	btScalar	mVerticalVelocity;
	btScalar	mVerticalOffset;
	btScalar	mFallSpeed;
	btScalar	mJumpSpeed;
	btScalar	mMaxJumpHeight;
	btScalar	mMaxSlopeRadians;
	btScalar	mMaxSlopeCosine;
	btScalar	mGravity;

	btScalar	mTurnAngle;

	btScalar	mStepHeight;

	btScalar	mAddedMargin;

	btVector3	mWalkDirection;
	btVector3	mNormalizedDirection;

	btVector3	mCurrentPosition;

	btManifoldArray mManifoldArray;

	bool		mGroundContact;
	btVector3	mGroundNormal;

	bool		mTouchingContact;

bool dbg_step_high;
bool dbg_down_sweep_hit;
void performStep(btScalar dt);
bool SweepAndSlide(btVector3 &from, btVector3 &to, int);

	bool		mUseWalkDirection;
	btScalar	mVelocityTimeInterval;

	int			mUpAxis;

	btVector3	mLinearVelocity;
	btScalar	mMass;

	class	ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback
	{
		btCollisionObject *mMe;

	public:

		ClosestNotMeRayResultCallback(btCollisionObject * me) : btCollisionWorld::ClosestRayResultCallback(btVector3(0, 0, 0), btVector3(0, 0, 0)), mMe(me) {}

		btScalar addSingleResult(btCollisionWorld::LocalRayResult &rayResult, bool normalInWorldSpace)
		{
			if	(rayResult.m_collisionObject == mMe)
				return 1.0;
			return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
		}
	};

	class	ClosestNotMeConvexResultCallback : public btCollisionWorld::ClosestConvexResultCallback
	{
		btCollisionObject *mMe;
		const btVector3	mUp;
		btScalar		mMinSlopeDot;

	public:

		ClosestNotMeConvexResultCallback(btCollisionObject *me, const btVector3 &up, btScalar minSlopeDot) : btCollisionWorld::ClosestConvexResultCallback(btVector3(0, 0, 0), btVector3(0, 0, 0)), mMe(me), mUp(up), mMinSlopeDot(minSlopeDot) {}

		btScalar addSingleResult(btCollisionWorld::LocalConvexResult &convexResult, bool normalInWorldSpace)
		{
			if	(convexResult.m_hitCollisionObject == mMe)
				return 1.0;

			btVector3 hitNormalWorld;
			if	(normalInWorldSpace)
				hitNormalWorld = convexResult.m_hitNormalLocal;
			else
				hitNormalWorld = convexResult.m_hitCollisionObject->getWorldTransform().getBasis() * convexResult.m_hitNormalLocal;

			btScalar dotUp = mUp.dot(hitNormalWorld);
			if	(dotUp < mMinSlopeDot)
				return 1.0;

			return btCollisionWorld::ClosestConvexResultCallback::addSingleResult(convexResult, normalInWorldSpace);
		}
	};

	static btVector3 *getUpAxisDirections()
	{
		static btVector3 sUpAxisDirection[3] = { btVector3(1, 0, 0), btVector3(0, 1, 0), btVector3(0, 0, 1) };
		return sUpAxisDirection;
	}

	static btVector3 getNormalizedVector(const btVector3& v)
	{
		btVector3 n = v.normalized();

		if (n.length() < SIMD_EPSILON)
			n.setValue(0, 0, 0);

		return n;
	}

	bool		SweepTest(const btVector3 &, const btVector3 &, btScalar &fraction, btVector3 *normal = 0, btVector3 *hit = 0);

	btVector3	computeReflectionDirection(const btVector3 & direction, const btVector3 & normal);
	void		setPlayerMode();

public:

	btCustomCharacterController(btPairCachingGhostObject *ghostObject, btConvexShape *convexShape, btScalar stepHeight, btCollisionWorld *collisionWorld, int upAxis = 1);

	btVector3	getUpAxisDirection() const { return getUpAxisDirections()[mUpAxis]; }

	void		setDuckingConvexShape(btConvexShape * shape);
	bool		recoverFromPenetration(const btVector3 &step_direction);
	void		stepUp(btCollisionWorld * collisionWorld);
	void		setRBForceImpulseBasedOnCollision();
	void		updateTargetPositionBasedOnCollision(const btVector3 & hitNormal, btScalar tangentMag = 0, btScalar normalMag = 1);
	void		stepForwardAndStrafe(btCollisionWorld * collisionWorld, const btVector3 & walkMove);
	void		stepDown(btCollisionWorld * collisionWorld, btScalar dt);
	void		setVelocityForTimeInterval(const btVector3 & velocity, btScalar timeInterval);

	void		reset() {}

	void		warp(const btVector3 & origin);
	void		preStep(btCollisionWorld * collisionWorld);
	void		playerStep(btCollisionWorld * collisionWorld, btScalar dt);

	void		setFallSpeed(btScalar fallSpeed);
	void		setJumpSpeed(btScalar jumpSpeed);
	void		setMaxJumpHeight(btScalar maxJumpHeight);

	bool		canJump() const;
	void		jump();

	void		duck();

	void		stand();
	bool		canStand();

	void		setGravity(const btScalar gravity);
	btScalar	getGravity() const;

	void		setMaxSlope(btScalar slopeRadians);
	btScalar	getMaxSlope() const;

	bool		onGround() const;

	void		setWalkDirection(const btVector3 & walkDirection);
	void		setWalkDirection(const btScalar x, const btScalar y, const btScalar z);
	void		setOrientation(const btQuaternion & orientation);

	btVector3	getWalkDirection() const;
	btVector3	getPosition() const;

	void		debugDraw(btIDebugDraw * debugDrawer);

	void updateAction(btCollisionWorld * collisionWorld, btScalar dt);
};


#endif // CC_PHYSICS_H
