#ifndef __BVH1_H__
#define __BVH1_H__

#include "common.h"
#include "geometry.h"

#include <algorithm>

#define USE_TRIANGLEDATA 0

#define CT 3.0f
#define CI 2.0f

namespace VRUT
{

	/// BVH data structure.
	class BVH1
	{

	public:

		/// Point in 3d stored aligned for use SSE intrinsics.
		union Point
		{
			/// Array of coordinates.
			float v[4]; 
			/// Coordinate in format for SSE intrinsics.
			__m128 _p;
		};

		/// Structure of node in BVH.
		struct BVHNode
		{
			/// Minimal AABB coordinate.
			VRUT_ALIGNED(16) Point min; 
			/// Maximal AABB coordinate.
			VRUT_ALIGNED(16) Point max; 
			/// An offset to triangle array.
			int offset; 
			/// Count of triangles or index to node array.
			unsigned int t;
		};

	private:
		
		/// Array of BVH nodes.
		BVHNode * nodes;

		/// Number of nodes.
		int numberOfNodes;

		/// Vector of triangles.
#if USE_TRIANGLEDATA
		std::vector<TriangleData> triangles;
#else
		std::vector<TriangleData> * triangles;
		struct TrianglesCol {
			Vector3 v2, e1, e2;
		};
		std::vector<TrianglesCol> trianglesCol;
		struct TrianglesNorm {
			Vector3 n1, n2, n3;
		};
		std::vector<TrianglesNorm> trianglesNorm;
		struct TrianglesTex {
			Vector2 texCoord1, texCoord2, texCoord3;
		};
		std::vector<TrianglesTex> trianglesTex;
#endif

		/// Return a surface area of a give node.
		float GetSurafceArea(const BVHNode & node);

		/// Returns cost of subtree with a given root index.
		float GetCost(int index);

	public:

		/// Constructor, Nmax is suggested number of triangles in leaf.
		BVH1(void);

		/// Destructor, free all used resources.
		~BVH1(void);

		/// Clears.
		void Clear(void);

		/// Return nodes.
		const BVHNode * GetNodes(void);

		/// Returns cost of the BVH.
		float GetCost(void);

		unsigned int maxLeaf() {
			unsigned int c = 0;
			for (int i = 0; i < numberOfNodes; ++i) {
				if (nodes[i].offset >= 0)
					c = std::max(c, nodes[i].t);
			}
			return c;
		}

		void GetVerticesAt(const int index, Vector3 & v1, Vector3 & v2, Vector3 & v3) const;
		void GetTextureCoordsAt(const int index, Vector2 *& texCoords1, Vector2 *& texCoords2, Vector2 *& texCoords3) const;
#if !USE_TRIANGLEDATA
		void GetNormalAt(const int index, const float u, const float v, Vector3 & normal) const;
#endif

		friend class BVHOptimizer;
		friend class BVHBuilder;
		friend class BVHBinnedBuilder;
		friend class BVHSAHBuilder;
		friend class RayCaster;

	};
};
#endif /* __BVH1_H__ */
