/******************************************************************************
 *	File		-	worldTree.cpp
 *	Author		-	Joey Pollack
 *	Date		-	2021/11/04 (y/m/d)
 *	Mod Date	-	2021/11/04 (y/m/d)
 *	Description	-	The tree view listing all objects in the world
 ******************************************************************************/

#include "world_tree.h"
#include <editor/editor.h>
#include <core/core.h>
#include <world/world.h>
#include <world/entity.h>
#include <world/components.h>
#include <dearimgui/imgui.h>
#include <editor/editor.h>
#include <utils/logger.h>

namespace lunarium { namespace editor
{
    WorldTree::WorldTree(Editor *editor)
        : Panel("World Tree", PanelDockZone::DDZ_LEFT, true, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar),
            mpEditor(editor),
            // TODO: Temp world created here
            mpWorld(nullptr),
            mpSelectedEntity(nullptr), mNewChild(false), mParentEnt(nullptr), mCMenuOpen(false)
    {
        AddPopup(PopUp::NEW_ENTITY, "New Entity", [](Panel *p)
            {
                WorldTree* pWT = (WorldTree*)p;
                if (!pWT->mpWorld)
                {
                    return false;
                }

                char name_buf[64] = "NewEntity";
                ImGui::TextUnformatted("Entity Name: ");
                ImGui::SameLine();
                if (ImGui::InputText("##Entity Name", name_buf, 64, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue))
                {
                    LUUID id = pWT->mpWorld->CreateEntity();
                    pWT->mpWorld->GetEntity(id)->SetName(name_buf);

                    if (pWT->mNewChild)
                    {
                        pWT->mParentEnt->AddChild(id);
                    }

                    pWT->mNewChild = false;
                    pWT->mParentEnt = nullptr;
                    return false;
                }

                if (Core::Input().IsKeyDown(KeyCode::ESCAPE, true))
                {
                    pWT->mNewChild = false;
                    return false;
                } 

                return true;
        }).LogIfFailed(Editor::LogCat);
    }

    void WorldTree::SetWorld(lunarium::World *pWorld)
    {
        mpWorld = pWorld;
        mpSelectedEntity = nullptr;
    }

    lunarium::World *WorldTree::GetWorld()
    {
        return mpWorld;
    }

    void WorldTree::DoFrame()
    {
        Entity *pSelection = nullptr;
        if (!mpWorld)
        {
            
            ImGui::Text("No World Loaded");
            return;
        }

        // Drop onto panel itself?
        // TODO: Nope this does not work for dropping onto the panel
        // if (ImGui::BeginDragDropTarget())
        // {
        //     if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("Entity"))
        //     {
        //         Entity *pDroppedEnt = *((Entity **)payload->Data);

        //         if (pDroppedEnt->HasParent())
        //             mpWorld->GetEntity(pDroppedEnt->GetParent())->RemoveChild(pDroppedEnt->GetUUID());
                
        //     }
        //     ImGui::EndDragDropTarget();
        // }

        ImGui::SetNextItemOpen(true);

        if (ImGui::TreeNode("World Root"))
        {
            if (ImGui::BeginDragDropTarget())
            {
                if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("Entity"))
                {
                    Entity *pDroppedEnt = *((Entity **)payload->Data);

                    if (pDroppedEnt->HasParent())
                        mpWorld->GetEntity(pDroppedEnt->GetParent())->RemoveChild(pDroppedEnt->GetUUID());
                }
                ImGui::EndDragDropTarget();
            }

            // List all world entities
            int idx = 0;
            for (auto iter = mpWorld->EntitiesBegin(); !mpWorld->EntitiesIsEnd(iter); iter++, idx++)
            {
                if (!(*iter)->HasComponent<ParentEntityComponent>())
                {
                    ShowEntity((*iter));
                }
            }

            ImGui::TreePop();
        }

        if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && ImGui::IsWindowHovered())
        {
            mpSelectedEntity = nullptr;
            mpEditor->OnEntitySelect(nullptr);
        }

        DoContextMenu();
    }

    
    void WorldTree::ShowEntity(Entity* pEnt)
    {
        bool was_clicked = false;
        bool was_right_clicked = false;

        bool node_open = false;
        if (pEnt->HasChildren())
        {
            node_open = ImGui::TreeNode(pEnt->GetName().c_str());
        }
        else
        {
            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
            ImGui::Selectable(pEnt->GetName().c_str());
            was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left);
            was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right);
        }

        // Handle drag and drop even if the node is closed
        if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID))
        {
            // Need to pass a pointer to the payload data. This means we need to pass a
            // pointer to the Entity pointer (Entity**) which &pEnt becomes
            ImGui::SetDragDropPayload("Entity", (void *)&pEnt, sizeof(Entity *));
            ImGui::Text("%s", pEnt->GetName().c_str());
            ImGui::EndDragDropSource();
        }

        if (ImGui::BeginDragDropTarget())
        {
            if (const ImGuiPayload *payload = ImGui::AcceptDragDropPayload("Entity"))
            {
                Entity *pDroppedEnt = *((Entity **)payload->Data);

                // Can't drop and entity on itself
                if (pDroppedEnt != pEnt)
                {
                    // If the dropped ent is the parent of pEnt then we need to swap the relationship
                    if (pEnt->HasParent() && pEnt->GetParent() == pDroppedEnt->GetUUID())
                    {
                        pDroppedEnt->RemoveChild(pEnt->GetUUID());
                        pEnt->AddChild(pDroppedEnt->GetUUID());
                    }

                    // If the dropped ent is already a child of pEnt then we do nothing

                    // Else add dropped ent as a child of pEnt
                    else if (!pDroppedEnt->HasParent() || (pDroppedEnt->GetParent() != pEnt->GetUUID()))
                    {
                        if (pDroppedEnt->HasParent())
                            mpWorld->GetEntity(pDroppedEnt->GetParent())->RemoveChild(pDroppedEnt->GetUUID());

                        pEnt->AddChild(pDroppedEnt->GetUUID());
                    }
                }
            }
            ImGui::EndDragDropTarget();
        }

        // Now handle the node if it's open
        if (node_open)
        {
            was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left);
            was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right);
            
            if (pEnt->HasChildren())
            {
                const std::vector<LUUID>& children = pEnt->GetChildren();
                // for (auto iter = children.begin(); iter != children.end(); iter++)
                // {
                //     Entity* pe = mpWorld->GetEntity((*iter));
                //     ShowEntity(pe);
                // }
                for (int i = 0; i < children.size(); i++)
                {
                    Entity* pe = mpWorld->GetEntity(children[i]);
                    ShowEntity(pe);
                }
            }
            

            ImGui::TreePop();
        }
        else
        {
            // was_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Left);
            // was_right_clicked = ImGui::IsItemClicked(ImGuiMouseButton_Right);
        }

        if (was_clicked)
        {      
            mpSelectedEntity = pEnt;
            mpEditor->OnEntitySelect(pEnt);
        }
        if (was_right_clicked)
        {
            mNewChild = true;
            mParentEnt = pEnt;
        }
    }

    void WorldTree::DoContextMenu()
    {
        if (ImGui::BeginPopupContextWindow(0, ImGuiPopupFlags_MouseButtonRight))
        {
            if (ImGui::Button("Create New Entity"))
            {
                if (!mpWorld)
                {
                    Logger::Warn(Editor::LogCat, "Can not create new entity - World pointer is null");
                }
                else
                {
                    OpenPopup(PopUp::NEW_ENTITY).LogIfFailed(Editor::LogCat);
                }
            }

            if (mNewChild)
            {
                mCMenuOpen = true;
                if (ImGui::Button("Delete Entity"))
                {
                    // mParentEnt is the entity that was right-clicked on
                    // Lets give it a less confusing name for this context
                    Entity* ToRemove = mParentEnt;

                    // Give the editor a chance to clean up any refs to this entity
                    mpEditor->OnEntityDelete(ToRemove);

                    // If this entity has a parent we need to patch some relationships
                    bool has_parent = false;
                    LUUID parent_id = 0;
                    if (ToRemove->HasComponent<ParentEntityComponent>())
                    {
                        // These will be used to patch the children to the grandparent - now the parent
                        parent_id = ToRemove->GetComponent<ParentEntityComponent>().Parent;
                        has_parent = true;

                        // Tell the parent this child does not exist anymore
                        mpWorld->GetEntity(parent_id)->RemoveChild(ToRemove->GetUUID());
                    }

                    if (ToRemove->HasChildren())
                    {
                        auto children = ToRemove->GetChildren();
                        for (LUUID child : children)
                        {
                            // Clear the parent for each child
                            mpWorld->GetEntity(child)->ClearParent();

                            if (has_parent)
                            {
                                // Make the grandparent the new parent (if there is a grandparent)
                                mpWorld->GetEntity(parent_id)->AddChild(child);
                            }
                        }
                    }

                    // Relationships are all patched so finally remove the entity
                    mpWorld->RemoveEntity(ToRemove->GetUUID());
                }

                if (ImGui::Button("Delete Entity Hierarchy"))
                {
                    // mParentEnt is the entity that was right-clicked on
                    // Lets give it a less confusing name for this context
                    Entity *ToRemove = mParentEnt;

                    // Give the editor a chance to clean up any refs to this entity
                    mpEditor->OnEntityDelete(ToRemove);

                    // If this entity has a parent we need to tell it we don't exist anymore
                    LUUID parent_id = 0;
                    if (ToRemove->HasComponent<ParentEntityComponent>())
                    {
                        parent_id = ToRemove->GetComponent<ParentEntityComponent>().Parent;
                        mpWorld->GetEntity(parent_id)->RemoveChild(ToRemove->GetUUID());
                    }

                    if (ToRemove->HasChildren())
                    {
                        // Recursively remove all children
                        auto children = ToRemove->GetChildren();
                        for (LUUID child : children)
                        {
                            RecursiveEntDelete(mpWorld->GetEntity(child));
                        }
                    }

                    // All children are removed so remove the entity
                    mpWorld->RemoveEntity(ToRemove->GetUUID());
                }
            }
            ImGui::EndPopup();
        }

        else if (mNewChild && mCMenuOpen)
        {
            mNewChild = false;
            mCMenuOpen = false;
            mParentEnt = nullptr;
        }
        
    }

    
    void WorldTree::RecursiveEntDelete(Entity* pe)
    {
        mpEditor->OnEntityDelete(pe);

        if (pe->HasChildren())
        {
            auto children = pe->GetChildren();
            for (LUUID child : children)
            {
                RecursiveEntDelete(mpWorld->GetEntity(child));
            }
        }

        mpWorld->RemoveEntity(pe->GetUUID());
    }

}}