// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of NVIDIA CORPORATION nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright (c) 2008-2025 NVIDIA Corporation. All rights reserved.
// Copyright (c) 2004-2008 AGEIA Technologies, Inc. All rights reserved.
// Copyright (c) 2001-2004 NovodeX AG. All rights reserved.  

#ifndef SC_ACTOR_SIM_H
#define SC_ACTOR_SIM_H

#include "foundation/PxUserAllocated.h"
#include "CmPtrTable.h"
#include "CmUtils.h"
#include "PxActor.h"
#include "ScInteractionFlags.h"
#include "ScActorCore.h"
#include "PxsSimpleIslandManager.h"
#include "PxFiltering.h"

namespace physx
{

class PxActor;

namespace Sc
{

#define SC_NOT_IN_SCENE_INDEX		0xffffffff  // the body is not in the scene yet
#define SC_NOT_IN_ACTIVE_LIST_INDEX	0xfffffffe  // the body is in the scene but not in the active list

	struct PxFilterObjectFlagEx : PxFilterObjectFlag
	{
		enum Enum
		{
			eRIGID_STATIC		= eNEXT_FREE,
			eRIGID_DYNAMIC		= eNEXT_FREE<<1,
			eNON_RIGID			= eNEXT_FREE<<2,
			eDEFORMABLE_SURFACE	= eNEXT_FREE<<3,
			eDEFORMABLE_VOLUME	= eNEXT_FREE<<4,
			ePARTICLESYSTEM		= eNEXT_FREE<<5,

			eLAST				= ePARTICLESYSTEM
		};
	};

	static const PxReal ScInternalWakeCounterResetValue = 20.0f*0.02f;

	class Interaction;
	class ElementSim;
	class Scene;

	class ShapeManager : public PxUserAllocated
	{
	public:

		ShapeManager() {}
		~ShapeManager() {}

		PX_FORCE_INLINE	PxU32				getNbElements()		const	
											{ 
												return mShapes.getCount(); 
											}
		PX_FORCE_INLINE	ElementSim**		getElements()				
											{ 
												return reinterpret_cast<ElementSim**>(mShapes.getPtrs());
											}
		PX_FORCE_INLINE	ElementSim*const*	getElements()		const	
											{ 
												return reinterpret_cast<ElementSim*const*>(mShapes.getPtrs()); 
											}
		//					void			onElementAttach(ElementSim& element);
							void			onElementDetach(ElementSim& element);

		Cm::PtrTable	mShapes;
	};

	class ActorSim : public ShapeManager
	{
		friend class Scene;  // the scene is allowed to set the scene array index
		friend class Interaction;
		PX_NOCOPY(ActorSim)

	public:

		enum InternalFlags
		{
			//BF_DISABLE_GRAVITY			= 1 << 0,	// Don't apply the scene's gravity

			BF_HAS_STATIC_TOUCH				= 1 << 1,	// Set when a body is part of an island with static contacts. Needed to be able to recalculate adaptive force if this changes
			BF_KINEMATIC_MOVED				= 1 << 2,	// Set when the kinematic was moved

			BF_ON_DEATHROW					= 1 << 3,	// Set when the body is destroyed

			BF_IS_IN_SLEEP_LIST				= 1 << 4,	// Set when the body is added to the list of bodies which were put to sleep
			BF_IS_IN_WAKEUP_LIST			= 1 << 5,	// Set when the body is added to the list of bodies which were woken up
			BF_SLEEP_NOTIFY					= 1 << 6,	// A sleep notification should be sent for this body (and not a wakeup event, even if the body is part of the woken list as well)
			BF_WAKEUP_NOTIFY				= 1 << 7,	// A wake up notification should be sent for this body (and not a sleep event, even if the body is part of the sleep list as well)

			BF_HAS_CONSTRAINTS				= 1 << 8,	// Set if the body has one or more constraints
			BF_KINEMATIC_SETTLING			= 1 << 9,	// Set when the body was moved kinematically last frame
			BF_KINEMATIC_SETTLING_2			= 1 << 10,
			BF_KINEMATIC_MOVE_FLAGS			= BF_KINEMATIC_MOVED | BF_KINEMATIC_SETTLING | BF_KINEMATIC_SETTLING_2, //Used to clear kinematic masks in 1 call
			BF_KINEMATIC_SURFACE_VELOCITY	= 1 << 11,	//Set when the application calls setKinematicVelocity. Actor remains awake until application calls clearKinematicVelocity. 
			BF_IS_COMPOUND_RIGID			= 1 << 12,	// Set when the body is a compound actor, we dont want to set the sq bounds

											// PT: WARNING: flags stored on 16-bits now.
		};

											ActorSim(Scene&, ActorCore&);
		virtual								~ActorSim();

		// Get the scene the actor resides in
		PX_FORCE_INLINE	Scene&				getScene()					const	{ return mScene; }

		// Get the number of interactions connected to the actor
		PX_FORCE_INLINE	PxU32				getActorInteractionCount()	const	{ return mInteractions.size(); }

		// Get an iterator to the interactions connected to the actor
		PX_FORCE_INLINE	Interaction**		getActorInteractions()		const	{ return mInteractions.begin();	}

		// Get the type ID of the actor
		PX_FORCE_INLINE	PxActorType::Enum	getActorType()				const	{ return mCore.getActorCoreType();	}

		// Returns true if the actor is a dynamic rigid body (including articulation links)
		PX_FORCE_INLINE	PxU16				isDynamicRigid()			const	{ return mFilterFlags & PxFilterObjectFlagEx::eRIGID_DYNAMIC;		}
		PX_FORCE_INLINE	PxU16				isDeformableSurface()		const	{ return mFilterFlags & PxFilterObjectFlagEx::eDEFORMABLE_SURFACE;	}
		PX_FORCE_INLINE	PxU16				isDeformableVolume()		const	{ return mFilterFlags & PxFilterObjectFlagEx::eDEFORMABLE_VOLUME;	}
		PX_FORCE_INLINE PxU16				isParticleSystem()			const	{ return mFilterFlags & PxFilterObjectFlagEx::ePARTICLESYSTEM;		}
		PX_FORCE_INLINE PxU16				isNonRigid()				const	{ return mFilterFlags & PxFilterObjectFlagEx::eNON_RIGID;			}
		PX_FORCE_INLINE	PxU16				isStaticRigid()				const	{ return mFilterFlags & PxFilterObjectFlagEx::eRIGID_STATIC;		}

		virtual			void				postActorFlagChange(PxU32, PxU32) {}

						void				setActorsInteractionsDirty(InteractionDirtyFlag::Enum flag, const ActorSim* other, PxU8 interactionFlag);

		PX_FORCE_INLINE	ActorCore&			getActorCore()							const	{ return mCore;							}

		PX_FORCE_INLINE	bool				isActive()								const	{ return (mActiveListIndex < SC_NOT_IN_ACTIVE_LIST_INDEX);	}

		PX_FORCE_INLINE PxU32				getActiveListIndex()					const	{ return mActiveListIndex;				}  // if the body is active, the index is smaller than SC_NOT_IN_ACTIVE_LIST_INDEX
		PX_FORCE_INLINE void				setActiveListIndex(PxU32 index)					{ mActiveListIndex = index;				}

		PX_FORCE_INLINE PxU32				getActiveCompoundListIndex()			const	{ return mActiveCompoundListIndex;		}  // if the body is active and is compound, the index is smaller than SC_NOT_IN_ACTIVE_LIST_INDEX
		PX_FORCE_INLINE void				setActiveCompoundListIndex(PxU32 index)			{ mActiveCompoundListIndex = index;		}

		PX_FORCE_INLINE PxNodeIndex			getNodeIndex()							const	{ return mNodeIndex;					}

		PX_FORCE_INLINE PxU32				getActorID()							const	{ return mId;							}

		PX_FORCE_INLINE	PxU16				getInternalFlag()						const	{ return mInternalFlags;				}
		PX_FORCE_INLINE PxU16				readInternalFlag(InternalFlags flag)	const	{ return PxU16(mInternalFlags & flag);	}
		PX_FORCE_INLINE void				raiseInternalFlag(InternalFlags flag)			{ mInternalFlags |= flag;				}
		PX_FORCE_INLINE void				clearInternalFlag(InternalFlags flag)			{ mInternalFlags &= ~flag;				}

		PX_FORCE_INLINE	PxFilterObjectAttributes	getFilterAttributes()			const	{ return PxFilterObjectAttributes(mFilterFlags);	}

		virtual			PxActor*			getPxActor() const = 0;

		//This can all be removed and functionality can be subsumed by the island system, removing the need for this 
		//virtual call and any associated work
		virtual			void				registerCountedInteraction() {}
		virtual			void				unregisterCountedInteraction() {}
		virtual			PxU32				getNumCountedInteractions()	const { return 0;  }

		virtual			void				internalWakeUp(PxReal wakeCounterValue = ScInternalWakeCounterResetValue) { PX_UNUSED(wakeCounterValue); }

	private:
		//These are called from interaction creation/destruction
						void				registerInteractionInActor(Interaction* interaction);
						void				unregisterInteractionFromActor(Interaction* interaction);
						void				reallocInteractions(Sc::Interaction**& mem, PxU32& capacity, PxU32 size, PxU32 requiredMinCapacity);
	protected:
		// dsequeira: interaction arrays are a major cause of small allocations, so we don't want to delegate them to the heap allocator
		// it's not clear this inline array is really needed, we should take it out and see whether the cache perf is worse

		static const PxU32 INLINE_INTERACTION_CAPACITY = 4;
						Interaction*		mInlineInteractionMem[INLINE_INTERACTION_CAPACITY];

		Cm::OwnedArray<Sc::Interaction*, Sc::ActorSim, PxU32, &Sc::ActorSim::reallocInteractions>
											mInteractions;

						Scene&				mScene;
						ActorCore&			mCore;
		// Sleeping
						PxU32				mActiveListIndex;			// Used by Scene to track active bodies
						PxU32				mActiveCompoundListIndex;	// Used by Scene to track active compound bodies

		// Island manager
						PxNodeIndex			mNodeIndex;

						PxU32				mId;	// PT: ID provided by Sc::Scene::mActorIDTracker

						PxU16				mInternalFlags;
						PxU16				mFilterFlags;	// PT: PxFilterObjectAttributes. Capturing the type information in local flags here is redundant
															// but avoids reading the Core memory from the Sim object, and is also faster to test multiple types at once.
	};

} // namespace Sc

}

#endif
