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

#ifndef __CUDACC__

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

  using MGML::vec3f;
  using MGML::vec4f;
  using MGML::AABB3f;
  using MGML::AABB4f;
  using MGML::PackedSphere3f;
  using MGML::to_vec3f;


  #include <memory>
  #include <list>
  #include <vector>
  #include <map>
  #include <algorithm>

  using std::list;
  using std::vector;
  using std::map;
  using namespace MGML_ERROR;
#endif

#ifndef MATERIAL_GUARDIAN
  #include "Material.h"
#endif



namespace RAYTR
{


// структура будет такая:
// первые 8 байт - сама структрута ObjectList
// далее по 40 байт на каждый треугольник и по 24 байт на сферу
// сначала идут треугольники, потом сферы. Читаем текстурными выборками по 2 float (или uint)
class ObjectList
{

public:

	enum { TRIANGLE = 0,
		     SPHERE   = 1,
		     BOX      = 2};

  struct Triangle
  {
    float3 v[3];
    unsigned int selfIndex;
  };

#ifndef __CUDACC__
 
  struct Sphere
  {
    PackedSphere3f sph;
    unsigned int selfIndex;
    unsigned int dummy;
  };
#else

  struct Sphere
  {
    float3 pos;
    float r;
    unsigned int selfIndex;
    unsigned int dummy;
  };
#endif


#ifndef __CUDACC__

  static AABB3f GetBoundingBox(const Triangle* in_t);
  static AABB3f GetBoundingBox(const Sphere* in_s);

  inline universal_call Triangle* GetTriangles() const 
  { 
    return  (Triangle*)( ((char*)this) + sizeof(ObjectList));
  }

  inline universal_call const Sphere* GetSpheres() const 
  { 
    return  (const Sphere*) ( 
      (char*)this + 
      sizeof(ObjectList) + 
      m_triangleCount*sizeof(Triangle) 
      );
  }
#endif

	inline universal_call int GetNumTriangles() const 
		{ return m_triangleCount; }

	inline universal_call void SetNumTriangles(unsigned int num) 
		{ m_triangleCount = (unsigned short)num;}

  inline universal_call int GetNumSpheres() const 
		{return m_spheresCount;}

	inline universal_call void SetNumSpheres(unsigned int num) 
		{ m_spheresCount = (unsigned short)num;}

  inline universal_call int GetNumPrimitives() const
   {return GetNumTriangles() + GetNumSpheres(); }


//protected:

	unsigned short m_triangleCount;
	unsigned short m_spheresCount;
  unsigned int dummy;
};




#ifndef __CUDACC__


void DrawBox(const AABB4f& box);
void DrawBox(const AABB3f& box);


class AccelerationStructure : public MGML_MATH::SSE_Aligned_Object
{

protected:
  typedef MGML_MATH::SPHERE<4,float>      Sphere;
	typedef MGML_GRAPHICS::VERTEX<4,float>  Vertex;
  typedef MGML_MATH::TRIANGLE<4,float>		Triangle;
	typedef MGML_MATH::AABB<4,float>			  AABB;
  typedef RAYTR::Material                 Material;

public:

  AccelerationStructure();
  virtual ~AccelerationStructure();

  virtual const AABB4f& GetBindingBox() const;

  virtual const char*	GetObjData() const;
	virtual unsigned int GetObjDataSize() const;

  virtual const ObjectList*	GetObjListByOffsetInBytes(uint offset) const;

  virtual void SetSpheres(const Sphere* in_sph,  uint sph_count);
	virtual void SetVertices(const Vertex* in_vert, uint vert_count);
	virtual void SetIndices(const uint* in_ind, uint ind_count);
  virtual void SetMaterials(const Material* a_mat, int a_N);
	
  virtual void Build() = 0;
  virtual void Draw() = 0;

protected:
  AccelerationStructure(const AccelerationStructure& rhs) {}
  AccelerationStructure& operator=(const AccelerationStructure& rhs) { return *this; }

  enum {MAX_PRIMS_IN_LEAF = 65535};

  struct VPrimitive: public MGML_MATH::SSE_Aligned_Object
  {
    inline AABB4f& Box() {return m_box;}
    inline const AABB4f& Box() const {return m_box;}

    virtual int GetType() const = 0;
		virtual int SizeInBytes() const = 0;
    virtual bool IntersectAABB(const AABB4f& b) const = 0;
    virtual void IncludeInAABB(AABB4f& b) const = 0;
    virtual bool HaveDiffuseMaterial() const = 0;

    inline int GetIndex() const {return m_primitiveIndex;}

    inline float GetMaxCoord(int axis) const {return m_box.vmax[axis];}
		inline float GetMinCoord(int axis) const {return m_box.vmin[axis];}
    inline vec4f pos() const {return m_box.center();}
    
    uint m_primitiveIndex;
    AABB4f m_box;
    AccelerationStructure* thisAccelStruct;
  };

  struct VTriangle : public VPrimitive
  {
    VTriangle(int a_id, AccelerationStructure* t);

    int GetType() const;
    int SizeInBytes() const;
    bool IntersectAABB(const AABB4f& b) const;
    void IncludeInAABB(AABB4f& b) const;
    bool HaveDiffuseMaterial() const;
  };

  struct VSphere : public VPrimitive
  {
    VSphere(int a_id, AccelerationStructure* t);

    int GetType() const;
    int SizeInBytes() const;
    bool IntersectAABB(const AABB4f& b) const;
    void IncludeInAABB(AABB4f& b) const;
    bool HaveDiffuseMaterial() const;
  };

  typedef std::vector<VPrimitive*> PrimitiveList;

  virtual int PushListInObjectListBuffer(const PrimitiveList& plist);
  virtual AABB4f CalcBindingBox(const PrimitiveList& plist);

  AABB m_bBox;
  
  char* m_objListData;
  uint m_objListDataTop;
  uint m_objListDataSize;

  const Sphere* m_spheres; 
	uint  m_numSph;
	
	const Vertex* m_vert;
	uint  m_numVert;

	const uint* m_index;
	uint  m_numInd;

  const Material* m_materials;
  uint m_numMat;

  struct StatInfo
	{
		StatInfo();

    virtual void Clear();
    virtual void Collect() = 0;
    virtual void Print(std::ostream& out);

		int m_emptyNodes;
		int m_totalNodes;
		int m_leafes;
		float m_avgPrimInLeaf;

		int m_notEmptyLeafesNum;
		int m_maxDeep;
		int m_maxPrimsInLeaf;
    int m_objectListMemory;
	};

  friend struct StatInfo;

};







#endif


};

