#define MGML_GRAPHICS_GUARDIAN

#ifndef MGML_MATH_GUARDIAN
	#include "MGML_MATH.h"
#endif

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


//#ifndef KD_TREE_GUARDIAN
//	#include "kd_tree.h"
//#endif

//#include "MGML_MESH.h"

namespace MGML_GRAPHICS
{
 
template <int n_,class T> 
struct VERTEX;

template <int   n, 
		  class T, 
		  class INDEX> class TRIANGLE;


template <int   dim, 
		  class T,
		  class HAL> class MESH;

		  class D3D_HAL{};
		  class OpenGL_HAL{};

template <class T> struct OTC;

typedef MGML_MATH::VECTOR<3,unsigned char> RGB256;

template<int n>
inline universal_call RGB256 REAL_COLOR_TO_RGB256(const MGML_MATH::VECTOR<n,float>& real_color)
{
 //typedef st_assert<(n >= 3)> n_must_be_gt_3;

 float  r = real_color.x()*255;
 float  g = real_color.y()*255;
 float  b = real_color.z()*255;

 ASSERT(r < 256 && r >= 0);
 ASSERT(g < 256 && g >= 0);
 ASSERT(b < 256 && b >= 0);

 RGB256 res;
 res.set(r,g,b);

 return res;
}

template<int n>
inline universal_call MGML_MATH::VECTOR<n,float> RGB256_TO_REAL_COLOR(const RGB256& clr)
{
	MGML_MATH::VECTOR<n,float> res;
	res.x() = clr.x();
	res.y() = clr.y();
	res.z() = clr.z();
	
	res *= (1.0f/255.0f);

	return res;
}

inline universal_call UINT RGB256_TO_UINT32(const RGB256& clr)
{
	unsigned int result = 0;
	unsigned int red   = clr.x();
	unsigned int green = clr.y();
	unsigned int blue  = clr.z();

	result = red | (green << 8) | (blue << 16);

	return result;
}


inline universal_call MGML_MATH::VECTOR<4,float> UINT32_TO_REAL_COLOR(unsigned int clr)
{
	unsigned char red_b	  = (clr & 0x000000FF);
	unsigned char green_b = (clr & 0x0000FF00) >> 8;
	unsigned char blue_b  = (clr & 0x00FF0000) >> 16;

	float red	= (float)red_b;
	float green = (float)green_b;
	float blue	= (float)blue_b;

	float inv_255 = 1.0f/255.0f;

	MGML_MATH::VECTOR<4,float> res;
	res.set(inv_255*red,inv_255*green,inv_255*blue,1);

	return res;
}

template<int n>
inline universal_call unsigned int REAL_COLOR_TO_UNT32(const MGML_MATH::VECTOR<n,float>& real_color)
{
	float  r = real_color.x()*255;
	float  g = real_color.y()*255;
	float  b = real_color.z()*255;

	ASSERT(r < 256 && r >= 0);
	ASSERT(g < 256 && g >= 0);
	ASSERT(b < 256 && b >= 0);

	unsigned char red = (unsigned char)r, green = (unsigned char)g, blue = (unsigned char)b;
	return red | (green << 8) | (blue << 16);
}


//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////      VERTEX        ///////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////////////////////////
template <int n_,class T> 
struct VERTEX: public MGML_MATH::SSE_Aligned_Object
{
	MGML_MATH::VECTOR<n_,T> pos;
	MGML_MATH::VECTOR<n_,T> norm;
	MGML_MATH::VECTOR<2,T>  t;

	unsigned int material_id;
	unsigned char texture_id[4]; // multitexturing - max 4 textures

#ifndef __CUDACC__
	VERTEX()
	{
		for(int i=0;i<4;i++)
			texture_id[i] = 255;
	}
#endif

	universal_call VERTEX& operator*=(const MGML_MATH::MATRIX<n_,n_,T>& m)
	{
		pos  = pos*m;
		norm = norm*m;
		norm.Normalize();
		return *this;
	}

	friend universal_call VERTEX operator*(const MGML_MATH::MATRIX<n_,n_,T>& m,const VERTEX& v)
	{
		VERTEX res;
		res.pos  = m*v.pos;
		res.norm = m*v.norm;
		res.norm.Normalize();
		return res;
	}

	inline bool HasSameMaterialAndTextures(const VERTEX& rhs) const
	{
		bool textures_the_same = true;
		for(int i=0;i<4;i++)
			textures_the_same = textures_the_same && (texture_id[i] == rhs.texture_id[i]);

		return material_id == rhs.material_id && textures_the_same;
	}
	//перегрузить c помощью SSE для производительности на CPU:
	//VERTEX& operator=(VERTEX& v);
 
};

//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////      MATERIAL        /////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////////////////////////


template<int n>
struct MATERIAL : public MGML_MATH::Auto_Align<n,float>
{
	typedef MGML_MATH::VECTOR<n,float> vec;
public:

	universal_call float getReflection() const {return k_reflect;}
	universal_call float getRefraction() const {return k_refract;}

  universal_call vec getColor() const 
  {
    vec color = ka + kd + ks;
    color.Normalize();
    return color;
  }

	// phong model {
	vec  ka;			  // ambient
	vec  kd;			  // diffuse
	vec  ks;			  // specular
	float  power;		  // power of cos in phong model
	// } end phong model

	float  k_reflect;	  // reflections component
	float  k_refract;	  // refractive component
	float  n;			  // refractive exponent
	unsigned int BRDF_id; // BRDF index if BRDF used

	//перегрузить c помощью SSE для производительности на CPU:
	//VERTEX& operator=(VERTEX& v);
};

typedef MATERIAL<3> Material;


//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////      TRIANGLE        /////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////////////////////////

class Triangle: public MGML_MATH::SSE_Aligned_Object
{
public:
 
	Triangle(){}

	Triangle(const int a,
			 const int b,
			 const int c)
	{
		A = a;
		B = b;
		C = c;
	}

	union
	{
		int v[3];
		struct
		{
			int A,B,C;
		};
	};

	union
	{
		int  attr32pack;
		struct
		{
			unsigned char texture_id[4];
		};
	};
	
 	
};




template<int n,class T>
struct Light_Source_Struct: public MGML_MATH::SSE_Aligned_Object
{
	typedef MGML_MATH::VECTOR<n,T> vec;

	universal_call Light_Source_Struct()
	{
		Intensity = 1.0f;
		real_color.set(1,1,1,1);
	}
	universal_call ~Light_Source_Struct(){}

	universal_call void setColor(unsigned char r,unsigned char g,unsigned char b)
	{
		this->real_color.set(r,g,b);
		vec tv(r,g,b,0);
		tv.Normalize();
		real_color = tv;
	}

	universal_call vec	  getColor() const {return real_color;}
	universal_call T      getIntensity() const {return Intensity;}

	universal_call T	  get_kc() const {return _kc;}
	universal_call T	  get_kl() const {return _kl;}
	universal_call T	  get_kq() const {return _kq;}

	universal_call T&	  kc() {return _kc;}
	universal_call T&	  kl() {return _kl;}
	universal_call T&	  kq() {return _kq;}

	universal_call vec& color() {return real_color;}
	universal_call T&   intensity() {return Intensity;}

  universal_call T GetAverageAmbientLevel() const {return 0.15f;}
  universal_call T GetAverageDiffuseLevel() const {return 0.80f;}
  universal_call T GetAverageSpecularLevel() const {return 1.0f;}

protected:
	
	vec	real_color;
	T	Intensity,_kc,_kl,_kq;
};


template<int n,class T>
struct Point_Light: public Light_Source_Struct<n,T>
{
	typedef MGML_MATH::VECTOR<n,T> vec;

	Point_Light(){ this->_kc = 1.0; this->_kl = 0.01; this->_kq = 0.005;}
	//Point_Light(const vec& v){kc = 1; kl = 0.05; kq = 0.01; _pos = v; }


	universal_call void       setPos(const vec& p){_pos = p;}
	universal_call const vec& getPos() const {return _pos;}
	universal_call vec& pos() {return _pos;}
	universal_call const vec& pos() const {return _pos;}


protected:
	vec _pos;
};


template<int n,class T>
struct Infinite_Light: public Light_Source_Struct<n,T>
{
	typedef MGML_MATH::VECTOR<n,T> vec;

    // use _pos to store normal
	universal_call void       setNorm(const vec& p){_norm = p;}
	universal_call const vec& getNorm() const {return _norm;}
protected:
	MGML_MATH::VECTOR<n,T> _norm;
};


template<int n,class T>
struct Spot_Light: public Point_Light<n,T>
{
	typedef MGML_MATH::VECTOR<n,T> vec;

protected:
    MGML_MATH::VECTOR<n,T> _norm;
    T ang1;
	T ang2;
};

template<int n,class T>
struct Sphere_Light: public Point_Light<n,T>
{

protected:
    MGML_MATH::SPHERE<n,T> _sphere; 
};

template<int n,class T>
struct Flat_Light: public Light_Source_Struct<n,T>
{

protected:
    MGML_MATH::VECTOR<n,T> _pos;
	MGML_MATH::VECTOR<n,T> _norm;

	MGML_MATH::VECTOR<2,T> _size;
	VERTEX<n,T>			   _vert[4];
};






};

