#include "GameObject.h"

#include "Components/TagComponent.h"
#include "systems/Logger.h"

namespace OX
{
    GameObject::GameObject(const std::string &name)
        : m_name(name)
    {
        addComponent(std::make_unique<TagComponent>(name));
    }

    GameObject::~GameObject() = default;

    void GameObject::setName(const std::string &name) { m_name = name; }
    const std::string &GameObject::name() const { return m_name; }

    GameObject *GameObject::parent() const { return m_parent; }

    void GameObject::addChild(std::unique_ptr<GameObject> child)
    {
        child->m_parent = this;
        m_children.push_back(std::move(child));
    }

    std::unique_ptr<GameObject> GameObject::removeChild(GameObject *child)
    {
        auto it = std::ranges::find_if(m_children, [&](const auto &u)
        {
            return u.get() == child;
        });

        if (it == m_children.end()) return nullptr;

        child->m_parent = nullptr;
        auto out = std::move(*it);
        m_children.erase(it);
        return out;
    }


    const std::vector<std::unique_ptr<GameObject> > &GameObject::children() const
    {
        return m_children;
    }

    void GameObject::addComponent(std::unique_ptr<Component> comp)
    {
        comp->OnAttach(this);
        m_components.push_back(std::move(comp));
    }

    bool GameObject::IsAncestorOf(const GameObject *other) const
    {
        if (!other || !other->m_parent) return false;

        const GameObject *current = other->m_parent;
        int depth = 0;
        const int MAX_DEPTH = 1000;

        while (current != nullptr && depth < MAX_DEPTH) {
            if (current == this) {
                return true;
            }
            current = current->m_parent;
            depth++;
        }

        if (depth >= MAX_DEPTH) {
            Logger::LogWarning("Possible circular reference detected in GameObject hierarchy");
        }

        return false;
    }


    void GameObject::addExistingChild(GameObject *child)
    {
        if (!child || child == this || IsAncestorOf(child))
            return;

        // Remove from old parent
        if (GameObject *oldParent = child->parent()) {
            std::unique_ptr<GameObject> taken = oldParent->removeChild(child);
            if (!taken) return;
            taken->m_parent = this;
            m_children.push_back(std::move(taken));
        }
    }


    const std::vector<std::unique_ptr<Component> > &GameObject::components() const
    {
        return m_components;
    }

    YAML::Node GameObject::Serialize() const
    {
        YAML::Node node;
        node["name"] = m_name;

        for (auto &comp: m_components)
            node["components"].push_back(comp->Serialize());

        for (auto &child: m_children)
            node["children"].push_back(child->Serialize());

        return node;
    }

    void GameObject::Deserialize(const YAML::Node &node)
    {
        if (node["name"])
            m_name = node["name"].as<std::string>();

        if (node["components"]) {
            for (auto &cnode: node["components"]) {
                auto type = cnode["type"].as<std::string>();
                if (type == "TagComponent") {
                    auto name = cnode["name"].as<std::string>();
                    std::unique_ptr<Component> comp = std::make_unique<TagComponent>(name);
                    addComponent(std::move(comp));
                } else if (type == "TransformComponent") {
                    //auto name = cnode["name"].as<std::string>();
                    //std::unique_ptr<Component> comp = std::make_unique<TagComponent>(name);
                    //addComponent(std::move(comp));
                } else {
                    Logger::LogError("Invalid Component: '%s'", type.c_str());
                }
            }
        }

        if (node["children"]) {
            for (auto &cnode: node["children"]) {
                auto child = std::make_unique<GameObject>();
                child->Deserialize(cnode);
                addChild(std::move(child));
            }
        }
    }
} // namespace OX
