/******************************************************************************
*	File		-	entity.cpp
*	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
******************************************************************************/

#include "entity.h"
#include "world.h"
#include "components.h"
#include <utils/logger.h>

namespace lunarium
{
    Entity::Entity(World& w)
        : mHandle(entt::null), mWorld(w)
    {
        mUUID = UUID::GetNewID();
        Init();
    }

    Entity::Entity(World& w, LUUID uuid, entt::entity handle)
        : mHandle(handle), mWorld(w), mUUID(uuid)
    {
        Init();
    }

    void Entity::Init()
    {
        mHandle = mWorld.GetEntityRegistry()->create();
    }

    LUUID Entity::GetUUID() const
    {
        return mUUID;
    }

    
    bool Entity::HasChildren() const
    {
        return mChildren.size() > 0;
    }

    
    void Entity::AddChild(Entity* pChild)
    {
        mChildren.push_back(pChild);
    }


    /////////////////////////////////////////////////////////////////////
    //				SERIALIZING
    /////////////////////////////////////////////////////////////////////
    OpRes Entity::Serialize(nlohmann::ordered_json& node)
    {
        #if !BUILD_NO_EDITOR // Only does this when this is an editor build

        node["UUID"] = mUUID;

        auto& components = node["components"];
        if (HasComponent<TagComponent>())
        {
            nlohmann::ordered_json tag;
            tag["type_name"] = "TagComponent";
            tag["info"] = GetComponent<TagComponent>().Info;
            components.emplace_back(tag);
        }

        if (HasComponent<TransformComponent>())
        {
            nlohmann::ordered_json transform;
            TransformComponent& comp = GetComponent<TransformComponent>();
            transform["type_name"] = "TransformComponent";

            auto& pos =  transform["position"];
            pos["x"] = comp.Position.x;
            pos["y"] = comp.Position.y;
            pos["z"] = comp.Position.z;

            auto& rot =  transform["rotation"];
            rot["x"] = comp.Rotation.x;
            rot["y"] = comp.Rotation.y;
            rot["z"] = comp.Rotation.z;

            auto& scale =  transform["scale"];
            scale["x"] = comp.Scale.x;
            scale["y"] = comp.Scale.y;
            scale["z"] = comp.Scale.z;

            components.emplace_back(transform);
        }


        // TODO: ADD CODE TO SERIALIZE ANY NEW COMPONENTS


        // Children
        auto& children = node["children"];
        for (int i = 0; i < mChildren.size(); i++)
        {
            nlohmann::ordered_json child;
            mChildren[i]->Serialize(child).LogIfFailed(LogCategory::GAME_SYSTEM);
            children.emplace_back(child);
        }

        #endif
        return OpRes::OK();
    }

    OpRes Entity::Deserialize(nlohmann::ordered_json& node)
    {
        #if !BUILD_NO_EDITOR // Only does this when this is an editor build

        mUUID = node["UUID"].get<u64>();

        // TODO: Load components
        auto& components = node["components"];
        for (auto iter = components.begin(); iter != components.end(); iter++)
        {
            auto& comp = *iter;

            std::string comp_type_name = comp["type_name"].get<std::string>();

            if ("TagComponent" == comp_type_name)
            {
                std::string info = comp["info"].get<std::string>();
                AddComponent<TagComponent>(info);
            }

            if ("TransformComponent" == comp_type_name)
            {
                auto& pos = comp["position"];
                float x = pos["x"].get<f32>();
                float y = pos["y"].get<f32>();
                float z = pos["z"].get<f32>();
                glm::vec3 position(x, y, z);

                auto& rot = comp["rotation"];
                x = rot["x"].get<f32>();
                y = rot["y"].get<f32>();
                z = rot["z"].get<f32>();
                glm::vec3 rotation(x, y, z);

                auto& sc = comp["scale"];
                x = sc["x"].get<f32>();
                y = sc["y"].get<f32>();
                z = sc["z"].get<f32>();
                glm::vec3 scale(x, y, z);

                AddComponent<TransformComponent>(position, rotation, scale);
            }
        }

        
        // TODO: ADD CODE TO DESERIALIZE ANY NEW COMPONENTS

        // TODO: Load children
        auto& children = node["children"];
        for (auto iter = children.begin(); iter != children.end(); iter++)
        {
            auto& child = *iter;

            Entity* ne = new Entity(mWorld);
            ne->Deserialize(child).LogIfFailed(LogCategory::GAME_SYSTEM);
            mChildren.push_back(ne);
        }

        #endif
        return OpRes::OK();
    }
    
    bool Entity::IsValidNode(nlohmann::ordered_json& node)
    {
        if (node["UUID"].is_null()) { return false; }
        if (!node["UUID"].is_number()) { return false; }

        return true;
    }
    
    nlohmann::ordered_json Entity::AsJSON()
    {
        #if !BUILD_NO_EDITOR // Only does this when this is an editor build
        nlohmann::ordered_json node;

        Serialize(node).LogIfFailed(LogCategory::GAME_SYSTEM);

        return node;
        #endif

        return nlohmann::ordered_json();
    }
}