#pragma once

#include <float.h>												// FLT_MAX
#include "RasterCube.h"										// CPossibleIntersectionSink<>

#define ALREADY_TESTED_ARRARY_SIZE	1024



template <class TTri>
class CEveryObjectOnlyOnce :public CPossibleIntersectionSink<TTri *>
{
public:

	//! constructor
	//! /param invFrom
	//! /param invDir
	//! /param infRayLength
	//! /param inpIgnore for bias free rayshooting from object surface, may be 0
	CEveryObjectOnlyOnce( const Vec3 &invFrom, const Vec3 &invDir, float infRayLength, const TTri *inpIgnore )
	{
		m_vFrom=invFrom;
		m_vDir=invDir;
		m_fMaxRayLength=infRayLength;

		if(inpIgnore)
		{
			m_dwAlreadyTested=1;
			m_arrAlreadyTested[0]=inpIgnore;
		}
		else
		{
			m_dwAlreadyTested=0;
		}
	}

protected:	// --------------------------------------------

	Vec3							m_vFrom;																					//!< position in object space 
	Vec3							m_vDir;																						//!< direction in object space
	const TTri *			m_arrAlreadyTested[ALREADY_TESTED_ARRARY_SIZE];		//!< [0..ALREADY_TESTED_ARRARY_SIZE-1], this is faster than a set<> at least for typical amounts
	DWORD							m_dwAlreadyTested;																//!< 0..ALREADY_TESTED_ARRARY_SIZE
	float							m_fMaxRayLength;																	//!<

	bool InsertAlreadyTested( TTri * &inObject )
	{
		const TTri * *ptr=m_arrAlreadyTested;

		for(DWORD i=0;i<m_dwAlreadyTested;i++)
			if(*ptr++==inObject)
				return true;

		if(m_dwAlreadyTested<ALREADY_TESTED_ARRARY_SIZE-1) m_arrAlreadyTested[m_dwAlreadyTested++]=inObject;
//			else assert(0);		// ALREADY_TESTED_ARRARY_SIZE is not big enougth

		return false;
	}
};



// find all intersection within the range
template <class TTri>
class CEveryObjectOnlyOnceSTLVector :public CEveryObjectOnlyOnce<TTri>
{
public:

	//! constructor
	//! /param invFrom
	//! /param invDir
	//! /param infRayLength
	//! /param inpIgnore for bias free rayshooting from object surface, may be 0
	CEveryObjectOnlyOnceSTLVector( const Vec3 &invFrom, const Vec3 &invDir, float infRayLength,
		std::vector<CIntersInfo<TTri> > &inOutput, const TTri *inpIgnore )
		:CEveryObjectOnlyOnce<TTri>(invFrom,invDir,infRayLength,inpIgnore), m_FoundIntersections(inOutput)
	{
	}

	//! /param inObject 
	//! /param inoutfRayMaxDist in and out value for max. shooting distance or FLT_MAX, if you found a interesection you may update this because further intersections are no longer interesting
	//! /return true=do further processiung, false=stop I found what I need (e.g. any intersection for shadow testing)
	bool ReturnElement( TTri * &inObject, float &inoutfRayMaxDist )
	{
		if(!InsertAlreadyTested(inObject))
		{
			float fPos=inObject->CalcIntersectionFromDir(m_vFrom,m_vDir);

			if(fPos!=FLT_MAX)
			{
				assert(fPos>=0);

				if(fPos<m_fMaxRayLength)
				{
					CIntersInfo<TTri> inters;

					inters.m_fDist=fPos;
					inters.m_Point=m_vFrom+inters.m_fDist*m_vDir;
					inters.m_pTri=inObject;

					m_FoundIntersections.push_back(inters);
				}

/*
				// take the nearest intersection
				if(fPos<m_fMaxRayLength)
				{
					m_FoundIntersection.m_SubObjectNo=inObject;

					inoutfRayMaxDist=m_fMaxRayLength;
				}
*/
			}
		}
		return true;	// do further intersections
	}

private:	// --------------------------------------------

	std::vector<CIntersInfo<TTri> > &			m_FoundIntersections;															//!< reference to the output STL vector - optimizable
};






//! find if there is a intersection in the range
template <class TTri>
class CEveryObjectOnlyOnceBool :public CEveryObjectOnlyOnce<TTri>
{
public:
	//! constructor
	//! /param invFrom start position in worldspace
	//! /param invDir
	//! /param infRayLength
	//! /param inpIgnore for bias free rayshooting from object surface, may be 0
	CEveryObjectOnlyOnceBool( const Vec3 &invFrom, const Vec3 &invDir, float infRayLength, const TTri *inpIgnore )
		:CEveryObjectOnlyOnce<TTri>(invFrom,invDir,infRayLength,inpIgnore)
	{
		m_bResult=false;
	}

	//! /param inObject 
	//! /param inoutfRayMaxDist in and out value for max. shooting distance or FLT_MAX, if you found a interesection you may update this because further intersections are no longer interesting
	//! /return true=do further processiung, false=stop I found what I need (e.g. any intersection for shadow testing)
	bool ReturnElement( TTri * &inObject, float &inoutfRayMaxDist )
	{
		if(!InsertAlreadyTested(inObject))
		{
			float fPos=inObject->CalcIntersectionFromDir(m_vFrom,m_vDir);

			if(fPos!=FLT_MAX)
			{
				assert(fPos>=0);

				if(fPos<m_fMaxRayLength)
				{
					m_bResult=true;
					return false;		// no further intersection, one hit is enougth
				}
			}
		}
		
		return true;	// try further intersections, this one was no real one
	}

	//! /return reference to the STL list of all found intersections
	bool GetResult()
	{
		return m_bResult;
	}

private: // --------------------------------------------

	bool							m_bResult;																	//!< true=there was a hit, false otherwise
};






template <class TTri>
class CEveryObjectDebug :public CPossibleIntersectionSink<TTri *>
{
public:

	//! constructor
	//! /param invFrom
	//! /param invDir
	//! /param infRayLength
	//! /param inpTri, may not be 0
	CEveryObjectDebug( TTri *inpTri )
	{
		assert(inpTri);

		m_pTriPointer=inpTri;
		m_bFoundInThisBucket=false;
		m_dwBucketsOk=0;
		m_dwBucketsFailed=0;
	}

	//! /param inObject 
	//! /param inoutfRayMaxDist in and out value for max. shooting distance or FLT_MAX, if you found a interesection you may update this because further intersections are no longer interesting
	//! /return true=do further processiung, false=stop I found what I need (e.g. any intersection for shadow testing)
	bool ReturnElement( TTri * &inObject, float &inoutfRayMaxDist )
	{
		if(inObject==m_pTriPointer)m_bFoundInThisBucket=true;

		return(true);	// try further intersections, this one was no real one
	}

	void EndReturningBucket()
	{
		if(m_bFoundInThisBucket)
			m_dwBucketsOk++;
		 else 
			m_dwBucketsFailed++;

		m_bFoundInThisBucket=false;
	}

	// --------------------------------------------

	DWORD							m_dwBucketsOk;																		//!<
	DWORD							m_dwBucketsFailed;																//!<

protected: // --------------------------------------------

	TTri *						m_pTriPointer;																		//!< triangle that has to be in every bucket on the way of the line
	bool							m_bFoundInThisBucket;															//!<
};
