#include "DeerRender/Mesh.h"
#include "Deer/Log.h"

namespace Deer {
	namespace MeshManager {
        struct MeshManagerContainer {
            Path mesh_name;
            VertexArray* mesh_data = nullptr;
        };
    
        size_t minModelId = 1;
        MeshManagerContainer meshes[SCENE_MAX_MESH_COUNT]{};
        std::unordered_map<Path, size_t> mesh_name_id;

		VertexArray* loadModelToGPU(const MeshData& data);
	}

	void MeshManager::unloadAllModels() {
		for (uint16_t i = 1; i < minModelId; i++) {
			delete meshes[i].mesh_data;
			meshes[i].mesh_data = nullptr;
		}
        mesh_name_id.clear();

        mesh_name_id["NULL"] = 0;
        meshes[0].mesh_data = nullptr;
        meshes[0].mesh_name = "NULL";
		minModelId = 1;
	}

	uint16_t MeshManager::loadModel(const Path& name) {
        if (mesh_name_id.contains(name))
            return mesh_name_id[name];

        MeshData meshData;
        DataStore::loadModel(meshData, name);

        return loadModel(meshData, name);
	}

	uint16_t MeshManager::loadModel(const MeshData& meshData, const Path& name) {
		if (minModelId >= SCENE_MAX_MESH_COUNT) {
			DEER_CORE_ERROR("Max model loaded into a scene");
			return 0;
		}

        if (mesh_name_id.contains(name)) {
            DEER_CORE_ERROR("Mesh with name {0} already exists", name.string().c_str());
            return 0;
        }

		uint16_t modelId = minModelId;
		minModelId++;

		meshes[modelId].mesh_data = loadModelToGPU(meshData);
		meshes[modelId].mesh_name = name;

		return modelId;
	}

	VertexArray& MeshManager::getModel(uint16_t model_id) {
		DEER_CORE_ASSERT(model_id >= 0 && model_id < minModelId, "Invalid model id, id is not loaded");

		return *meshes[model_id].mesh_data;
	}

	const Path& MeshManager::getModelName(uint16_t model_id) {
		DEER_CORE_ASSERT(model_id >= 0 && model_id < minModelId, "Invalid model id, id is not loaded");

		return meshes[model_id].mesh_name;
	}

    VertexArray* MeshManager::loadModelToGPU(const MeshData& data) {
        VertexArray* vertexArray = VertexArray::create();
        vertexArray->bind();

        Ref<VertexBuffer> vertexPositionBuffer = VertexBuffer::create(data.vertexPositionsData, data.vertexCount * sizeof(VertexPosition));

        BufferLayout positionLayout({
            {"v_position", DataType::Short3, ShaderDataType::FloatingPoint}
        }, sizeof(VertexPosition)); // 
        BufferLayout normalLayout({
            {"v_normal", DataType::Byte3, ShaderDataType::NormalizedFloatingPoint}
        }, sizeof(VertexNormal));

        vertexPositionBuffer->bind();
        vertexPositionBuffer->setLayout(positionLayout);
        vertexArray->addVertexBuffer(vertexPositionBuffer);

        Ref<VertexBuffer> vertexNormalBuffer = VertexBuffer::create(data.vertexNormalData, data.vertexCount * sizeof(VertexNormal));
        vertexNormalBuffer->bind();
        vertexNormalBuffer->setLayout(normalLayout);
        vertexArray->addVertexBuffer(vertexNormalBuffer);

        Ref<IndexBuffer> indexBuffer = IndexBuffer::create(data.indexData, data.indexCount * sizeof(uint16_t), IndexDataType::Unsigned_Short);
        indexBuffer->bind();
        vertexArray->setIndexBuffer(indexBuffer);

        return vertexArray;
    }
}