//
// 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-2019 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 SQ_PRUNER_H
#define SQ_PRUNER_H

#include "foundation/PxBounds3.h"
#include "geometry/PxGeometry.h"
#include "PxQueryReport.h"
#include "PxQueryFiltering.h"
#include "PsUserAllocated.h"
#include "SqPruningStructure.h"
#include "GuSphere.h"
#include "GuBox.h"
#include "GuCapsule.h"

namespace physx
{	
	namespace Gu
	{
		class ShapeData;
		class BVHStructure;
	}
}

namespace physx
{	
	namespace Cm
	{
		class RenderOutput;
	}

namespace Sq
{

typedef PxU32 PrunerHandle;
typedef PxU32 PrunerCompoundId;

static const PrunerHandle INVALID_PRUNERHANDLE = 0xFFffFFff;
static const PxReal SQ_PRUNER_INFLATION = 1.01f; // pruner test shape inflation (not narrow phase shape)

struct PrunerPayload
{
	size_t data[2];

	PX_FORCE_INLINE	bool operator == (const PrunerPayload& other) const
	{
		return (data[0] == other.data[0]) && (data[1] == other.data[1]);
	}
};

struct PrunerCallback
{
	virtual PxAgain invoke(PxReal& distance, const PrunerPayload& payload) = 0;
    virtual ~PrunerCallback() {}
};

class Pruner : public Ps::UserAllocated
{
public:

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	\brief		Adds objects to the pruner.
	 *	\param		results				[out]	an array for resulting handles
	 *	\param		bounds				[in]	an array of bounds. These bounds are used as-is so they should be pre-inflated if inflation is needed.
	 *	\param		userData			[in]	an array of object data
	 *	\param		count				[in]	the number of objects in the arrays
	 *	\param		hasPruningStructure [in]	if added objects have pruning structure. The structure will be merged later, adding the objects will not invalidate the pruner. 
	 *
	 *	\return		true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE.
	 *
	 *  Handles are usable as indices. Each handle is either be a recycled handle returned by the client via removeObjects(),
	 *  or a fresh handle that is either zero, or one greater than the last fresh handle returned.
	 *
	 *	Objects and bounds in the arrays have the same number of elements and ordering. 
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool						addObjects(PrunerHandle* results, const PxBounds3* bounds, const PrunerPayload* userData, PxU32 count, bool hasPruningStructure) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Removes objects from the pruner.
	 *	\param		handles		[in]	the objects to remove
	 *	\param		count		[in]	the number of objects to remove
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						removeObjects(const PrunerHandle* handles, PxU32 count) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Updates objects after manually updating their bounds via "getPayload" calls.
	 *	\param		handles		[in]	the objects to update
	 *	\param		count		[in]	the number of objects to update
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						updateObjectsAfterManualBoundsUpdates(const PrunerHandle* handles, PxU32 count) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Updates objects with new indexed bounds.
	 *	\param		handles		[in]	the objects to update
	 *	\param		indices		[in]	the indices of the bounds in the bounds array
	 *	\param		newBounds	[in]	updated bounds array
	 *	\param		count		[in]	the number of objects to update
	 *
	 *	\warning	THESE BOUNDS WILL BE INFLATED ON-THE-FLY. So this is inconsistent with the "addObjects" behavior.
	 *	\warning	The inflation value is hardcoded in Sq::inflateBounds().
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						updateObjectsAndInflateBounds(const PrunerHandle* handles, const PxU32* indices, const PxBounds3* newBounds, PxU32 count) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Makes the queries consistent with previous changes.
	 *	This function must be called before starting queries on an updated Pruner and assert otherwise.
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						commit() = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Merges pruning structure to current pruner, parameters may differ for each pruner implementation
	 *	\param		mergeParams	[in]	Pruning structure to merge.	 
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						merge(const void* mergeParams) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Query functions
	 *  
	 *	Note: return value may disappear if PrunerCallback contains the necessary information
	 *			currently it is still used for the dynamic pruner internally (to decide if added objects must be queried)
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual	PxAgain						raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const = 0;
	virtual	PxAgain						overlap(const Gu::ShapeData& queryVolume, PrunerCallback&) const = 0;
	virtual	PxAgain						sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&) const = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Retrieve the object data associated with the handle
	 *	
	 *	\param	handle		The handle returned by addObjects()
	 *
	 *	\return				A reference to the object data
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual const PrunerPayload&		getPayload(PrunerHandle handle) const = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Retrieve the object data associated with the handle, plus the destination address for its matrix. The user is then expected to write the new AABB there.
	 *	
	 *	\param	handle		[in] The handle returned by addObjects()
	 *	\param	bounds		[out] destination address for this object's bounds
	 *
	 *	\return				A reference to the object data
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual const PrunerPayload&		getPayload(PrunerHandle handle, PxBounds3*& bounds) const = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Preallocate space 
	 *	
	 *	\param	entries		the number of entries to preallocate space for
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						preallocate(PxU32 entries) = 0;

	// shift the origin of the pruner objects
	virtual void						shiftOrigin(const PxVec3& shift) = 0;

	virtual								~Pruner() {}

	// additional 'internal' interface		
	virtual	void						visualize(Cm::RenderOutput&, PxU32) const {}
};

//////////////////////////////////////////////////////////////////////////
/**
*	Pruner building accel structure over time base class
*/
//////////////////////////////////////////////////////////////////////////
class IncrementalPruner: public Pruner
{
public:
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	gets rid of internal accel struct.	 
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						purge() = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 * sets the rebuild hint rate used for step building the accel structure.
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						setRebuildRateHint(PxU32 nbStepsForRebuild) = 0;	

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/** 
	 * Steps the accel structure build.
	 * synchronousCall specifies if initialization can happen. It should not initialize build when called from a different thread
	 * returns true if finished
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool						buildStep(bool synchronousCall = true) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/** 
	 * Prepares new tree build
	 * returns true if new tree is needed
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool						prepareBuild() = 0;	
};


//////////////////////////////////////////////////////////////////////////
// Compound flag to use for static/dynamic filtering atm
struct CompoundFlag
{
	enum Enum
	{
		STATIC_COMPOUND = (1<<0),
		DYNAMIC_COMPOUND = (1<<1)
	};
};

PX_COMPILE_TIME_ASSERT(PxQueryFlag::eSTATIC & CompoundFlag::STATIC_COMPOUND);
PX_COMPILE_TIME_ASSERT(PxQueryFlag::eDYNAMIC & CompoundFlag::DYNAMIC_COMPOUND);

//////////////////////////////////////////////////////////////////////////
/**
*	Pruner holding compound objects
*/
//////////////////////////////////////////////////////////////////////////
class CompoundPruner: public Ps::UserAllocated
{
public:
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	\brief		Adds compound to the pruner.
	 *	\param		results				[out]	an array for resulting handles
	 *	\param		bvhStructure		[in]	BVH structure holding bounds and BVH.
	 *	\param		compoundId			[in]	compound id
	 *	\param		transform			[in]	compound transform
	 *	\param		userData			[in]	an array of object data
	 *
	 *	\return		true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE.
	 *
	 *  Handles are usable as indices. Each handle is either be a recycled handle returned by the client via removeObjects(),
	 *  or a fresh handle that is either zero, or one greater than the last fresh handle returned.
	 *
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool						addCompound(PrunerHandle* results, const Gu::BVHStructure& bvhStructure, PrunerCompoundId compoundId, const PxTransform& transform, CompoundFlag::Enum flags, const PrunerPayload* userData) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Removes compound from the pruner.
	 *	\param		compoundId			[in]	compound to remove
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						removeCompound(PrunerCompoundId compoundId) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Updates compound object
	 *	\param		compoundId			[in]	compound to update
	 *	\param		transform			[in]	compound transformation
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						updateCompound(PrunerCompoundId compoundId, const PxTransform& transform) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Updates object after manually updating their bounds via "getPayload" calls.
	 *	\param		compoundId	[in]	compound that the object belongs to
	 *	\param		handle		[in]	the object to update
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						updateObjectAfterManualBoundsUpdates(PrunerCompoundId compoundId, const PrunerHandle handle) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Removes object from compound pruner.
	 *	\param		compoundId	[in]	compound that the object belongs to	 
	 *	\param		handle		[in]	the object to remove
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void						removeObject(PrunerCompoundId compoundId, const PrunerHandle handle) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	\brief		Adds object to the pruner.
	 *	\param		compoundId	[in]	compound that the object belongs to
	 *	\param		result				[out]	an array for resulting handles
	 *	\param		bounds				[in]	an array of bounds. These bounds are used as-is so they should be pre-inflated if inflation is needed.
	 *	\param		userData			[in]	an array of object data
	 *
	 *	\return		true if success, false if internal allocation failed. The first failing add results in a INVALID_PRUNERHANDLE.
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool						addObject(PrunerCompoundId compoundId, PrunerHandle& result, const PxBounds3& bounds, const PrunerPayload userData) = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Query functions
	 *  
	 *	Note: return value may disappear if PrunerCallback contains the necessary information
	 *			currently it is still used for the dynamic pruner internally (to decide if added objects must be queried)
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual	PxAgain						raycast(const PxVec3& origin, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const = 0;
	virtual	PxAgain						overlap(const Gu::ShapeData& queryVolume, PrunerCallback&, PxQueryFlags flags) const = 0;
	virtual	PxAgain						sweep(const Gu::ShapeData& queryVolume, const PxVec3& unitDir, PxReal& inOutDistance, PrunerCallback&, PxQueryFlags flags) const = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Retrieve the object data associated with the handle
	 *	
	 *	\param	handle		[in] The handle returned by addObjects()
	 *  \param  compoundId  [in] The compound id
	 *
	 *	\return				A reference to the object data
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual const PrunerPayload&		getPayload(PrunerHandle handle, PrunerCompoundId compoundId) const = 0;

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/**
	 *	Retrieve the object data associated with the handle, plus the destination address for its matrix. The user is then expected to write the new AABB there.
	 *	
	 *	\param	handle		[in] The handle returned by addObjects()
	 *  \param  compoundId  [in] The compound id
	 *	\param	bounds		[out] destination address for this object's bounds
	 *
	 *	\return				A reference to the object data
	 */
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual const PrunerPayload&		getPayload(PrunerHandle handle, PrunerCompoundId compoundId, PxBounds3*& bounds) const = 0;


	// shift the origin of the pruner objects
	virtual void						shiftOrigin(const PxVec3& shift) = 0;

	virtual								~CompoundPruner() {}

	// additional 'internal' interface		
	virtual	void						visualize(Cm::RenderOutput&, PxU32) const {}

};


//////////////////////////////////////////////////////////////////////////
/**
*	Creates AABBPruner
*/
//////////////////////////////////////////////////////////////////////////
IncrementalPruner* createAABBPruner(bool incrementalRebuild);

}

}

#endif // SQ_PRUNER_H