#include "CModelLoader.hpp"
#include <iostream>

CModel* CModelLoader::loadModel(std::string fileName, SMaterial* material)
{
	Assimp::Importer import;
	const aiScene* scene = import.ReadFile(fileName, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_JoinIdenticalVertices);

	if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode)
	{
		std::cout << std::string(import.GetErrorString()) << std::endl;
		DEBUG_LOG(trace, "Kernel", "ModelLoader", std::string("Assimp error : ") + std::string(import.GetErrorString()))
		return nullptr;
	}

	std::vector<CMesh*> meshes;
	processNode(scene->mRootNode, scene, &meshes, material);

	CModel* model = new CModel(meshes);
	model->load();
	return model;
}

void CModelLoader::processNode(aiNode* node, const aiScene* scene, std::vector<CMesh*>* meshes, SMaterial* material)
{
	// process all the node's meshes (if any)
	for (unsigned int i = 0; i < node->mNumMeshes; i++)
	{
		aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
		processMesh(mesh, scene, meshes, material);
	}
	// then do the same for each of its children
	for (unsigned int i = 0; i < node->mNumChildren; i++)
	{
		processNode(node->mChildren[i], scene, meshes, material);
	}
}

void CModelLoader::processMesh(aiMesh* mesh, const aiScene* scene, std::vector<CMesh*>* meshes, SMaterial* material)
{
	std::vector<SVertex> vertices;
	std::vector<unsigned int> indices;
	SVertex vertex;
	for (unsigned int i = 0; i < mesh->mNumVertices; i++)
	{
		vertex.m_position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
		vertex.m_normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);

		if (mesh->mTextureCoords[0]) // if the mesh contains texture coordinates
		{
			vertex.m_texCoords.x = mesh->mTextureCoords[0][i].x;
			vertex.m_texCoords.y = mesh->mTextureCoords[0][i].y;
		}
		else
		{
			vertex.m_texCoords = glm::vec2(0.0f, 0.0f);
		}

		if (mesh->mBitangents)// if the mesh contains bitangent coordinates
		{
			vertex.m_bitangent.x = mesh->mBitangents->x;
			vertex.m_bitangent.y = mesh->mBitangents->y;
			vertex.m_bitangent.z = mesh->mBitangents->z;
		}
		else
		{
			vertex.m_bitangent = glm::vec3(0.0f, 0.0f, 0.0f);
		}

		if (mesh->mTangents)// if the mesh contains tangent coordinates
		{
			vertex.m_tangent.x = mesh->mTangents->x;
			vertex.m_tangent.y = mesh->mTangents->y;
			vertex.m_tangent.z = mesh->mTangents->z;
		}
		else
		{
			vertex.m_tangent = glm::vec3(0.0f, 0.0f, 0.0f);
		}

		vertices.push_back(vertex);
	}
	// process indices

	for (unsigned int i = 0; i < mesh->mNumFaces; i++)
	{
		aiFace face = mesh->mFaces[i];
		for (unsigned int j = 0; j < face.mNumIndices; j++)
			indices.push_back(face.mIndices[j]);
	}
	// process material
	std::vector<CImageTexture*> diffuseMaps;
	std::vector<CImageTexture*> specularMaps;
	std::vector<CAbstractTexture*> textures;
	SMaterial* mat = new SMaterial;
	if (mesh->mMaterialIndex >= 0 && material == nullptr)
	{
		aiMaterial* amaterial = scene->mMaterials[mesh->mMaterialIndex];
		diffuseMaps = loadMaterialTextures(amaterial,
			aiTextureType_DIFFUSE, ETextureType::TEXTURE_DIFFUSE);
		specularMaps = loadMaterialTextures(amaterial,
			aiTextureType_SPECULAR, ETextureType::TEXTURE_SPECULAR);

		for (size_t i = 0; i < diffuseMaps.size(); i++)
		{
			textures.push_back((CAbstractTexture*)diffuseMaps[i]);
		}
		for (size_t j = 0; j < specularMaps.size(); j++)
		{
			textures.push_back((CAbstractTexture*)specularMaps[j]);
		}
		std::vector<SColor> colorsC; //= { {glm::vec4(1.0f, 1.0f, 1.0f, 1.0f), EColorType::COLOR_DIFFUSE} };
		aiColor4D color(0.0f, 0.0f, 0.0f, 0.0f);
		amaterial->Get(AI_MATKEY_COLOR_DIFFUSE, color);
		SColor diffuseColor = { glm::vec4(color.r, color.g, color.b, color.a), EColorType::COLOR_DIFFUSE };
		colorsC.push_back(diffuseColor);

		*mat = { textures, colorsC, CKernel::m_mainShader };
	}
	else// if the material is given, override the material of the wavefront obj file
	{
		delete mat;
		mat = material;
	}


	CMesh* m = new CMesh();

	m->setMaterial(mat);

	m->setIndexes(indices);
	m->setVertices(vertices);

	meshes->push_back(m);

}

std::vector<CImageTexture*> CModelLoader::loadMaterialTextures(aiMaterial* mat, aiTextureType type, ETextureType typeName)
{
	std::vector<CImageTexture*> textures;
	for (unsigned int i = 0; i < mat->GetTextureCount(type); i++)
	{
		aiString str;
		mat->GetTexture(type, i, &str);
		CImageTexture* text = new CImageTexture(typeName, str.C_Str());
		text->init();//Load the texture in GPU before adding it the texture list
		textures.push_back(text);
	}
	return textures;
}