//
// Asura_Physics_Object.h
//
// Created by KJFL - Monday, November 15, 2004
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
#pragma once
#endif

#ifndef __ASURA_PHYSICS_OBJECT_H__
#define __ASURA_PHYSICS_OBJECT_H__

#include "Core/Asura_Maths.h"
#include "Core/Asura_Handle.h"

// KJFL - 24 January 2007 - I'm in two minds whether to have this include or to
// use a forward declaration of the report class(es). Arguably you don't need
// the include to compile, but people will need to add the include themselves
// if they wish to use the reports, which means them looking into the Physics
// folder for the filename etc. I'd rather have one header to include (this one)
// to encourage the use of the one interface, rather than people trying to get
// things directly.
#include "Physics/Asura_Physics_CollisionReport.h"

class Asura_Collision_Object;
class Asura_Physics_RestFrame;

enum ASURA_PHYSICS_OBJECT_TYPE
{
	ASURA_PHYSICS_OBJECT_STATIC,
	ASURA_PHYSICS_OBJECT_ACTOR,
	ASURA_PHYSICS_OBJECT_DYNAMIC,
	ASURA_PHYSICS_OBJECT_EXTERNALCONTROL,

	NUM_ASURA_PHYSICS_OBJECT_TYPES
};

class Asura_Physics_Object;
class Asura_Physics_RigidBodyInstance;

template< typename T >
class Asura_MempoolFactory_Template;

typedef Asura_MempoolFactory_Template<Asura_Physics_Object> Asura_Physics_Object_Factory;


class Asura_Physics_Object
{
	public:

		// NY - 16 December 2005 - reset call to free up memory used by factory
		static void Reset( void );

		// create an instance
		static Asura_Physics_Object* Create( enum ASURA_PHYSICS_OBJECT_TYPE eType = ASURA_PHYSICS_OBJECT_STATIC );

		// destroy an instance
		static void Destroy( Asura_Physics_Object* pxObject );

        static void SetDefaultFlagsOfPolysToIgnoreCallback( u_short(*pfnCallback)(void) );

        // Get... and corresponding Set... methods

		// get the physics type
        ASURA_PHYSICS_OBJECT_TYPE GetType( void ) const { return m_eType; }

		// change the physics type (e.g. DYNAMIC to EXTERNALCONTROL)
		void ChangeType( enum ASURA_PHYSICS_OBJECT_TYPE eType );

	
		// Get the handle for the underlying rigid body 
		Asura_Handle GetHandle( void ) const
		{
			return m_iRigidBodyHandle;
		}

		// Position
		const Asura_Vector_3&		GetPosition() const;
		
		// SetPosition() moves an object to a new position.
		// This method is used to move External Control objects - the Physics
		// will assume the object moved from its previous position to the new
		// one and objects in its path need to be collided against.
		// Depending on the object type this method may flush any cached data
		// the object might have. E.g. if you SetPosition() on an Actor, it 
		// destroys any contact info for it to stop it being dragged back, and
		// effectively calls TeleportTo() i.e. invalidates the previous position.
		void						SetPosition(const Asura_Vector_3& xPosition);

		// TeleportTo explicitly says that the previous position of the object 
		// is no longer valid - e.g. for moving an External Control object to
		// a new place without the Physics assuming it moved through the space
		// between points.
		void						TeleportTo( const Asura_Vector_3& xPosition );
	
		// Orientation
		const Asura_Matrix_3x3&		GetOrientation() const;
		
		void						SetOrientation(const Asura_Quat& xOrient);
		void						SetOrientation(const Asura_Matrix_3x3& xOrient);
		
		void						SetOrientationFromDir( const Asura_Vector_3& xDir )
		{
			Asura_Matrix_3x3 xOrient;

			Asura_Maths::OrientationFromDirection( xOrient, xDir );
			SetOrientation( xOrient );
		}

		void						SetOrientationFromEuler( const Asura_Euler_3& xEuler )
		{
			Asura_Matrix_3x3 xOrient;

			xEuler.ToMatrix( xOrient );
			SetOrientation( xOrient );
		}


		enum AXIS_CONSTRAINT_ID
		{
			AXIS_CONSTRAINT_NONE,
			AXIS_CONSTRAINT_Y_AXIS,
			AXIS_CONSTRAINT_ALL_AXES,
			AXIS_CONSTRAINT_SELF_RIGHTING,

			NUM_AXIS_CONTRAINT_IDS
		};

		enum AXIS_CONSTRAINT_ID GetAxisConstraint( void ) const;
		
		void SetAxisConstraint( enum AXIS_CONSTRAINT_ID eAxisConstraint );		

		enum COLLISION_PROXY
		{
			COLLISION_PROXY_OBB = 0,
			COLLISION_PROXY_SPHERE = 1,
			COLLISION_PROXY_SPHERESTACK = 2,
			COLLISION_PROXY_POLYGONAL = 3, 

			NUM_COLLISION_PROXIES
		};

		enum COLLISION_PROXY GetCollisionProxy( void ) const;
	
		void SetCollisionProxy( enum COLLISION_PROXY eProxyType );

		// Collision object / bounding box

		// Returns the AABB which contains the object in the current frame and the last frame;
		// this takes rotations etc into account.
		const Asura_Bounding_Box&		GetBoundingBox() const;

		const Asura_Collision_Object*	GetCollisionObject() const { return m_pxCollisionObject; }

		void							SetCollisionObject( const Asura_Collision_Object* pxCollision );
				

		// Velocity and impulses
		//
		// A note about units both linear and angular velocities are in WORLD SPACE.
		// Linear velocity is in m per sec. Angular velocity is in radians per sec.
		// An angular velocity of ( 0, 2*pi, 0 ) will make the object rotate around the 
		// the world y-axis at a rate of 1 revolution per second.

		// Set Linear Velocity - 
		//   For Dynamic objects this sets linear velocity but leaves angular velocity as is
		//   For Actors this sets how fast you want to move next physics frame
		void SetLinVelocity( const Asura_Vector_3& xLinVelocity );

		// Set Angular Velocity but leave linear velocity as is - only to be used with Dynamic objects
		void SetAngVelocity( const Asura_Vector_3& xAngVelocity );
		
		// Set both angular and linear velocity - only to be used with Dynamic objects
		void SetVelocities( const Asura_Vector_3& xLinVelocity, const Asura_Vector_3& xAngVelocity );

		// Get both angular and linear velocity - only to be used with Dynamic objects
		void GetVelocities( Asura_Vector_3& xLinVelocity, Asura_Vector_3& xAngVelocity ) const;

		// Get the 'jump velocity' of an actor - this is a separate internal velocity due to jumping & falling
		void GetActorJumpVelocity( Asura_Vector_3& xJumpVelocity ) const;

		// Set the 'jump velocity' of an actor - Note: this velocity is a measure of the separate, instantaneous 
		// momentum due to falling / jumping - not the "velocity when I press jump" which will be something else
		// in your project code. You *can* use this to do start a character jump - but you may wish to call
		// ApplyImpulse() instead which adds your suggested velocity to the current value. 
		// You can effectively clamp falling speeds by calling the Get...() method above, tweak the value, and
		// then call this Set...() method.
		void SetActorJumpVelocity( const Asura_Vector_3& xJumpVelocity );

		// zero all momenta - both angular and linear
		void ZeroMomentum( void );

		// zero linear momentum but leave angular components
		void ZeroLinearMomentum( void );

		// zero angular momentum but leave linear components
		void ZeroAngularMomentum( void );


		// These ApplyImpulse methods all apply a change to the object's linear and/or angular momentum.
		// If bIgnoreMass is true (the default) then the impulse is treated as a direct velocity change,
		// otherwise the object's mass is taken into account. 
		// 
		// e.g. an xImpulse of magnitude 4.0f will result in a change in velocity of 4m/s for any object 
		// with bIgnoreMass==true, or any object weighing 1kg. If the object weighed 2kg and bIgnoreMass
		// was set to false, the object's velocity would only change by only 2m/s.
		
		// Apply a linear impulse to the object's centre-of-mass
		void ApplyImpulse( const Asura_Vector_3& xImpulse, bool bIgnoreMass = true );

		// Apply a linear impulse to a point on the object (position in world-space)
		void ApplyImpulse( const Asura_Vector_3& xImpulse, const Asura_Vector_3& xPosition, bool bIgnoreMass = true );

		// Apply an impulse to the angular velocity only 
		void ApplyRotationalImpulse( const Asura_Euler_3& xAngularImpulse, bool bIgnoreMass = true );


		// vehicle-specific init
		void SetupVehicle( const Asura_Bounding_Box& xBB, const Asura_AVector3* pxBasePositions );

		// general behaviour flags
		bool IsAsleep() const;

		// KJFL - Wake the object so it moves. Currently can't tell things to go to sleep by design.
		void WakeUp( void );

		void Sleep();

		bool IsIgnore()	const;
		void SetIgnore( bool bIgnore );
	
		bool FeelsGravity()	const;
		void SetFeelsGravity( bool bFeelsGravity );

		// KJFL - 22 February 2007 
		//
		// Default movement for an Actor is "super-slidy" - when an Actor hits a poly it will resolve 
		// its movement direction against the obstacle and carry on moving with the same speed. E.g.
		// If you ask an Actor to move at 6m/s along the ground, but it hits a slope, it will change
		// direction to be going up the slope and move the same distance along the slope as it would 
		// have on flat ground. Similarly for running into walls - the Actor will slide along at normal
		// speed.
		//
		// You can turn this behaviour off by setting the value below to false, which will make the 
		// velocity be resolved against the obstacle normal inelastically. *HOWEVER* - Actors will 
		// still be super-slidy on polys flagged as "PHYSICS WALKABLE SURFACE".
		bool GetActorIsSuperSlidy( void ) const;
		void SetActorIsSuperSlidy( bool bSuperSlidy );

		// KJFL - 22 February 2007  
		//
		// Actors can be flagged to treat any polygons which are steep slopes (beyond 45 degrees
		// currently) as vertical walls - so rather than going a bit up the slope and then sliding
		// down again, they will slide perpendicularly as if it was a vertical wall in front of them.
		// The default behaviour is not to do this (i.e. false in the value below) but this is for
		// purely historical reasons to keep older projects as-is. Note this doesn't affect Actors
		// when sliding down steep slopes - just when they're trying to go up them.
		//
		// Note: Polys flagged as "PHYSICS WALKABLE SURFACE" are always counted as floor irrespective
		// of this setting, and the Actor won't treat them as walls and will walk on them without
		// sliding.
		bool GetActorShouldTreatSteepSlopesAsVertical( void ) const;
		void SetActorShouldTreatSteepSlopesAsVertical( bool bTreatAsVertical );

		bool GetActorMoveAsVehicle( void ) const;
		void SetActorMoveAsVehicle( bool bMoveAsVehicle );

		// KJFL - 28 March 2007 - 
		// 
		// Actors flagged as "Don't Push Actors" will collide against other Actors as if they
		// were static objects that can't be pushed out of the way.
		bool GetActorDontPushActors( void ) const;
		void SetActorDontPushActors( bool bDontPushActors );

		// KJFL - 29 May 2007 - This object should be treated as super-slidy by Actors.
		// Can be used to stop Actors standing on each other's heads.
		bool GetActorsShouldSlideOffMe( void ) const;
		void SetActorsShouldSlideOffMe( bool bSlide );

		// KJFL - 21 August 2007 - Actors should treat this object as a static obstacle when they're
		// moving and slide off it. However, when the objects are intersecting etc or the object runs 
		// into the Actor, masses are used as normal to determine the collision response.
		bool GetActorsDontPushMe( void ) const;
		void SetActorsDontPushMe( bool bDontPush );
	
		// if "ReportObjectCollisions" is true, any collisions with other objects will mean
		// a report is generated and added to the linked list accessed by GetObjectCollisionReportList().
		bool ReportObjectCollisions( void ) const;
		void SetReportObjectCollisions( bool bReport );

		// Returns a linked list of collision reports, or null if no reports. See report class for
		// more details of the data it contains. Do *not* try to save a pointer to a collision 
		// report for "later use" - all the pointers will become invalid at the next physics update.
		const Asura_Physics_ObjectCollisionReport* GetObjectCollisionReportList( void ) const;

		// if "ReportEnvironmentCollisions" is true, any collisions with the environment will mean
		// a report is generated and added to the linked list accessed by GetEnvironmentCollisionReportList().
		bool ReportEnvironmentCollisions( void ) const;
		void SetReportEnvironmentCollisions( bool bReport );

		// Returns a linked list of collision reports, or null if no reports. See report class for
		// more details of the data it contains. Do *not* try to save a pointer to a collision 
		// report for "later use" - all the pointers will become invalid at the next physics update.
		const Asura_Physics_EnvironmentCollisionReport* GetEnvironmentCollisionReportList( void ) const;


#ifdef ASURA_USE_COLLISIONFX
		void SetReportObjectCollisionFX( bool bReport );
		bool GetReportObjectCollisionFX( void ) const;
	
		void SetReportEnvironmentCollisionFX( bool bReport );
		bool GetReportEnvironmentCollisionFX( void ) const;
#endif

		// Get/set the entity guid which is associated with this physics object - this
		// is the guid that will be returned in any collision reports if something hits
		// this object. This defaults to ASURA_GUID_UNREGISTERED.
		Asura_Guid GetAssociatedEntityGuid( void ) const;
		void SetAssociatedEntityGuid( Asura_Guid uGuid );
	
		// Bool to determine whether to use the object's own gravity value or the Physics system's
		// global direction vector.
		bool GetUseOwnGravityDirection( void ) const;
		void SetUseOwnGravityDirection( bool bUseOwnGravityDirection );

		// Get/Set the direction of gravity for this object
		const Asura_AVector3& GetOwnGravityDirection( void ) const;
		void SetOwnGravityDirection( const Asura_AVector3& xDirection );

		// Get direction of gravity to be applied to this object...
		const Asura_AVector3& GetGravityDirection( void ) const;

		// Get list of support features - the points on which this object is currently resting
		u_int GetSupportFeature( Asura_AVector3* pxSupportPoints, const Asura_AVector3& xAxis ) const;

		// Get/Set the scale factor to use with gravity, e.g. 0.0f means no gravity,
		// 2.0f means double gravity, -1.0f means gravity is inverted, etc.
		float GetGravityScale( void ) const;
		void SetGravityScale( float fGravityScale );

		// Elasticity of collision response - automatically clamped to 0.0f->1.0f range
		// 1.0f means fully elastic (bounces away with same speed)
		// 0.0f means fully inelastic (stops dead)
		float GetElasticity( void ) const;
		void SetElasticity( float fElasticity );

		// Friction of collision response - automatically clamped to be +ve
		// 0.0f means no friction
		// Otherwise friction impulse = FrictionCoefficient * NormalReactionForce (i.e. Coulomb friction)
		float GetFriction( void ) const;
		void SetFriction( float fFriction );

		// Drag of medium (air etc) - automatically clamped to 0.0f->1.0f range
		// 0.0f means no drag
		// 1.0f means 100% drag - any velocity immediately cancelled
		float GetDrag( void ) const;
		void SetDrag( float fDrag );
		
		// Angular Drag of medium (air etc) - automatically clamped to 0.0f->1.0f range
		// 0.0f means no drag
		// 1.0f means 100% drag - any velocity immediately cancelled
		float GetAngularDrag( void ) const;
		void SetAngularDrag( float fAngularDrag );

		// WARNING: this function only works for actors, not general objects
		bool IsInContactWithFloor( void ) const;

		// Returns true if the object has touched the environment or another object 
		// during the last game frame.
		bool HasHitSomething( void ) const;

		bool IsSupported( void ) const;

		// Returns true if the object has touched another object during the last game frame.
		bool HasHitAnObject( void ) const;

		// This is equivalent to using IsIgnoreDynamicObjects() && IsIgnoreExternalControlObjects()
		bool IsOnlyCollideWithEnvironmentAndStaticObjects( void ) const
		{
			return IsIgnoreDynamicObjects() && IsIgnoreExternalControlObjects();
		}
	
		// This is equivalent to using SetIgnoreDynamicObjects() and SetIgnoreExternalControlObjects()
		void SetOnlyCollideWithEnvironmentAndStaticObjects( bool bNewSetting )
		{
			SetIgnoreDynamicObjects( bNewSetting );
			SetIgnoreExternalControlObjects( bNewSetting );
		}

		// Turn on/off collisions with dynamic objects 
		// (meaning you can still collide potentially with env, static & external controlled objects)
		void SetIgnoreDynamicObjects( bool bNewSetting );
		bool IsIgnoreDynamicObjects( void ) const;

		// Turn on/off collisions with external control objects 
		// (meaning you can still potentially collide with env, static & dynamic objects)
		void SetIgnoreExternalControlObjects( bool bNewSetting );
		bool IsIgnoreExternalControlObjects( void ) const;

		// ART - 06/06/06 - Accessors for flags of polys to be ignored by 
		// this physics-object.
		u_short GetFlagsOfPolysToIgnore() const;
		void SetFlagsOfPolysToIgnore( const u_short usFlagsOfPolysToIgnore );

#ifdef ASURA_TOOLS
        u_int Tools_GetFlagsOfPolysToIgnore() const;
        void  Tools_SetFlagsOfPolysToIgnore( const u_int uFlagsOfPolysToIgnore );
#endif

		// These methods use 1.0f/Mass, so 10kg would be 0.1f, and an infinite mass would be 0.0f
		// (Note, objects default to a value of 1.0, so don't feel you need to set masses to be
		// "correct" - just set objects masses when needed to get the desired effect.
		float GetInvMass( bool bReturnEffectiveValue = true ) const;
		void SetInvMass( float fInvMass );

		void SetUseAsRestFrame( bool bUseAsRestFrame );
		bool HasRestFrame( void ) const;
		//is this object using (IE, resting on) the rest frame owned by the passed in object
		bool HasRestFrameFrom(const Asura_Physics_Object* pxOtherObject) const;
		// Alternative for the rest frame test - get the rest frame from one instance, and see if it's the one in the other instance.
		// So the system is similar, only return the rest frame if we own it (i.e. things rest on us, not us on them).
		const Asura_Physics_RestFrame* GetOwnedRestFrame() const;
		bool HasMatchingRestFrame(const Asura_Physics_RestFrame* pxRestFrame) const;


		// Collision Groups - for two objects to collide, they must *both* want to collide with 
		// at least one collision group the other object is a member of.
		//
		// Get/Set the bitfield which says which groups the object is a member of
		u_int GetCollisionGroups( void ) const;
		void SetCollisionGroups( u_int uGroupsBitfield );

		// Get/Set the bitfield which says which groups the object should collide with
		u_int GetCollisionGroupsToCollideWith( void ) const;
		void SetCollisionGroupsToCollideWith( u_int uGroups );

		void SetHandleToIgnore(Asura_Handle iHandle);
		bool IsInContactWithNearlyFloor() const		{ return IsInContactWithFloor(); }
		void SetLinImpulse(const Asura_Vector_3& xImpulse) { ApplyImpulse( xImpulse); } 
		const Asura_Vector_3& GetLinImpulse() const;
		const Asura_Vector_3& GetLinVelocity() const  { return GetLinImpulse(); }

		Asura_Handle GetHandleToIgnore() const;

		void SetOnlyCollideWithEnvironment( bool bNewSetting )
		{
			SetOnlyCollideWithEnvironmentAndStaticObjects( bNewSetting );
		}

		void SetAllSurfacesWalkable( bool bSet );
		bool GetAllSurfacesWalkable() const;

		void	SetNotWalkableSurfaceFlags( u_short usFlags );
		u_short GetNotWalkableSurfaceFlags() const;

		void	SetPolyFlagsToReturnForActorCollision( u_short usFlags );
		u_short	GetPolyFlagsToReturnForActorCollision() const;

		// Game code may require access to this, but its not part of the designed interface, so avoid if possible.
		const Asura_Physics_RigidBodyInstance* GetRigidBodyInstance() const { return m_pxRigidBodyInstance; }

	private:

		static Asura_Physics_Object_Factory	s_xFactory;

		// KJFL - Tuesday, November 16, 2004 - ideally I'd have private constructors to force the use of the Create/Destroy mempool interface
		// but then I'd have to make this class a friend of the mempool node which calls new when creating new pools (I may change that to use
		// allocate instead). Anyway, no constructors is probably better from a code generation point-of-view.
		//Asura_Physics_Object() {}
		//~Asura_Physics_Object() {}
		
        ASURA_PHYSICS_OBJECT_TYPE               m_eType;
		Asura_Handle							m_iHandle;
		Asura_Handle							m_iRigidBodyHandle;

		class Asura_Physics_RigidBodyInstance*	m_pxRigidBodyInstance;
		const class Asura_Collision_Object*		m_pxCollisionObject;


		// KJFL - 09 November 2005 - it would be nice to get rid of these position
		// and orientation members, since in theory they're just duplicates of those
		// contained in the rigid body. However, 'actors' i.e. axially-aligned bodies
		// store their 'real' orientation here.
		mutable	Asura_Vector_3		m_xPosition;
		mutable	Asura_Matrix_3x3	m_xOrientation;

};


#endif//__ASURA_PHYSICS_OBJECT_H__
