#include "DeerRender/Log.h"
#include "DeerRender/Mesh.h"
#include "DeerStudio/EditorDataImporter.h"

#include "assimp/Importer.hpp"
#include "assimp/metadata.h"
#include "assimp/postprocess.h"
#include "assimp/scene.h"

#include <algorithm>
#include <cstdint>

namespace Deer {
	Scope<MeshData> convertAssimpMesh(const aiMesh* mesh);

	template <>
	Scope<MeshData> StorageData::deserialize<EditorDataImporter, MeshData>() {
		Assimp::Importer importer;

		DEER_CORE_TRACE("Size {}", size);
		std::string extension;
		const char* extension_char = nullptr;
		if (getMetadata().contains("dataId")) {
			extension = Path(getMetadata()["dataId"]).extension().string();
			extension_char = extension.c_str();
		};
		const aiScene* scene = importer.ReadFileFromMemory(data.get(), size, aiProcess_Triangulate, extension_char); // aiProcess_JoinIdenticalVertices aiProcess_GenNormals |

		if (scene == nullptr) {
			DEER_CORE_ERROR("Error processing assimp");
			return Scope<MeshData>();
		}

		if (scene->mNumMeshes == 0) {
			DEER_CORE_ERROR("Error, file does not have any mesh");
			return Scope<MeshData>();
		}

		if (scene->mNumMeshes != 1) {
			DEER_CORE_WARN("Warn: DeerStudio does not support loading multiples meshes in one object, loading only the first one");
		}

		return convertAssimpMesh(scene->mMeshes[0]);
	}

	Scope<MeshData> convertAssimpMesh(const aiMesh* mesh) {
		Scope<MeshData> result = MakeScope<MeshData>();

		result->createVertices(mesh->mNumVertices);
		VertexPosition* vertexData = result->getVertexPosition();
		VertexNormal* vertexNormal = result->getVertexNormal();

		// Fill vertex positions and normals
		for (uint16_t i = 0; i < result->getVertexCount(); ++i) {
			vertexData[i].x = mesh->mVertices[i].x;
			vertexData[i].y = mesh->mVertices[i].y;
			vertexData[i].z = mesh->mVertices[i].z;

			if (mesh->HasNormals()) {
				// Normal: scale float normal [-1,1] to int8_t [-64,64]
				vertexNormal[i].x = static_cast<int8_t>(std::clamp(mesh->mNormals[i].x * 64.0f, -64.0f, 64.0f));
				vertexNormal[i].y = static_cast<int8_t>(std::clamp(mesh->mNormals[i].y * 64.0f, -64.0f, 64.0f));
				vertexNormal[i].z = static_cast<int8_t>(std::clamp(mesh->mNormals[i].z * 64.0f, -64.0f, 64.0f));
			}
		}

		if (mesh->HasTextureCoords(0)) {
			result->createUVData();
			VertexUV* vertexUVs = result->getVertexUV();

			for (uint32_t i = 0; i < result->getVertexCount(); i++) {
				vertexUVs[i].u = mesh->mTextureCoords[0][i].x;
				vertexUVs[i].v = mesh->mTextureCoords[0][i].y;
			}
		}

		result->createIndices(mesh->mNumFaces * 3);
		uint32_t* indexData = result->getIndexData();

		// Fill indices
		uint32_t idx = 0;
		for (uint32_t f = 0; f < mesh->mNumFaces; ++f) {
			const aiFace& face = mesh->mFaces[f];
			if (face.mNumIndices != 3)
				continue; // skip non-triangle faces

			indexData[idx++] = static_cast<uint32_t>(face.mIndices[0]);
			indexData[idx++] = static_cast<uint32_t>(face.mIndices[1]);
			indexData[idx++] = static_cast<uint32_t>(face.mIndices[2]);
		}

		return result;
	}
} // namespace Deer