#include "SceneExplorer.h"
#include "Core.h"
#include "Editor.h"
#include "systems/Scene/Scene.h"
#include "systems/Scene/GameObject.h"
#include "systems/Scene/Components/TagComponent.h"
#include <imgui.h>

namespace OX
{
    static inline const char *GetTag(GameObject *obj)
    {
        auto *tag = obj->GetComponent<TagComponent>();
        return tag ? tag->name.c_str() : "Unnamed";
    }

    static void ReparentObject(GameObject *object, GameObject *newParent)
    {
        if (!object || !newParent || object == newParent || object->IsAncestorOf(newParent))
            return;

        GameObject *oldParent = object->parent();
        if (oldParent)
            oldParent->removeChild(object);

        newParent->addExistingChild(object);
    }

    static void DrawGameObject(GameObject *obj, Editor &editor)
    {
        if (!obj) return;

        ImGui::PushID(obj);
        const char *tag = GetTag(obj);
        bool opened = ImGui::TreeNodeEx(tag, ImGuiTreeNodeFlags_DefaultOpen);

        // Selection
        if (ImGui::IsItemClicked(ImGuiMouseButton_Left))
            editor.SetSelected(obj);

        // Context Menu
        if (ImGui::BeginPopupContextItem()) {
            if (ImGui::MenuItem("Add Child")) {
                auto child = std::make_unique<GameObject>("GameObject");
                GameObject *ptr = child.get();
                obj->addChild(std::move(child));
                ptr->addComponent(std::make_unique<TagComponent>("GameObject"));
            }
            ImGui::EndPopup();
        }

        // Drag Source
        if (ImGui::BeginDragDropSource()) {
            ImGui::SetDragDropPayload("SCENE_OBJECT", &obj, sizeof(GameObject *));
            ImGui::Text("Move: %s", tag);
            ImGui::EndDragDropSource();
        }

        // Drop Target
        if (ImGui::BeginDragDropTarget()) {
            if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("SCENE_OBJECT")) {
                auto *dropped = *(GameObject **) payload->Data;
                if (dropped && dropped != obj)
                    ReparentObject(dropped, obj);
            }
            ImGui::EndDragDropTarget();
        }

        // Children
        if (opened) {
            const auto &children = obj->children();
            for (const auto &child: children)
                DrawGameObject(child.get(), editor);
            ImGui::TreePop();
        }

        ImGui::PopID();
    }

    void SceneExplorer::Draw(Core &core, Editor &editor)
    {
        if (!ImGui::Begin("Scene Explorer")) {
            ImGui::End();
            return;
        }

        auto &roots = core.GetScene().roots();
        for (const auto &root: roots)
            DrawGameObject(root.get(), editor);

        ImGui::End();
    }
}
