/******************************************************************************
*	File		-	entity.h
*	Author		-	Joey Pollack
*	Date		-	2022/05/23 (y/m/d)
*	Mod Date	-	2022/05/23 (y/m/d)
*	Description	-	Provides functionality to work with world entities
******************************************************************************/

#ifndef LUNARIUM_ENTITY_H_
#define LUNARIUM_ENTITY_H_

#include <utils/uuid.h>
#include <utils/logger.h>
#include <entt/entt.hpp>
#include <world/world.h>
#include <assets/serializing/json_serializable.h>


namespace lunarium // TODO: : public JSONSerializable
{
    class Entity : public JSONSerializable
    {
    public:
        Entity(World& w);
        Entity(World& w, LUUID uuid, entt::entity handle = entt::null);

        // These component methods are based on methods from the Hazel engine, written by Cherno
        // https://github.com/TheCherno/Hazel/blob/dev/Hazel/src/Hazel/Scene/Entity.h
        template<typename T, typename... Args>
		T& AddComponent(Args&&... args)
		{
			if (HasComponent<T>())
            {
                Logger::Error(LogCategory::GAME_SYSTEM, "Entity already has component!");
                return GetComponent<T>();
            }
			T& component = mWorld.GetEntityRegistry()->emplace<T>(mHandle, std::forward<Args>(args)...);
			return component;
		}

        template<typename T>
		T& GetComponent()
		{
			if (!HasComponent<T>())
            {
                Logger::Error(LogCategory::GAME_SYSTEM, "Entity does not have component!");
            }
			return mWorld.GetEntityRegistry()->get<T>(mHandle);
		}

		template<typename T>
		bool HasComponent()
		{
			return mWorld.GetEntityRegistry()->all_of<T>(mHandle);
		}

		template<typename T>
		void RemoveComponent()
		{
			if (!HasComponent<T>())
            {
                Logger::Error(LogCategory::GAME_SYSTEM, "Entity does not have component!");
            }
			mWorld.GetEntityRegistry()->remove<T>(mHandle);
		}

        LUUID GetUUID() const;

        bool HasChildren() const;
        void AddChild(Entity* pChild);

        // Serializing
        [[nodiscard]] virtual OpRes Serialize(nlohmann::ordered_json& node);
        [[nodiscard]] virtual OpRes Deserialize(nlohmann::ordered_json& node);
        [[nodiscord]] virtual bool IsValidNode(nlohmann::ordered_json& node);
        [[nodiscard]] virtual nlohmann::ordered_json AsJSON();

    private:
        LUUID mUUID;
        entt::entity mHandle;
        World& mWorld; // The world the entity is contained within
        std::vector<Entity*> mChildren;

    private:
        void Init();
    };
}

#endif  // LUNARIUM_ENTITY_H_