/*
 * $Id:$
 *
 * Description  : Naive geometry accelerator class using just a copy of a list of triangles
 * Author       : Jiri Bittner
 *
 * Purpose      :
 *     Long description of what the file is for.
 */

#ifndef __GEOMETRY_ACCELERATOR_NAIVE_H__
#define __GEOMETRY_ACCELERATOR_NAIVE_H__

#include "RayPacket.h"

#define USE_SSE_FOR_TRIANGLES
namespace VRUT
{
	class GeometryAcceleratorNaive
	{
	public:
		smart_pointer<Geometry> geometry;
		std::vector<TriangleData> triangles;

		///Cast ray towards the geometry, get intersection info if it does
		///NOTE: everything must be in one space (model space by default), \c intersection info will be in that space too!
		bool CastRay(const Ray &ray, RayIntersectionInfo &info) const {
			bool hasIntersection=false;

			for (size_t i=0; i < triangles.size(); i++) {
				float u = 0;
				float v = 0;
				float dist;
#ifdef USE_SSE_FOR_TRIANGLES
				if (ray.IntersectsTriSSE(_mm_set_ps(0, triangles[i].v2.z, triangles[i].v2.y, triangles[i].v2.x),
										 _mm_set_ps(0, triangles[i].e1.z, triangles[i].e1.y, triangles[i].e1.x),
										 _mm_set_ps(0, triangles[i].e2.z, triangles[i].e2.y, triangles[i].e2.x), info.dist, u, v, dist)) {
#else
				if (ray.IntersectsTri(triangles[i].v2, triangles[i].e1, triangles[i].e2, info.dist, u, v, dist)) {
#endif
					info.dist = dist;
					info.u = u;
					info.v = v;
					if ((triangles[i].n1.x == 0) && (triangles[i].n1.y == 0) && (triangles[i].n1.z == 0))
						info.normal = triangles[i].n2;
					else
						info.normal = (u*triangles[i].n1 + (1-u-v)*triangles[i].n2 + v*triangles[i].n3).Normalize();

					info.index = i;
					hasIntersection=true;
				}
			}
			return hasIntersection;
		}

		void CastRay(RayPacket rayPacket[], RayIntersectionInfo info[], unsigned char intersection[], size_t count) const
		{
			size_t count2=count/4+(((count%4)!=0)?1:0);

			for (size_t i=0; i < triangles.size(); i++)
			{
				for (size_t i2 = 0; i2 < count2; ++i2)
				{
					RayPacket::Point u;
					RayPacket::Point v;
					unsigned char interse=rayPacket[i2].intersectTri(triangles[i].v2, triangles[i].e1, triangles[i].e2, (RayPacket::Point*)&u, (RayPacket::Point*)&v);
					if (interse)
					{
						for (size_t ii=0; ii < 4; ++ii)
						{
							if ((i2*4+ii<count) && ((interse>>ii)&1))
							{
								info[i2*4+ii].dist = rayPacket[i2].dist.v[ii];
								info[i2*4+ii].u = u.v[ii];
								info[i2*4+ii].v = v.v[ii];
								if ((triangles[i].n1.x == 0) && (triangles[i].n1.y == 0) && (triangles[i].n1.z == 0))
									info[i2*4+ii].normal = triangles[i].n2;
								else
									info[i2*4+ii].normal = (u.v[ii]*triangles[i].n1 + (1-u.v[ii]-v.v[ii])*triangles[i].n2 + v.v[ii]*triangles[i].n3).Normalize();

								info[i2*4+ii].index = i;
								intersection[i2]|=1<<ii;
							}
						}
					}
				}
			}
		}

		static void CastRay(const smart_pointer<Geometry> &geometry, RayPacket rayPacket[], RayIntersectionInfo info[], unsigned char intersection[], size_t count)
		{
			Ray rays[4];
			for (size_t i = 0; i <count; ++i)
			{
				if (i%4==0)
					rayPacket[i/4].ToRays(rays);
				if (geometry->CastRay(rays[i%4], info[i]))
					intersection[i/4]|=1<<(i/4);
			}
			/*
			//faster but problematic version for big geometries like these from STL files
			std::vector<TriangleData> triangles = geometry->TriangulateData();
			for (size_t i = 0; i < triangles.size(); ++i)
				if ((triangles[i].n1.x == 0) && (triangles[i].n1.y == 0) && (triangles[i].n1.z == 0))
					triangles[i].n2=triangles[i].GetNormal();
			
			size_t count2=count/4+(((count%4)!=0)?1:0);
			for (size_t i=0; i < triangles.size(); i++)
			{
				for (size_t i2 = 0; i2 < count2; ++i2)
				{
					RayPacket::Point u;
					RayPacket::Point v;
					unsigned char interse=rayPacket[i2].intersectTri(triangles[i].v2, triangles[i].e1, triangles[i].e2, (RayPacket::Point*)&u, (RayPacket::Point*)&v);
					if (interse)
					{
						for (size_t ii=0; ii < 4; ++ii)
						{
							if ((i2*4+ii<count) && ((interse>>ii)&1))
							{
								info[i2*4+ii].dist = rayPacket[i2].dist.v[ii];
								info[i2*4+ii].u = u.v[ii];
								info[i2*4+ii].v = v.v[ii];
								if ((triangles[i].n1.x == 0) && (triangles[i].n1.y == 0) && (triangles[i].n1.z == 0))
									info[i2*4+ii].normal = triangles[i].n2;
								else
									info[i2*4+ii].normal = (u.v[ii]*triangles[i].n1 + (1-u.v[ii]-v.v[ii])*triangles[i].n2 + v.v[ii]*triangles[i].n3).Normalize();

								info[i2*4+ii].index = i;
								intersection[i2]|=1<<ii;
							}
						}
					}
				}
			}*/
		}
		
		GeometryAcceleratorNaive(const smart_pointer<Geometry> &geometry, const int Nmax = -1) : geometry(geometry)
		{
			Rebuild();
		}

		~GeometryAcceleratorNaive() {
			// all triangles will get deleted
		}


		void Rebuild() {
			triangles.clear();
			triangles = geometry->TriangulateData();
			for (auto &triangle : triangles)
				if ((triangle.n1.x == 0) && (triangle.n1.y == 0) && (triangle.n1.z == 0))
					triangle.n2=triangle.GetNormal();
		}

		void Optimize(const int count) {}

		void GetTextureCoordsAt(const int index, Vector2 *&texCoords1, Vector2 *&texCoords2, Vector2 *&texCoords3)
		{
			texCoords1=&triangles[index].texCoord1;
			texCoords2=&triangles[index].texCoord2;
			texCoords3=&triangles[index].texCoord3;
		}
	};		
}
#endif

