#pragma once
#include "Deer/Memory.h"

#define GLM_ENABLE_EXPERIMENTAL
#include <stdint.h>

#include <array>
#include <string>
#include <vector>

#include "Deer/Log.h"
#include "glm/glm.hpp"
#include "glm/gtc/quaternion.hpp"

#define ENTITY_MAX_CHILDREN 64

// Here we define the core components of the ECS

namespace Deer {
	class ComponentScriptInstance;

	struct TagComponent {
		std::string tag;
		uint32_t entityUID;

		TagComponent() = default;
		TagComponent(const TagComponent&) = default;
		TagComponent(std::string name, uint32_t _id = 0)
		    : tag(name), entityUID(_id) {}
	};

	struct RelationshipComponent {
		uint16_t parent_id = 0;
		uint16_t childCount = 0;
		// Note: Since the root(id : 0) can't be a children, we will use 0 as
		// null or empty
		std::array<uint16_t, ENTITY_MAX_CHILDREN> childrens{};

		inline uint16_t getChildrenId(size_t i) {
			DEER_CORE_ASSERT(i >= 0 && i < childCount && childrens[i] != 0,
			                 "Invalid child request {0}", i);
			return childrens[i];
		}
		inline bool addChildrenId(uint16_t childId) {
			if (childCount >= ENTITY_MAX_CHILDREN) return false;
			childrens[childCount] = childId;
			childCount++;
			return true;
		}
		inline void removeChildren(uint16_t childId) {
			uint16_t childPosition = 0;
			for (int i = 0; i < childCount; i++) {
				if (childrens[i] == childId) {
					childPosition = i;
					break;
				}
			}

			// Move all childs one position
			for (int i = childPosition + 1; i < childCount; i++) {
				childrens[i - 1] = childrens[i];
			}

			childrens[childCount - 1] = 0;
			childCount--;
		}

		RelationshipComponent() = default;
		RelationshipComponent(const RelationshipComponent&) = default;
		RelationshipComponent(uint16_t _parent) : parent_id(_parent) {}
	};

	struct TransformComponent {
		glm::vec3 position = glm::vec3(0.0f);
		glm::vec3 scale = glm::vec3(1.0f);
		glm::quat rotation = glm::quat(1.0f, 0.0f, 0.0f, 0.0f);

		TransformComponent() = default;
		TransformComponent(glm::vec3 _position) : position(_position) {}
		TransformComponent(const TransformComponent&) = default;

		inline const glm::vec3 getEulerAngles() {
			return glm::degrees(glm::eulerAngles(rotation));
		}
		inline void setEulerAngles(const glm::vec3& eulerAngles) {
			rotation = glm::quat(glm::radians(eulerAngles));
		}

		glm::mat4 getMatrix() const;
	};
}  // namespace Deer