#pragma once
#include "DeerRender/Render/VertexArray.h"
#include "DeerRender/Resource.h"
#include "DeerRender/Tools/Memory.h"
#include "DeerRender/Tools/Path.h"

#include <cstdint>

namespace Deer {
	struct VertexPosition {
		float x;
		float y;
		float z;

		VertexPosition() = default;
		VertexPosition(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
	};

	struct VertexUV {
		float u;
		float v;

		VertexUV() = default;
		VertexUV(float _u, float _v) : u(_u), v(_v) {}
	};

	// Vertex normal is represented with a number fromn [-64,64], and then its
	// divided by 64 to know the decimal number
	struct VertexNormal {
		int8_t x = 0;
		int8_t y = 0;
		int8_t z = 0;

		VertexNormal() = default;
		VertexNormal(int8_t _x, int8_t _y, int8_t _z) : x(_x), y(_y), z(_z) {}
	};

	struct MeshData {
	  public:
		void createVertices(uint32_t count) {
			vertexPositionsData = MakeScope<VertexPosition[]>(count);
			vertexNormalData = MakeScope<VertexNormal[]>(count);
			vertexCount = count;
		}
		void createUVData() { vertexUVData = MakeScope<VertexUV[]>(vertexCount); }
		void createIndices(uint32_t count) {
			indexData = MakeScope<uint32_t[]>(count);
			indexCount = count;
		}

		inline VertexPosition* getVertexPosition() const { return vertexPositionsData.get(); }
		inline VertexNormal* getVertexNormal() const { return vertexNormalData.get(); }
		inline VertexUV* getVertexUV() const { return vertexUVData.get(); }
		inline uint32_t* getIndexData() const { return indexData.get(); }

		inline uint32_t getVertexCount() const { return vertexCount; }
		inline uint32_t getIndexCount() const { return indexCount; }

	  private:
		uint32_t vertexCount = 0;
		Scope<VertexPosition[]> vertexPositionsData;
		Scope<VertexNormal[]> vertexNormalData;
		Scope<VertexUV[]> vertexUVData;

		uint32_t indexCount = 0;
		Scope<uint32_t[]> indexData;
	};

	struct GPUMesh {
		Scope<VertexArray> vertexArray;
	};

	template <>
	class ResourceBuilder<GPUMesh> {
	  public:
		using BaseDataType = MeshData;
		static Scope<GPUMesh> buildResource(const BaseDataType& baseData);
	};

	namespace MeshManager {
		uint16_t loadModel(const Path&);
		uint16_t loadModel(const MeshData&, const Path&);
		VertexArray& getModel(uint16_t model_id);
		const Path& getModelName(uint16_t model_id);

		void unloadAllModels();
	} // namespace MeshManager

	namespace DataStore {
		void saveModel(const MeshData&, const Path& name);
		void loadModel(MeshData&, const Path& name);

		void saveBinModel(const MeshData&, const Path& name);

		void createExampleMeshData();
	} // namespace DataStore
} // namespace Deer