// 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 PXS_SIMPLE_ISLAND_GEN_H
#define PXS_SIMPLE_ISLAND_GEN_H

#include "foundation/PxUserAllocated.h"
#include "PxsIslandSim.h"
#include "CmTask.h"

/*
PT: runs first part of the island gen's second pass in parallel with the Pxg-level constraint partitioning.

mIslandGen task spawns Pxg constraint partitioning task(s).
mIslandGen runs processNarrowPhaseTouchEvents() in parallel with Pxg.

///////////////////////////////////////////////////////////////////////////////

Previous design:

mPostIslandGen runs as a continuation task after mIslandGen and Pxg.

mPostIslandGen mainly runs mSetEdgesConnectedTask, which:
- calls mSimpleIslandManager->setEdgeConnected()
- calls mSimpleIslandManager-secondPassIslandGen()
- calls wakeObjectsUp()

///////////////////////////////////////////////////////////////////////////////

New design:

postIslandGen is not a task anymore (mPostIslandGen does not exist).
postIslandGen is directly called at the end of mIslandGen.
So it now runs in parallel with Pxg.
mIslandGen and Pxg continue to mSolver task.

postIslandGen mainly runs mSetEdgesConnectedTask, which:
- calls mSimpleIslandManager->setEdgeConnected()
- calls mSimpleIslandManager->secondPassIslandGenPart1()

mSolver now first runs the parts that don't overlap with Pxg:
- calls mSimpleIslandManager-secondPassIslandGenPart2()
- calls wakeObjectsUp()

///////////////////////////////////////////////////////////////////////////////

Before:
mIslandGen->processNarrowPhaseTouchEvents	|mPostIslandGen											|mSolver
=>PxgConstraintPartition					|=>setEdgesConnected->secondPassIslandGen->wakeObjectsUp|

After:
mIslandGen->processNarrowPhaseTouchEvents->postIslandGen									|secondPassIslandGenPart2->wakeObjectsUp->mSolver
=>PxgConstraintPartition					=>setEdgesConnected->secondPassIslandGenPart1	|
*/
#define USE_SPLIT_SECOND_PASS_ISLAND_GEN	1

namespace physx
{
	class PxsContactManager;

// PT: TODO: fw declaring an Sc class here is not good
namespace Sc
{
	class Interaction;
}

namespace Dy
{
	struct Constraint;
}

namespace IG
{
	class SimpleIslandManager;

class ThirdPassTask : public Cm::Task
{
	SimpleIslandManager& mIslandManager;
	IslandSim& mIslandSim;

public:

	ThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager, IslandSim& islandSim);

	virtual void runInternal();

	virtual const char* getName() const
	{
		return "ThirdPassIslandGenTask";
	}

private:
	PX_NOCOPY(ThirdPassTask)
};

class PostThirdPassTask : public Cm::Task
{
	SimpleIslandManager& mIslandManager;

public:

	PostThirdPassTask(PxU64 contextID, SimpleIslandManager& islandManager);

	virtual void runInternal();

	virtual const char* getName() const
	{
		return "PostThirdPassTask";
	}
private:
	PX_NOCOPY(PostThirdPassTask)
};

class AuxCpuData
{
	public:
	PX_FORCE_INLINE PxsContactManager*	getContactManager(IG::EdgeIndex edgeId)	const { return reinterpret_cast<PxsContactManager*>(mConstraintOrCm[edgeId]);	}
	PX_FORCE_INLINE Dy::Constraint*		getConstraint(IG::EdgeIndex edgeId)		const { return reinterpret_cast<Dy::Constraint*>(mConstraintOrCm[edgeId]);		}

	Cm::BlockArray<void*>	mConstraintOrCm;	//! Pointers to either the constraint or Cm for this pair
};

class SimpleIslandManager : public PxUserAllocated
{
	HandleManager<PxU32> mNodeHandles;		//! Handle manager for nodes
	HandleManager<EdgeIndex> mEdgeHandles;	//! Handle manager for edges

	//An array of destroyed nodes
	PxArray<PxNodeIndex> mDestroyedNodes;
	Cm::BlockArray<Sc::Interaction*> mInteractions;

	//Edges destroyed this frame
	PxArray<EdgeIndex> mDestroyedEdges;
	GPUExternalData	mGpuData;

	CPUExternalData	mCpuData;
	AuxCpuData		mAuxCpuData;

	PxBitMap mConnectedMap;

	// PT: TODO: figure out why we still need both
	IslandSim mAccurateIslandManager;
	IslandSim mSpeculativeIslandManager;

	ThirdPassTask mSpeculativeThirdPassTask;
	ThirdPassTask mAccurateThirdPassTask;

	PostThirdPassTask mPostThirdPassTask;
	PxU32 mMaxDirtyNodesPerFrame;

	const PxU64	mContextID;
	const bool mGPU;
public:

	SimpleIslandManager(bool useEnhancedDeterminism, bool gpu, PxU64 contextID);
	~SimpleIslandManager();

	PxNodeIndex	addNode(bool isActive, bool isKinematic, Node::NodeType type, void* object);
	void		removeNode(const PxNodeIndex index);

	// PT: these two functions added for multithreaded implementation of Sc::Scene::islandInsertion
	void preallocateContactManagers(PxU32 nb, EdgeIndex* handles);
	bool addPreallocatedContactManager(EdgeIndex handle, PxsContactManager* manager, PxNodeIndex nodeHandle1, PxNodeIndex nodeHandle2, Sc::Interaction* interaction, Edge::EdgeType edgeType);

	EdgeIndex addContactManager(PxsContactManager* manager, PxNodeIndex nodeHandle1, PxNodeIndex nodeHandle2, Sc::Interaction* interaction, Edge::EdgeType edgeType);
	EdgeIndex addConstraint(Dy::Constraint* constraint, PxNodeIndex nodeHandle1, PxNodeIndex nodeHandle2, Sc::Interaction* interaction);

	PX_FORCE_INLINE	PxIntBool isEdgeConnected(EdgeIndex edgeIndex) const { return mConnectedMap.test(edgeIndex); }

	void activateNode(PxNodeIndex index);
	void deactivateNode(PxNodeIndex index);
	void putNodeToSleep(PxNodeIndex index);

	void removeConnection(EdgeIndex edgeIndex);
	
	void firstPassIslandGen();
	void additionalSpeculativeActivation();
	void secondPassIslandGen();
	void secondPassIslandGenPart1();
	void secondPassIslandGenPart2();
	void thirdPassIslandGen(PxBaseTask* continuation);

	PX_INLINE void clearDestroyedPartitionEdges()
	{
		mGpuData.mDestroyedPartitionEdges.forceSize_Unsafe(0);
	}

	void setEdgeConnected(EdgeIndex edgeIndex, Edge::EdgeType edgeType);
	void setEdgeDisconnected(EdgeIndex edgeIndex);

	void setEdgeRigidCM(const EdgeIndex edgeIndex, PxsContactManager* cm);

	void clearEdgeRigidCM(const EdgeIndex edgeIndex);

	void setKinematic(PxNodeIndex nodeIndex);

	void setDynamic(PxNodeIndex nodeIndex);

	PX_FORCE_INLINE	IslandSim&			getSpeculativeIslandSim()			{ return mSpeculativeIslandManager;	}
	PX_FORCE_INLINE	const IslandSim&	getSpeculativeIslandSim()	const	{ return mSpeculativeIslandManager;	}

	PX_FORCE_INLINE	IslandSim&			getAccurateIslandSim()				{ return mAccurateIslandManager;	}
	PX_FORCE_INLINE	const IslandSim&	getAccurateIslandSim()		const	{ return mAccurateIslandManager;	}

	PX_FORCE_INLINE	const AuxCpuData&	getAuxCpuData()				const	{ return mAuxCpuData;				}

	PX_FORCE_INLINE PxU32				getNbEdgeHandles()			const	{ return mEdgeHandles.getTotalHandles(); }

	PX_FORCE_INLINE PxU32				getNbNodeHandles()			const	{ return mNodeHandles.getTotalHandles(); }

	void deactivateEdge(const EdgeIndex edge);

	PX_FORCE_INLINE PxsContactManager*	getContactManager(IG::EdgeIndex edgeId)	const { return reinterpret_cast<PxsContactManager*>(mAuxCpuData.mConstraintOrCm[edgeId]);	}
	PX_FORCE_INLINE Dy::Constraint*		getConstraint(IG::EdgeIndex edgeId)		const { return reinterpret_cast<Dy::Constraint*>(mAuxCpuData.mConstraintOrCm[edgeId]);		}

	PX_FORCE_INLINE Sc::Interaction*	getInteractionFromEdgeIndex(IG::EdgeIndex edgeId) const { return mInteractions[edgeId]; }

	PX_FORCE_INLINE	PxU64				getContextId() const { return mContextID; }

	bool checkInternalConsistency();

private:

	friend class ThirdPassTask;
	friend class PostThirdPassTask;

	bool		validateDeactivations() const;
	EdgeIndex	addEdge(void* edge, PxNodeIndex nodeHandle1, PxNodeIndex nodeHandle2, Sc::Interaction* interaction);
	EdgeIndex	resizeEdgeArrays(EdgeIndex handle, bool flag);

	PX_NOCOPY(SimpleIslandManager)
};

}
}

#endif
