#include "BVHBuilder.h"
#include "flexilog.h"

#include "geometrytext.h"
#include "FontGlyphsTexture.h"
#include "kernel.h"

using namespace VRUT;

void BVHBuilder::PreSetupTriangles(const smart_pointer<Geometry> & geometry)
{
	SAFE_DELETE(bvh->triangles);
	bvh->triangles = new std::vector<TriangleData>();
	if (geometry->IsOfType(Geometry::GEOMETRY_TEXT))
	{
		//Transform text into TriangleData
		GenerateTrianglesFromText(geometry, bvh->triangles);
	}
	else
		*bvh->triangles = geometry->TriangulateData();

	numberOfTriangles = bvh->triangles->size();
}

void BVHBuilder::GenerateTrianglesFromText(const smart_pointer<Geometry>& text, std::vector<TriangleData>* triangles)
{
	const smart_pointer<GeometryText> & geometry = text;

	wxFont font = geometry->GetFont();
	auto sz = geometry->GetSize();
	auto sp = geometry->GetSpacing();
	//font.SetPointSize(12);

	const auto glyphs = KERNEL->GetOrCreateFont(font);

	TextLettersData letters(geometry->GetText(), glyphs, geometry->GetSize(), geometry->GetSpacing(), geometry->GetMajorJustify(), geometry->GetMinorJustify());

	//character array 32x32
	const int dimX = 32;
	const int dimY = 32;

	triangles->resize(letters.GetLetterDesc().size() * 2); //2 triangles per letter

	auto metrics = glyphs->GetMetrics();

	for (size_t c = 0; c < letters.GetLetterDesc().size(); c++)
	{
		float tex[2] = {
			float(letters.GetLetterDesc()[c].glyph % dimX),
			float((letters.GetLetterDesc()[c].glyph / dimX) % dimY),
		};

		if (0/*c % 2 == 0*/)
		{
			TriangleData data;
			data.v1 = data.v2 = data.v3 = data.n1 = data.n2 = data.n3 = Vector3(0, 0, 0);
			data.texCoord1 = data.texCoord2 = data.texCoord3 = Vector2(0, 0);

			(*triangles)[2 * c + 0] = data;
			(*triangles)[2 * c + 1] = data;
			continue;
		}

		float width = metrics[letters.GetLetterDesc()[c].glyph].x_width;

		Vector2 pos = letters.GetLetterDesc()[c].position; 
		float characterSize = letters.GetRectSize();

		const Vector3 vec1 = Vector3(pos.x, pos.y, 0.0f);
		const Vector3 vec2 = Vector3(pos.x + width * 2 * sz, pos.y, 0.0f);
		const Vector3 vec3 = Vector3(pos.x + width * 2 * sz, pos.y + characterSize*0.85, 0.0f);
		const Vector3 vec4 = Vector3(pos.x, pos.y + characterSize*0.85, 0.0f);
		const Vector3 nor1 = Vector3(0.0f, 0.0f, 1.0f);
		const Vector3 nor2 = Vector3(0.0f, 0.0f, 1.0f);
		const Vector3 nor3 = Vector3(0.0f, 0.0f, 1.0f);
		const Vector3 nor4 = Vector3(0.0f, 0.0f, 1.0f);
		const Vector2 tco1 = Vector2((0.0f + tex[0] + 0.15) / dimX, (0.0f + tex[1]) / dimY);
		const Vector2 tco2 = Vector2((width + tex[0] + 0.15) / dimX, (0.0f + tex[1]) / dimY );
		const Vector2 tco3 = Vector2((width + tex[0] + 0.15) / dimX, (1.0f + tex[1] - 0.15) / dimY );
		const Vector2 tco4 = Vector2((0.0f + tex[0] + 0.15) / dimX, (1.0f + tex[1] - 0.15) / dimY);
		//First triangle
		TriangleData data;
		data.v1 = vec1;
		data.v2 = vec2;
		data.v3 = vec4;

		data.SetNormals(nor1, nor2, nor4);
		data.SetTexCoords(tco1, tco2, tco4);

		(*triangles)[2 * c + 0] = data;

		//Second triangle
		TriangleData data2;
		data2.v1 = vec2;
		data2.v2 = vec3;
		data2.v3 = vec4;

		data2.SetNormals(nor2, nor3, nor4);
		data2.SetTexCoords(tco2, tco3, tco4);

		(*triangles)[2 * c + 1] = data2;
	}
}

void BVHBuilder::PostSetupTriangles(const smart_pointer<Geometry> & geometry)
{
	bvh->triangleCols.resize(bvh->triangles->size());
	bvh->triangleNorms.resize(bvh->triangles->size());
	bvh->triangleTexs.resize(bvh->triangles->size());
	for (size_t t = 0; t < bvh->triangles->size(); ++t)
	{
		TriangleData & tdata = bvh->triangles->at(aabbs[t].index);
		bvh->triangleCols[t].v1 = tdata.v1;
		bvh->triangleCols[t].e21 = tdata.v2 - tdata.v1;
		bvh->triangleCols[t].e13 = tdata.v1 - tdata.v3;
		bvh->triangleCols[t].e21x13 = CrossProd(bvh->triangleCols[t].e21, bvh->triangleCols[t].e13);
		if ((tdata.n1.x == 0) && (tdata.n1.y == 0) && (tdata.n1.z == 0))
		{
			bvh->triangleNorms[t].n2 = tdata.GetNormal();
		}
		else
		{
			bvh->triangleNorms[t].n1 = tdata.n1;
			bvh->triangleNorms[t].n2 = tdata.n2;
			bvh->triangleNorms[t].n3 = tdata.n3;
		}
		bvh->triangleTexs[t].texCoord1 = tdata.texCoord1;
		bvh->triangleTexs[t].texCoord2 = tdata.texCoord2;
		bvh->triangleTexs[t].texCoord3 = tdata.texCoord3;
	}
	if (aabbs != nullptr)
	{
		VrutAlignedFree(aabbs);
		aabbs = nullptr;
	}
}

void BVHBuilder::ComputeAABBs()
{
	sceneMin._p = _mm_set1_ps(FLT_MAX);
	sceneMax._p = _mm_set1_ps(-FLT_MAX);
	centroidMin._p = _mm_set1_ps(FLT_MAX);
	centroidMax._p = _mm_set1_ps(-FLT_MAX);
	if (numberOfTriangles > 0)
	{
		aabbs = (TriangleAABB*)VrutAlignedMalloc(numberOfTriangles * sizeof(TriangleAABB), 16);
		for (unsigned int i = 0; i < numberOfTriangles; ++i)
		{
			TriangleData &tdata=bvh->triangles->at(i);
			__m128 t1 = _mm_set_ps(tdata.v1.x, tdata.v1.y, tdata.v1.z, tdata.v1.x);
			__m128 t2 = _mm_set_ps(tdata.v2.x, tdata.v2.y, tdata.v2.z, tdata.v2.x);
			__m128 t3 = _mm_set_ps(tdata.v3.x, tdata.v3.y, tdata.v3.z, tdata.v3.x);
			aabbs[i].max._p = _mm_max_ps(_mm_max_ps(t1, t2), t3);
			aabbs[i].min._p = _mm_min_ps(_mm_min_ps(t1, t2), t3);
			aabbs[i].index = i;
			aabbs[i].centroid._p = _mm_div_ps(_mm_add_ps(aabbs[i].min._p, aabbs[i].max._p), _mm_set1_ps(2.0f));
			sceneMin._p = _mm_min_ps(sceneMin._p, aabbs[i].min._p);
			sceneMax._p = _mm_max_ps(sceneMax._p, aabbs[i].max._p);
			centroidMin._p = _mm_min_ps(centroidMin._p, aabbs[i].centroid._p);
			centroidMax._p = _mm_max_ps(centroidMax._p, aabbs[i].centroid._p);
		}
	}
}

BVHBuilder::BVHBuilder() : numberOfTriangles(0), aabbs(nullptr), bvh(nullptr)
{
	sceneMin._p = _mm_set1_ps(FLT_MAX);
	sceneMax._p = _mm_set1_ps(-FLT_MAX);
	centroidMin._p = _mm_set1_ps(FLT_MAX);
	centroidMax._p = _mm_set1_ps(-FLT_MAX);
}

BVHBuilder::~BVHBuilder()
{
	if (aabbs != nullptr)
		VrutAlignedFree(aabbs);
}
