#include "Deer/Enviroment.h"

#include "Deer/Application.h"
#include "Deer/Components.h"
#include "Deer/Log.h"
#include "DeerRender/Render/Render.h"
#include "DeerRender/Render/RenderUtils.h"
#include "DeerRender/Render/Texture.h"

namespace Deer {
	Environment::Environment() {
		m_registry = MakeScope<entt::registry>();
		createEntityWithId(0, "root");
	}

	Environment::~Environment() {}

	void Environment::clear() {
		// Clear all existing entities and map
		m_registry->clear();

		while (!unused_entities_spaces.empty()) {
		    unused_entities_spaces.pop();
		}
		entities.clear();

		createEntityWithId(0, "root");
	}

	uint16_t Environment::getEntityCount() const {
		return entities.size() - unused_entities_spaces.size();
	}

	Entity& Environment::getEntity(uint16_t id) {
		DEER_CORE_ASSERT(entityExists(id), "Entity id {0} does not exist", id);
		return entities[id];
	}

	bool Environment::entityExists(uint16_t id) const {
		if (id >= entities.size()) return false;
		const Entity& refEntity = entities[id];
		return refEntity.isValid();
	}

	Entity& Environment::createEntity(const std::string& name) {
		uint16_t id;
		
		entt::entity entityID = m_registry->create();
		if (unused_entities_spaces.empty()) {
			id = entities.size();
			entities.push_back({});
		} else {
			id = unused_entities_spaces.top();
			unused_entities_spaces.pop();
		}

		Entity& entity = entities[id];
		entity = {entityID, this, id};

		entity.addComponent<TagComponent>(name, id);
		entity.addComponent<RelationshipComponent>();
		entity.addComponent<TransformComponent>();

		entity.parentId = 0;
		if (!getRoot().getComponent<RelationshipComponent>().addChildrenId(id)) {
			DEER_CORE_ERROR("Root is full!");
		}

		return entity;
	}

	Entity& Environment::createEntityWithId(uint16_t id, const std::string& name) {
		entt::entity entityID = m_registry->create();

		// We allocate all the memory until that id
		if (id >= entities.size()) {
			if (entities.capacity() <= id)
				entities.reserve(id + 1);

			for (int i = entities.size(); i < id; i++) {
				entities.push_back({});
				unused_entities_spaces.push(i);
			}

			entities.push_back({});
		} else {
			static std::stack<uint16_t> unused_entities_spaces_cache;

			// This code its to remove a specific element from a stack
			// We pop the stack until we find the id or we empty it to 
			// completly remove the target id and then we fill it again with the cache
			while (!unused_entities_spaces.empty()) {
				uint16_t topId = unused_entities_spaces.top();
				unused_entities_spaces.pop();
				
				if (topId == id)
					break;
				unused_entities_spaces_cache.push(topId);
			}

			while (!unused_entities_spaces_cache.empty()) {
				uint16_t refillId = unused_entities_spaces_cache.top();
				unused_entities_spaces_cache.pop();

				unused_entities_spaces.push(refillId);
			}
		}

		Entity& entity = entities[id];
		entt::entity _entityId = m_registry->create();
		entity = {_entityId, this, id};

		entity.addComponent<TagComponent>(name, id);
		entity.addComponent<RelationshipComponent>();
		entity.addComponent<TransformComponent>();

		if (id != 0) {
			if (!getRoot().getComponent<RelationshipComponent>().addChildrenId(
			        id)) {
				DEER_CORE_ERROR("Root is full!");
			}
		}
		return entity;
	}

	void Environment::destroyEntity(uint16_t entityID) {
		DEER_CORE_ASSERT(entityExists(entityID), "Entity id {0} does not exist", entityID);
		DEER_CORE_ASSERT(entityID != 0, "Cannot destroy root");

		Entity& entity = entities[entityID];
		entity.getParent().getComponent<RelationshipComponent>().removeChildren(entityID);

		RelationshipComponent& relationship = entity.getComponent<RelationshipComponent>();
		// We want to delete all childrens
		for (int i = 0; i < relationship.childCount; i++) {
			uint16_t childID = relationship.getChildrenId(i);
			destroyEntity(childID);
		}

		if (m_mainCamera == entityID) m_mainCamera = 0;
		
		m_registry->destroy(entity.entHandle);
		entity.clear();

		unused_entities_spaces.push(entityID);
	}

	uint16_t Environment::tryGetMainCamera() const { return m_mainCamera; }

	void Environment::setMainCamera(Entity& entity) {
		if (!entity.isValid()) m_mainCamera = 0;

		m_mainCamera = entity.getId();
	}
}  // namespace Deer