#include "Deer/Components.h"
#include "Deer/Enviroment.h"
#include "Deer/Log.h"

namespace Deer {
	Entity::Entity(entt::entity handle, Environment* scene, uint16_t entityID)
	    : entHandle(handle),
	      environment(scene),
	      entId(entityID) {}

	Entity::Entity(Entity&& ent) noexcept {
		environment = ent.environment;
		entHandle = ent.entHandle;
		parentId = ent.parentId;
		entId = ent.entId;

		ent.environment = nullptr;
		ent.entHandle = entt::null;
		ent.parentId = 0;
		ent.entId = 0;
	}

	Entity& Entity::operator=(Entity&& ent) noexcept {
		environment = ent.environment;
		entHandle = ent.entHandle;
		parentId = ent.parentId;
		entId = ent.entId;

		ent.environment = nullptr;
		ent.entHandle = entt::null;
		ent.parentId = 0;
		ent.entId = 0;

		return *this;
	}

	inline bool Entity::isValid() const {
		return environment != nullptr;
	}

	void Entity::setParent(Entity& parent) {
		DEER_CORE_ASSERT(isValid(), "Entity is not valid");
		DEER_CORE_ASSERT(parent.environment == environment,
		                 "Cannot set parent from different environments");
		DEER_CORE_ASSERT(parent.isValid(), "Parent is not valid");
		if (parent.getId() == getId())
			return;

		Entity& current_parent = getParent();

		if (parent.isDescendantOf(*this)) {
			DEER_CORE_WARN("Cannot set parent: would create cycle");
			return;
		}

		if (current_parent.isValid() && current_parent.getId() != parent.getId()) {
			current_parent.getComponent<RelationshipComponent>().removeChild(getId());
		}

		parent.getComponent<RelationshipComponent>().addChildId(getId());

		getComponent<RelationshipComponent>().parent_id = parent.getId();
		parentId = parent.getId();
	}

	bool Entity::isDescendantOf(Entity& parent) const {
		DEER_CORE_ASSERT(isValid(), "Entity is not valid");
		if (getId() == parent.getId())
			return true;
		if (isRoot())
			return false;

		return getParent().isDescendantOf(parent);
	}

	Entity& Entity::duplicate() {
		DEER_CORE_ASSERT(isValid(), "Entity is not valid");
		Entity& creation = environment->createEntity(
		    getComponent<TagComponent>().tag + "(d)");

		creation.setParent(getParent());

		creation.getComponent<TransformComponent>() =
		    getComponent<TransformComponent>();

		/*
		#ifdef DEER_RENDER
		    if (environment->m_registry->any_of<MeshComponent>(entHandle))
		        creation.addComponent<MeshComponent>(
		            getComponent<MeshComponent>());

		    if (environment->m_registry->any_of<CameraComponent>(entHandle))
		        creation.addComponent<CameraComponent>(
		            getComponent<CameraComponent>());
		#endif
		*/
		return creation;
	}

	void Entity::destroy() {
		DEER_CORE_ASSERT(isValid(), "Entity is not valid");
		DEER_CORE_ASSERT(!isRoot(), "Can not destroy the root");
		environment->destroyEntity(getId());
	}

	Entity& Entity::getParent() const {
		DEER_CORE_ASSERT(isValid(), "Entity is not valid");
		return environment->getEntity(parentId);
	}

	glm::mat4 Entity::getWorldMatrix() const {
		if (isRoot())
			return glm::mat4(1.0f);

		return getParent().getWorldMatrix() * getRelativeMatrix();
	}

	void Entity::clear() {
		environment = nullptr;
		entHandle = entt::null;
		parentId = 0;
		entId = 0;
	}

	glm::mat4 Entity::getRelativeMatrix() const {
		return getComponent<TransformComponent>().getMatrix();
	}
} // namespace Deer
