// 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.  

// PT: SQ-API LEVEL 2 (Level 1 = SqPruner.h)
// PT: this file is part of a "high-level" set of files within Sq. The SqPruner API doesn't rely on them.
// PT: this should really be at Np level but moving it to Sq allows us to share it.

#include "SqManager.h"
#include "GuSqInternal.h"
#include "GuBounds.h"

using namespace physx;
using namespace Sq;
using namespace Gu;

PrunerExt::PrunerExt() : mPruner(NULL), mDirtyList("SQmDirtyList"), mDirtyStatic(false)
{
}

PrunerExt::~PrunerExt()
{
	PX_DELETE(mPruner);
}

void PrunerExt::init(Pruner* pruner)
{
	mPruner = pruner;
}

void PrunerExt::preallocate(PxU32 nbShapes)
{
//	if(nbShapes > mDirtyMap.size())
//		mDirtyMap.resize(nbShapes);

	if(mPruner)
		mPruner->preallocate(nbShapes);
}

void PrunerExt::flushMemory()
{
	if(!mDirtyList.size())
		mDirtyList.reset();

	// PT: TODO: flush bitmap here

	// PT: TODO: flush pruner here?
}

void PrunerExt::addToDirtyList(PrunerHandle handle, bool dynamic, const PxTransform& transform)
{
	if(mPruner)
		mPruner->setTransform(handle, transform);

	PxBitMap& dirtyMap = mDirtyMap;
	{
		if(dirtyMap.size() <= handle)
		{
			PxU32 size = PxMax<PxU32>(dirtyMap.size()*2, 1024);
			const PxU32 minSize = handle+1;
			if(minSize>size)
				size = minSize*2;
			dirtyMap.resize(size);
			PX_ASSERT(handle<dirtyMap.size());
			PX_ASSERT(!dirtyMap.test(handle));
		}
	}

	if(!dirtyMap.test(handle))
	{
		dirtyMap.set(handle);
		mDirtyList.pushBack(handle);
	}

	if(!dynamic)
		mDirtyStatic = true;
}

void PrunerExt::removeFromDirtyList(PrunerHandle handle)
{
	PxBitMap& dirtyMap = mDirtyMap;
//	if(dirtyMap.test(handle))
	if(dirtyMap.boundedTest(handle))
	{
		dirtyMap.reset(handle);
		mDirtyList.findAndReplaceWithLast(handle);
	}

	// PT: if we remove the object that made us set mDirtyStatic to true, tough luck,
	// we don't bother fixing that bool here. It's going to potentially cause an
	// unnecessary update of the character controller's caches, which is not a big deal.
}

bool PrunerExt::processDirtyList(PxU32 index, const Adapter& adapter, float inflation)
{
	const PxU32 numDirtyList = mDirtyList.size();
	if(!numDirtyList)
		return false;
	const PrunerHandle* const prunerHandles = mDirtyList.begin();

	for(PxU32 i=0; i<numDirtyList; i++)
	{
		const PrunerHandle handle = prunerHandles[i];
		mDirtyMap.reset(handle);

		// PT: we compute the new bounds and store them directly in the pruner structure to avoid copies. We delay the updateObjects() call
		// to take advantage of batching.
		PX_UNUSED(index);

		PrunerPayloadData ppd;
		const PrunerPayload& pp = mPruner->getPayloadData(handle, &ppd);

		computeBounds(*ppd.mBounds, adapter.getGeometry(pp), *ppd.mTransform, 0.0f, inflation);
	}
	// PT: batch update happens after the loop instead of once per loop iteration
	mPruner->updateObjects(prunerHandles, numDirtyList);
	mDirtyList.clear();

	const bool ret = mDirtyStatic;
	mDirtyStatic = false;
	return ret;
}

// PT: TODO: re-inline this
/*void PrunerExt::growDirtyList(PrunerHandle handle)
{
	// pruners must either provide indices in order or reuse existing indices, so this 'if' is enough to ensure we have space for the new handle
	// PT: TODO: fix this. There is just no need for any of it. The pruning pool itself could support the feature for free, similar to what we do
	// in MBP. There would be no need for the bitmap or the dirty list array. However doing this through the virtual interface would be clumsy,
	// adding the cost of virtual calls for very cheap & simple operations. It would be a lot easier to drop it and go back to what we had before.

	PxBitMap& dirtyMap = mDirtyMap;
	if(dirtyMap.size() <= handle)
		dirtyMap.resize(PxMax<PxU32>(dirtyMap.size() * 2, 1024));
	PX_ASSERT(handle<dirtyMap.size());
	dirtyMap.reset(handle);
}*/

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

CompoundPrunerExt::CompoundPrunerExt() :
	mPruner	(NULL)
{
}

CompoundPrunerExt::~CompoundPrunerExt()
{
	PX_DELETE(mPruner);
}

void CompoundPrunerExt::preallocate(PxU32 nbShapes)
{
//	if(nbShapes > mDirtyList.size())
//		mDirtyList.reserve(nbShapes);

	if(mPruner)
		mPruner->preallocate(nbShapes);
}

void CompoundPrunerExt::flushMemory()
{
	if(!mDirtyList.size())
		mDirtyList.clear();
}

void CompoundPrunerExt::flushShapes(const Adapter& adapter, float inflation)
{
	const PxU32 numDirtyList = mDirtyList.size();
	if(!numDirtyList)
		return;

	const CompoundPair* const compoundPairs = mDirtyList.getEntries();

	for(PxU32 i=0; i<numDirtyList; i++)
	{
		const PrunerHandle handle = compoundPairs[i].second;
		const PrunerCompoundId compoundId = compoundPairs[i].first;		

		// PT: we compute the new bounds and store them directly in the pruner structure to avoid copies. We delay the updateObjects() call
		// to take advantage of batching.
		PrunerPayloadData ppd;
		const PrunerPayload& pp = mPruner->getPayloadData(handle, compoundId, &ppd);

		computeBounds(*ppd.mBounds, adapter.getGeometry(pp), *ppd.mTransform, 0.0f, inflation);

		// A.B. not very effective, we might do better here
		mPruner->updateObjectAfterManualBoundsUpdates(compoundId, handle);
	}
		
	mDirtyList.clear();
}

void CompoundPrunerExt::addToDirtyList(PrunerCompoundId compoundId, PrunerHandle handle, const PxTransform& transform)
{
	if(mPruner)
		mPruner->setTransform(handle, compoundId, transform);

	mDirtyList.insert(CompoundPair(compoundId, handle));
}

void CompoundPrunerExt::removeFromDirtyList(PrunerCompoundId compoundId, PrunerHandle handle)
{
	mDirtyList.erase(CompoundPair(compoundId, handle));
}

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

#include "SqFactory.h"
#include "common/PxProfileZone.h"
#include "common/PxRenderBuffer.h"
#include "GuBVH.h"
#include "foundation/PxAlloca.h"
#include "PxSceneDesc.h"	// PT: for PxSceneLimits TODO: remove

namespace
{
	enum PxScenePrunerIndex
	{
		PX_SCENE_PRUNER_STATIC		= 0,
		PX_SCENE_PRUNER_DYNAMIC		= 1,
		PX_SCENE_COMPOUND_PRUNER	= 0xffffffff,
	};
}

PrunerManager::PrunerManager(	PxU64 contextID, Pruner* staticPruner, Pruner* dynamicPruner,
								PxU32 dynamicTreeRebuildRateHint, float inflation,
								const PxSceneLimits& limits, const Adapter& adapter) :
	mAdapter			(adapter),
	mContextID			(contextID),
	mStaticTimestamp	(0),
	mInflation			(inflation)
{
	mPrunerExt[PruningIndex::eSTATIC].init(staticPruner);
	mPrunerExt[PruningIndex::eDYNAMIC].init(dynamicPruner);

	setDynamicTreeRebuildRateHint(dynamicTreeRebuildRateHint);

	mCompoundPrunerExt.mPruner = createCompoundPruner(contextID);

	preallocate(PruningIndex::eSTATIC, limits.maxNbStaticShapes);
	preallocate(PruningIndex::eDYNAMIC, limits.maxNbDynamicShapes);
	preallocate(PxU32(PX_SCENE_COMPOUND_PRUNER), 32);

	mPrunerNeedsUpdating = false;
}

PrunerManager::~PrunerManager()
{
}

void PrunerManager::preallocate(PxU32 prunerIndex, PxU32 nbShapes)
{
	if(prunerIndex==PruningIndex::eSTATIC)
		mPrunerExt[PruningIndex::eSTATIC].preallocate(nbShapes);
	else if(prunerIndex==PruningIndex::eDYNAMIC)
		mPrunerExt[PruningIndex::eDYNAMIC].preallocate(nbShapes);
	else if(prunerIndex==PX_SCENE_COMPOUND_PRUNER)
		mCompoundPrunerExt.preallocate(nbShapes);
}

void PrunerManager::flushMemory()
{
	for(PxU32 i=0;i<PruningIndex::eCOUNT;i++)
		mPrunerExt[i].flushMemory();

	mCompoundPrunerExt.flushMemory();
}

PrunerData PrunerManager::addPrunerShape(const PrunerPayload& payload, bool dynamic, PrunerCompoundId compoundId, const PxBounds3& bounds, const PxTransform& transform, bool hasPruningStructure)
{
	mPrunerNeedsUpdating = true;

	const PxU32 index = PxU32(dynamic);

	if(!index)
		invalidateStaticTimestamp();

	PrunerHandle handle;
	if(compoundId == INVALID_COMPOUND_ID)
	{
		PX_ASSERT(mPrunerExt[index].pruner());
		mPrunerExt[index].pruner()->addObjects(&handle, &bounds, &payload, &transform, 1, hasPruningStructure);
		//mPrunerExt[index].growDirtyList(handle);
	}
	else
	{
		PX_ASSERT(mCompoundPrunerExt.pruner());
		mCompoundPrunerExt.pruner()->addObject(compoundId, handle, bounds, payload, transform);
	}

	return createPrunerData(index, handle);
}

void PrunerManager::removePrunerShape(PrunerCompoundId compoundId, PrunerData data, PrunerPayloadRemovalCallback* removalCallback)
{
	mPrunerNeedsUpdating = true;
	const PxU32 index = getPrunerIndex(data);
	const PrunerHandle handle = getPrunerHandle(data);

	if(!index)
		invalidateStaticTimestamp();

	if(compoundId == INVALID_COMPOUND_ID)
	{
		PX_ASSERT(mPrunerExt[index].pruner());

		mPrunerExt[index].removeFromDirtyList(handle);
		mPrunerExt[index].pruner()->removeObjects(&handle, 1, removalCallback);
	}
	else
	{
		mCompoundPrunerExt.removeFromDirtyList(compoundId, handle);
		mCompoundPrunerExt.pruner()->removeObject(compoundId, handle, removalCallback);
	}
}

void PrunerManager::markForUpdate(PrunerCompoundId compoundId, PrunerData data, const PxTransform& transform)
{ 
	mPrunerNeedsUpdating = true;
	const PxU32 index = getPrunerIndex(data);
	const PrunerHandle handle = getPrunerHandle(data);

	if(!index)
		invalidateStaticTimestamp();

	if(compoundId == INVALID_COMPOUND_ID)
		// PT: TODO: at this point do we still need a dirty list? we could just update the bounds directly?
		mPrunerExt[index].addToDirtyList(handle, index!=0, transform);
	else
		mCompoundPrunerExt.addToDirtyList(compoundId, handle, transform);
}

void PrunerManager::setDynamicTreeRebuildRateHint(PxU32 rebuildRateHint)
{
	mRebuildRateHint = rebuildRateHint;

	for(PxU32 i=0;i<PruningIndex::eCOUNT;i++)
	{
		Pruner* pruner = mPrunerExt[i].pruner();
		if(pruner && pruner->isDynamic())
			static_cast<DynamicPruner*>(pruner)->setRebuildRateHint(rebuildRateHint);
	}
}

void PrunerManager::afterSync(bool buildStep, bool commit)
{
	PX_PROFILE_ZONE("Sim.sceneQueryBuildStep", mContextID);

	if(!buildStep && !commit)
	{
		mPrunerNeedsUpdating = true;
		return;
	}

	// flush user modified objects
	flushShapes();

	for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
	{
		Pruner* pruner = mPrunerExt[i].pruner();
		if(pruner)
		{
			if(pruner->isDynamic())
				static_cast<DynamicPruner*>(pruner)->buildStep(true);

			if(commit)
				pruner->commit();
		}
	}

	mPrunerNeedsUpdating = !commit;
}

void PrunerManager::flushShapes()
{
	PX_PROFILE_ZONE("SceneQuery.flushShapes", mContextID);

	// must already have acquired writer lock here

	const float inflation = 1.0f + mInflation;

	bool mustInvalidateStaticTimestamp = false;
	for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
	{
		if(mPrunerExt[i].processDirtyList(i, mAdapter, inflation))
			mustInvalidateStaticTimestamp = true;
	}

	if(mustInvalidateStaticTimestamp)
		invalidateStaticTimestamp();

	mCompoundPrunerExt.flushShapes(mAdapter, inflation);
}

void PrunerManager::flushUpdates()
{
	PX_PROFILE_ZONE("SceneQuery.flushUpdates", mContextID);

	if(mPrunerNeedsUpdating)
	{
		// no need to take lock if manual sq update is enabled
		// as flushUpdates will only be called from NpScene::flushQueryUpdates()
		mSQLock.lock();

		if(mPrunerNeedsUpdating)
		{
			flushShapes();

			for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
				if(mPrunerExt[i].pruner())
					mPrunerExt[i].pruner()->commit();

			PxMemoryBarrier();
			mPrunerNeedsUpdating = false;
		}
		mSQLock.unlock();
	}
}

void PrunerManager::forceRebuildDynamicTree(PxU32 prunerIndex)
{
	PX_PROFILE_ZONE("SceneQuery.forceDynamicTreeRebuild", mContextID);

	PxMutex::ScopedLock lock(mSQLock);
	Pruner* pruner = mPrunerExt[prunerIndex].pruner();
	if(pruner && pruner->isDynamic())
	{
		static_cast<DynamicPruner*>(pruner)->purge();
		static_cast<DynamicPruner*>(pruner)->commit();
	}
}

void* PrunerManager::prepareSceneQueriesUpdate(PruningIndex::Enum index)
{
	bool retVal = false;
	Pruner* pruner = mPrunerExt[index].pruner();
	if(pruner && pruner->isDynamic())
		retVal = static_cast<DynamicPruner*>(pruner)->prepareBuild();

	return retVal ? pruner : NULL;
}

void PrunerManager::sceneQueryBuildStep(void* handle)
{
	PX_PROFILE_ZONE("SceneQuery.sceneQueryBuildStep", mContextID);

	Pruner* pruner = reinterpret_cast<Pruner*>(handle);
	if(pruner && pruner->isDynamic())
	{
		const bool buildFinished = static_cast<DynamicPruner*>(pruner)->buildStep(false);
		if(buildFinished)
			mPrunerNeedsUpdating = true;
	}
}

// PT: TODO: revisit this. Perhaps it should be the user's responsibility to call the pruner's
// visualize functions directly, when & how he wants.
void PrunerManager::visualize(PxU32 prunerIndex, PxRenderOutput& out) const
{
	if(prunerIndex==PX_SCENE_PRUNER_STATIC)
	{
		if(getPruner(PruningIndex::eSTATIC))
			getPruner(PruningIndex::eSTATIC)->visualize(out, SQ_DEBUG_VIZ_STATIC_COLOR, SQ_DEBUG_VIZ_STATIC_COLOR2);
	}
	else if(prunerIndex==PX_SCENE_PRUNER_DYNAMIC)
	{
		if(getPruner(PruningIndex::eDYNAMIC))
			getPruner(PruningIndex::eDYNAMIC)->visualize(out, SQ_DEBUG_VIZ_DYNAMIC_COLOR, SQ_DEBUG_VIZ_DYNAMIC_COLOR2);
	}
	else if(prunerIndex==PX_SCENE_COMPOUND_PRUNER)
	{
		const CompoundPruner* cp = mCompoundPrunerExt.pruner();
		if(cp)
			cp->visualizeEx(out, SQ_DEBUG_VIZ_COMPOUND_COLOR, true, true);
	}
}

void PrunerManager::shiftOrigin(const PxVec3& shift)
{
	for(PxU32 i=0; i<PruningIndex::eCOUNT; i++)
		mPrunerExt[i].pruner()->shiftOrigin(shift);

	mCompoundPrunerExt.pruner()->shiftOrigin(shift);
}

void PrunerManager::addCompoundShape(const PxBVH& pxbvh, PrunerCompoundId compoundId, const PxTransform& compoundTransform, PrunerData* prunerData, const PrunerPayload* payloads, const PxTransform* transforms, bool isDynamic)
{
	const BVH& bvh = static_cast<const BVH&>(pxbvh);
	const PxU32 nbShapes = bvh.Gu::BVH::getNbBounds();

	PX_ALLOCA(res, PrunerHandle, nbShapes);

	PX_ASSERT(mCompoundPrunerExt.mPruner);
	mCompoundPrunerExt.mPruner->addCompound(res, bvh, compoundId, compoundTransform, isDynamic, payloads, transforms);
	const PxU32 index = PxU32(isDynamic);
	if(!index)
		invalidateStaticTimestamp();

	for(PxU32 i = 0; i < nbShapes; i++)
		prunerData[i] = createPrunerData(index, res[i]);
}

void PrunerManager::updateCompoundActor(PrunerCompoundId compoundId, const PxTransform& compoundTransform)
{	
	PX_ASSERT(mCompoundPrunerExt.mPruner);
	const bool isDynamic = mCompoundPrunerExt.mPruner->updateCompound(compoundId, compoundTransform);
	if(!isDynamic)
		invalidateStaticTimestamp();
}

void PrunerManager::removeCompoundActor(PrunerCompoundId compoundId, PrunerPayloadRemovalCallback* removalCallback)
{
	PX_ASSERT(mCompoundPrunerExt.mPruner);
	const bool isDynamic = mCompoundPrunerExt.mPruner->removeCompound(compoundId, removalCallback);
	if(!isDynamic)
		invalidateStaticTimestamp();
}

void PrunerManager::sync(const PrunerHandle* handles, const PxU32* boundsIndices, const PxBounds3* bounds, const PxTransform32* transforms, PxU32 count, const PxBitMap& ignoredIndices)
{
	if(!count)
		return;

	Pruner* dynamicPruner = getPruner(PruningIndex::eDYNAMIC);
	if(!dynamicPruner)
		return;

	PxU32 startIndex = 0;
	PxU32 numIndices = count;

	// if shape sim map is not empty, parse the indices and skip update for the dirty one
	if(ignoredIndices.count())
	{
		// PT: I think this codepath was used with SCB / buffered changes, but it's not needed anymore
		numIndices = 0;

		for(PxU32 i=0; i<count; i++)
		{
//			if(ignoredIndices.test(boundsIndices[i]))
			if(ignoredIndices.boundedTest(boundsIndices[i]))
			{
				dynamicPruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
				numIndices = 0;
				startIndex = i + 1;
			}
			else
				numIndices++;
		}
		// PT: we fallback to the next line on purpose - no "else"
	}

	dynamicPruner->updateObjects(handles + startIndex, numIndices, mInflation, boundsIndices + startIndex, bounds, transforms);
}
