// © Copyright 2008 Vladimir Frolov, Moscow State University Graphics & Media Lab
#pragma once
#define RTLIB_GUARDIAN

#ifdef __CUDACC__ 
#ifdef __DEVICE_EMULATION__
	#undef ASSERT 
	#define ASSERT(_expression) \
	if(!(_expression)) { fprintf(stderr,"Assertion failed. File: %s, Line %d\n",__FILE__,__LINE__); \
	abort(); }
#endif
#endif

#define BLOCK_SIZE_X 8
#define BLOCK_SIZE_Y 8
#define WARP_SIZE 32

#define SORT_BLOCK_SIZE 256

#define HRT_RAY_MISS 0xFFFFFFFE
#define HRT_RAY_HIT 0xFFFFFFFF

#define SHORT_STACK_ELEMS 4
#define SHORT_STACK_SIZE SHORT_STACK_ELEMS*2



#ifndef MGML_GUARDIAN
	#include "../CSL/MGML.h"
#endif

using namespace MGML;

//#ifndef ACCEL_STRUCT_GUARDIAN
//  #include "AccelerationStructure.h" 
//#endif

//#ifndef BVH_GUARDIAN
//	#include "BVH.h"
//#endif

//#ifndef HIERARCHICAL_GRID_GUARDIAN
//	#include "HierarchicalGrid.h"
//#endif


const unsigned int OBJECT_LIST_CACHE_SIZE = 160; // по float2: 32 треугольника
const unsigned int BVH_CACHE_SIZE = 64;

namespace RAYTR
{

enum RAY_TYPES {EYE_RAY,
				        SHADOW_RAY,
				        REFLECTED_RAY,
				        REFRACTED_RAY};

enum ACCELERATION_STRUCRURES {ACCEL_STRUCT_KD_TREE, 
                              ACCEL_STRUCT_BVH,
                              ACCEL_STRUCT_HIERARHICAL_GRID};


// для CPU надо будет перегрузить operator= (быстрое копирование c помощью sse)
class Hit
{

public:

	inline universal_call unsigned char GetObjectType() const
		{ return object_type;}

	enum { TRIANGLE = MGML_MATH::TYPE_OF_TRIANGLE,
		   SPHERE   = MGML_MATH::TYPE_OF_SPHERE,
		   BOX      = MGML_MATH::TYPE_OF_BOX,
		   NONE};

	//universal_call Hit(){object_id = 0xFFFFFFFF;}

	vec3f	pos;
	vec3f	norm;
	vec2f t;		// texture coordinates
	unsigned char texture_id   : 8;
	unsigned char material_id : 8;
	unsigned char object_type : 8;
	unsigned int  object_id   : 32;

};


struct Lite_Hit
{
	inline universal_call void SetObjectId(unsigned int a_id)
	 { object_id = ( a_id & 0x3FFFFFFF) | (object_id & 0xC0000000); }

	inline universal_call unsigned int GetObjectId() const
	 { return object_id & 0x3FFFFFFF; }

	inline universal_call void SetObjectType(unsigned char a_type)
	 { object_id = ( ((a_type << 30) & 0xC0000000) | (object_id & 0x3FFFFFFF) ) ; }

	inline universal_call unsigned char GetObjectType() const
	 { return (object_id & 0xC0000000) >> 30; }

	enum { TRIANGLE = Hit::TRIANGLE,
		   SPHERE   = Hit::SPHERE,
		   BOX      = Hit::BOX,
		   NONE		= Hit::NONE};

  friend struct Lite_Hit2;

//protected:
	unsigned int  object_id; // we can use 2 bits of object_id to store object_type
};


/////////////////////////////////
struct __align__(8) Lite_Hit2 : public Lite_Hit
{

  inline universal_call Lite_Hit2& operator=(const Lite_Hit& rhs)
     { object_id = rhs.object_id; return*this; }
  //inline universal_call Lite_Hit2& operator=(const Lite_Hit2& rhs)
 // {
 //   object_id = rhs.object_id;
 //   t = rhs.t;
 // }

  float t;
};






/////////////////////////////////
struct __align__(8) BVH_Leaf 
{
  int   offset;
  float t;
};




/////////////////////////////////
struct __align__(8) RayDataToSort
{
	uint nodeOffset;
	uint oldOffset; // offset to screen pixel if screen is lenear buffer
};


#ifdef __CUDACC__
	typedef vec3f vec;
	typedef Ray3f Ray;
#else
	typedef vec4f vec;
	typedef Ray4f Ray;
#endif



static universal_call Ray EyeRay(int x,int y,int w,int h)
{
	Ray my_ray;
	float fov = 3.141592654f/(4.0f); // !!должно совпадать с fov в HAL

	my_ray.pos.M[0] = 0;
	my_ray.pos.M[1] = 0;
	my_ray.pos.M[2] = 0;

	my_ray.dir.M[0] = x - (w/2);
	my_ray.dir.M[1] = y - (h/2);
	my_ray.dir.M[2] = -(w/2.0f)/tanf(fov/2.0f);

#ifndef __CUDACC__
	my_ray.dir.M[3] = 1;
	my_ray.pos.M[3] = 1;
#endif	

	my_ray.dir.Normalize();

	return my_ray;
}






}

using namespace RAYTR;