#include "anki/resource/Mesh.h"
#include "anki/resource/Material.h"
#include "anki/resource/MeshLoader.h"
#include "anki/gl/Vbo.h"
#include "anki/util/Functions.h"
#include <fstream>

namespace anki {

//==============================================================================
void Mesh::load(const char* filename)
{
	MeshLoader loader(filename);

	// Set the non-VBO members
	vertsCount = loader.getPositions().size();
	ANKI_ASSERT(vertsCount > 0);

	indicesCount.push_back(loader.getLodsCount());
	for(U lod = 0; lod < indicesCount.size(); lod++)
	{
		indicesCount[lod] = loader.getIndices(lod).size();
		ANKI_ASSERT(indicesCount[lod] > 0);
		ANKI_ASSERT(indicesCount[lod] % 3 == 0 && "Expecting triangles");
	}

	weights = loader.getWeights().size() > 1;
	texChannelsCount = loader.getTextureChannelsCount();

	try
	{
		createVbos(loader);

		visibilityShape.set(loader.getPositions());
	}
	catch(std::exception& e)
	{
		throw ANKI_EXCEPTION("Mesh loading failed: " + filename) << e;
	}
}

//==============================================================================
U32 Mesh::calcVertexSize() const
{
	U32 a = sizeof(Vec3) + sizeof(Vec3) + sizeof(Vec4) 
		+ texChannelsCount * sizeof(Vec2);
	if(weights)
	{
		a += sizeof(MeshLoader::VertexWeight);
	}
	return a;
}

//==============================================================================
void Mesh::createVbos(const MeshLoader& loader)
{
	// Calculate VBO size
	U32 vertexsize = calcVertexSize();
	U32 vbosize = vertexsize * vertsCount;

	// Create a temp buffer and populate it
	Vector<U8> buff(vbosize, 0);

	U8* ptr = &buff[0];
	for(U i = 0; i < vertsCount; i++)
	{
		ANKI_ASSERT(ptr + vertexsize <= &buff[0] + vbosize);

		memcpy(ptr, &loader.getPositions()[i], sizeof(Vec3));
		ptr += sizeof(Vec3);

		memcpy(ptr, &loader.getNormals()[i], sizeof(Vec3));
		ptr += sizeof(Vec3);

		memcpy(ptr, &loader.getTangents()[i], sizeof(Vec4));
		ptr += sizeof(Vec4);

		for(U j = 0; j < texChannelsCount; j++)
		{
			memcpy(ptr, &loader.getTexureCoordinates(j)[i], sizeof(Vec2));
			ptr += sizeof(Vec2);
		}

		if(weights)
		{
			memcpy(ptr, &loader.getWeights()[i], 
				sizeof(MeshLoader::VertexWeight));
			ptr += sizeof(MeshLoader::VertexWeight);
		}
	}

	// Create VBO
	vbo.create(
		GL_ARRAY_BUFFER,
		vbosize,
		&buff[0],
		GL_STATIC_DRAW);

	/// Create the indices VBOs
	indicesVbos.resize(loader.getLodsCount());
	U lod = 0;
	for(Vbo& v : indicesVbos)
	{
		v.create(
			GL_ELEMENT_ARRAY_BUFFER,
			getVectorSizeInBytes(loader.getIndices(lod)),
			&loader.getIndices(lod)[0],
			GL_STATIC_DRAW);

		++lod;
	}
}

//==============================================================================
void Mesh::getVboInfo(
	const VertexAttribute attrib, const U32 lod, const Vbo*& v, U32& size, 
	GLenum& type, U32& stride, U32& offset) const
{
	stride = calcVertexSize();

	// Set all to zero
	v = nullptr;
	size = 0;
	type = GL_NONE;
	offset = 0;

	switch(attrib)
	{
	case VA_POSITION:
		v = &vbo;
		size = 3;
		type = GL_FLOAT;
		offset = 0;
		break;
	case VA_NORMAL:
		v = &vbo;
		size = 3;
		type = GL_FLOAT;
		offset = sizeof(Vec3);
		break;
	case VA_TANGENT:
		v = &vbo;
		size = 4;
		type = GL_FLOAT;
		offset = sizeof(Vec3) * 2;
		break;
	case VA_TEXTURE_COORDS:
		if(texChannelsCount > 0)
		{
			v = &vbo;
			size = 2;
			type = GL_FLOAT;
			offset = sizeof(Vec3) * 2 + sizeof(Vec4);
		}
		break;
	case VA_TEXTURE_COORDS_1:
		if(texChannelsCount > 1)
		{
			v = &vbo;
			size = 2;
			type = GL_FLOAT;
			offset = sizeof(Vec3) * 2 + sizeof(Vec4) + sizeof(Vec2);
		}
		break;
	case VA_BONE_COUNT:
		if(weights)
		{
			v = &vbo;
			size = 1;
			type = GL_UNSIGNED_INT;
			offset = sizeof(Vec3) * 2 + sizeof(Vec4) 
				+ texChannelsCount * sizeof(Vec2);
		}
		break;
	case VA_BONE_IDS:
		if(weights)
		{
			v = &vbo;
			size = 4;
			type = GL_UNSIGNED_INT;
			offset = sizeof(Vec3) * 2 + sizeof(Vec4) 
				+ texChannelsCount * sizeof(Vec2) + sizeof(U32);
		}
		break;
	case VA_BONE_WEIGHTS:
		if(weights)
		{
			v = &vbo;
			size = 4;
			type = GL_FLOAT;
			offset = sizeof(Vec3) * 2 + sizeof(Vec4) 
				+ texChannelsCount * sizeof(Vec2) + sizeof(U32) 
				+ sizeof(U32) * 4;
		}
	case VA_INDICES:
		if(lod < indicesVbos.size())
		{
			v = &indicesVbos[lod];
		}
		break;
	default:
		ANKI_ASSERT(0);
		break;
	}
}

} // end namespace anki
