//
// Lol Engine
//
// Copyright: (c) 2010-2012 Sam Hocevar <sam@hocevar.net>
//            (c) 2009-2012 Benjamin Huet <huet.benjamin@gmail.com>
//   This program is free software; you can redistribute it and/or
//   modify it under the terms of the Do What The Fuck You Want To
//   Public License, Version 2, as published by Sam Hocevar. See
//   http://sam.zoy.org/projects/COPYING.WTFPL for more details.
//

//
// The BulletCharacterController class
// ------------------
// This class is a equivalent of btKinematicCharacterController, but more useful for Lol.
//

#if !defined __BULLETCHARACTERCONTROLLER_BULLETCHARACTERCONTROLLER_H__
#define __BULLETCHARACTERCONTROLLER_BULLETCHARACTERCONTROLLER_H__

#ifdef HAVE_PHYS_USE_BULLET
#include "core.h"
#include "EasyPhysics.h"
//#include "BulletDynamics\Character\btCharacterControllerInterface.h"
#endif

namespace lol
{

	namespace phys
	{

#define 0
#ifdef HAVE_PHYS_USE_BULLET

		///BulletKinematicCharacterController is an object that supports a sliding motion in a world.
		///It uses a ghost object and convex sweep test to test for upcoming collisions. This is combined with discrete collision detection to recover from penetrations.
		///Interaction between btKinematicCharacterController and dynamic rigid bodies needs to be explicity implemented by the user.
		class BulletKinematicCharacterController : public btCharacterControllerInterface
		{
		protected:

			btScalar m_halfHeight;

			btPairCachingGhostObject* m_ghostObject;
			btConvexShape*	m_convexShape;//is also in m_ghostObject, but it needs to be convex, so we store it here to avoid upcast

			btScalar m_verticalVelocity;
			btScalar m_verticalOffset;
			btScalar m_fallSpeed;
			btScalar m_jumpSpeed;
			btScalar m_maxJumpHeight;
			btScalar m_maxSlopeRadians; // Slope angle that is set (used for returning the exact value)
			btScalar m_maxSlopeCosine;  // Cosine equivalent of m_maxSlopeRadians (calculated once when set, for optimization)
			btScalar m_gravity;

			btScalar m_turnAngle;

			btScalar m_stepHeight;

			btScalar	m_addedMargin;//@todo: remove this and fix the code

			///this is the desired walk direction, set by the user
			btVector3	m_walkDirection;
			btVector3	m_normalizedDirection;

			//some internal variables
			btVector3 m_currentPosition;
			btScalar  m_currentStepOffset;
			btVector3 m_targetPosition;

			///keep track of the contact manifolds
			btManifoldArray	m_manifoldArray;

			bool m_touchingContact;
			btVector3 m_touchingNormal;

			bool  m_wasOnGround;
			bool  m_wasJumping;
			bool	m_useGhostObjectSweepTest;
			bool	m_useWalkDirection;
			btScalar	m_velocityTimeInterval;
			int m_upAxis;

			static btVector3* getUpAxisDirections();

			btVector3 computeReflectionDirection (const btVector3& direction, const btVector3& normal);
			btVector3 parallelComponent (const btVector3& direction, const btVector3& normal);
			btVector3 perpindicularComponent (const btVector3& direction, const btVector3& normal);

			bool recoverFromPenetration ( btCollisionWorld* collisionWorld);
			void stepUp (btCollisionWorld* collisionWorld);
			void updateTargetPositionBasedOnCollision (const btVector3& hit_normal, btScalar tangentMag = btScalar(0.0), btScalar normalMag = btScalar(1.0));
			void stepForwardAndStrafe (btCollisionWorld* collisionWorld, const btVector3& walkMove);
			void stepDown (btCollisionWorld* collisionWorld, btScalar dt);
		public:
			BulletKinematicCharacterController (btPairCachingGhostObject* ghostObject,btConvexShape* convexShape,btScalar stepHeight, int upAxis = 1);
			~BulletKinematicCharacterController ();


			///btActionInterface interface
			virtual void updateAction( btCollisionWorld* collisionWorld,btScalar deltaTime)
			{
				preStep ( collisionWorld);
				playerStep (collisionWorld, deltaTime);
			}

			///btActionInterface interface
			void	debugDraw(btIDebugDraw* debugDrawer);

			void setUpAxis (int axis)
			{
				if (axis < 0)
					axis = 0;
				if (axis > 2)
					axis = 2;
				m_upAxis = axis;
			}

			/// This should probably be called setPositionIncrementPerSimulatorStep.
			/// This is neither a direction nor a velocity, but the amount to
			///	increment the position each simulation iteration, regardless
			///	of dt.
			/// This call will reset any velocity set by setVelocityForTimeInterval().
			virtual void	setWalkDirection(const btVector3& walkDirection);

			/// Caller provides a velocity with which the character should move for
			///	the given time period.  After the time period, velocity is reset
			///	to zero.
			/// This call will reset any walk direction set by setWalkDirection().
			/// Negative time intervals will result in no motion.
			virtual 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 setGravity(btScalar gravity);
			btScalar getGravity() const;

			/// The max slope determines the maximum angle that the controller can walk up.
			/// The slope angle is measured in radians.
			void setMaxSlope(btScalar slopeRadians);
			btScalar getMaxSlope() const;

			btPairCachingGhostObject* getGhostObject();
			void	setUseGhostSweepTest(bool useGhostObjectSweepTest)
			{
				m_useGhostObjectSweepTest = useGhostObjectSweepTest;
			}

			bool onGround () const;
		};

#endif // HAVE_PHYS_USE_BULLET
#endif // 0

	} /* namespace phys */

} /* namespace lol */

#endif /* __BULLETCHARACTERCONTROLLER_BULLETCHARACTERCONTROLLER_H__ */