#pragma once
#include <cstdint>

#include "Deer/Log.h"
#include "DeerRender/Mesh.h"
#include "DeerRender/Mesh/Serialization/VertexNormalSerialization.h"
#include "DeerRender/Mesh/Serialization/VertexPositionSerialization.h"
#include "cereal/cereal.hpp"

namespace Deer {
	template <class T>
	struct SerializationArray {
		T* data;
		size_t size;

		SerializationArray(T* _data, size_t _size) : data(_data), size(_size) {}
		SerializationArray() = default;

		template <class Archive>
		void save(Archive& archive) const {
			archive(cereal::make_size_tag(size));
			for (size_t i = 0; i < size; i++) archive(data[i]);
		}

		template <class Archive>
		void load(Archive& archive) {
			archive(cereal::make_size_tag(size));
			data = new T[size];
			for (size_t i = 0; i < size; i++) archive(data[i]);
		}
	};

	template <class Archive>
	void save(Archive& archive, const MeshData& meshData) {
		const bool hasNormalData = meshData.vertexNormalData != nullptr;
		const size_t vertexCount = meshData.vertexCount;
		const size_t indexCount = meshData.indexCount;

		// GLOBAL VARS
		archive(cereal::make_nvp("hasNormalData", hasNormalData));

		// VERTEX POSITION
		SerializationArray<VertexPosition> vertexPositionsArray(
		    meshData.vertexPositionsData, vertexCount);
		archive(cereal::make_nvp("vertexPositions", vertexPositionsArray));

		// VERTEX NORMAL
		if (hasNormalData) {
			SerializationArray<VertexNormal> vertexNormalArray(
			    meshData.vertexNormalData, vertexCount);
			archive(cereal::make_nvp("vertexNormals", vertexNormalArray));
			if (indexCount != vertexNormalArray.size) {
				DEER_CORE_ERROR(
				    "Error while loading mesh, vertex normal array is not the "
				    "same lenght as vertex positions");
			}
		}

		// INDICES
		SerializationArray<uint16_t> indexArray(meshData.indexData, indexCount);
		archive(cereal::make_nvp("indices", indexArray));
	}

	template <class Archive>
	void load(Archive& archive, MeshData& meshData) {
		bool hasNormalData;
		size_t vertexCount;
		size_t indexCount;

		// GLOBAL VARS
		archive(cereal::make_nvp("hasNormalData", hasNormalData));

		// VERTEX POSITION
		SerializationArray<VertexPosition> vertexPositionsArray;
		archive(cereal::make_nvp("vertexPositions", vertexPositionsArray));
		meshData.vertexPositionsData = vertexPositionsArray.data;
		meshData.vertexCount = vertexPositionsArray.size;

		// VERTEX NORMAL
		if (hasNormalData) {
			SerializationArray<VertexNormal> vertexNormalArray;
			archive(cereal::make_nvp("vertexNormals", vertexNormalArray));
			meshData.vertexNormalData = vertexNormalArray.data;
		}

		// INDICES
		SerializationArray<uint16_t> indexArray;
		archive(cereal::make_nvp("indices", indexArray));
		meshData.indexData = indexArray.data;
		meshData.indexCount = indexArray.size;
	}
}  // namespace Deer