#ifndef __RAY_CASTER_H__
#define __RAY_CASTER_H__

#include "MaterialCache.h"
#include "RayStream.h"
#include "StaticStack.h"

#define USE_TOP_SHADOW_PACKET 1
#define USE_BOTTOM_SHADOW_PACKET 1

#define USE_REORDERING 1

namespace VRUT {

	class BVHBottom;
	class BVHTop;

	class RayCaster
	{
	public:
		struct BVHTraversalInfo
		{
			unsigned int nodeIndex;
			unsigned int numberOfRays;
			unsigned int numberOfPackets;
			BVHTraversalInfo(void) : nodeIndex(0), numberOfRays(0), numberOfPackets(0) {}
			BVHTraversalInfo(unsigned int nodeIndex, unsigned int numberOfRays, unsigned int numberOfPackets) 
				: nodeIndex(nodeIndex), numberOfRays(numberOfRays), numberOfPackets(numberOfPackets) {}
		};

		typedef StaticStack<RayCaster::BVHTraversalInfo> TraversalStack;
		typedef StaticStack<const AbstractBVH::BVHNode*> SingleTraversalStack;

		struct Buffers
		{
			TraversalStack topBundleStacks;
			SingleTraversalStack topPacketStack;
			TraversalStack bottomBundleStack;
			SingleTraversalStack bottomPacketStack;
		};

	private:
		bool topCulling;
		bool bottomCulling;

		unsigned int GetMaxExtentAxis(const AABB & box)
		{
			Vector3 d = box.MaxBound - box.MinBound;
			if (d.x < d.y)
			{
				if (d.z < d.y)
					return 1;
				else
					return 2;
			}
			if (d.z < d.x)
				return 0;
			return 2;
		}

		void CastRayPacket(RayStream & rayStream, const BVHBottom & bvh, unsigned int nodeIndex, unsigned int octant, RayCaster::Buffers &buffer);
		void CastRayPacketShadow(RayStream & rayStream, const BVHBottom & bvh, unsigned int nodeIndex, unsigned int octant, RayCaster::Buffers &buffer);
		void CastRayPacket(RayStream & rayStream, const BVHTop & bvh, unsigned int nodeIndex, unsigned int octant, RayCaster::Buffers &buffer);
		void CastRayPacketShadow(RayStream & rayStream, const BVHTop & bvh, MaterialCache & materialCache, Vector4 lightColors[], float rayOffset, unsigned int nodeIndex, unsigned int octant,
				RayCaster::Buffers &buffer);
		void CastRayBundle(RayStream & rayStream, const BVHBottom & bvh, unsigned int octant, RayCaster::Buffers &buffer);
		void CastRayBundleShadow(RayStream & rayStream, const BVHBottom & bvh, unsigned int octant, RayCaster::Buffers &buffer);
		void CastRayBundle(RayStream & rayStream, const BVHBottom & bvh, RayCaster::Buffers &buffer);
		void CastRayBundleShadow(RayStream & rayStream, const BVHBottom & bvh, RayCaster::Buffers &buffer);
		void CastRayBundle(RayStream & rayStream, const BVHTop & bvh, unsigned int octant, RayCaster::Buffers &buffer);
		void CastRayBundleShadow(RayStream & rayStream, const BVHTop & bvh, MaterialCache & materialCache, Vector4 lightColors[], float rayOffset, unsigned int octant, RayCaster::Buffers &buffer);

	public:
		RayCaster(void);
		~RayCaster(void);
		void CastRayBundle(RayStream & rayStream, const BVHTop & bvh, RayCaster::Buffers &buffer);
		void CastRayBundleShadow(RayStream & rayStream, const BVHTop & bvh, MaterialCache & materialCache, Vector4 lightColors[], float rayOffset, RayCaster::Buffers &buffer);
		void SetTopCulling(bool topCulling);
		bool GetTopCulling(void);
		void SetBottomCulling(bool bottomCulling);
		bool GetBottomCulling(void);
	};
}

#endif /* __RAY_CASTER_H__ */
