/******************************************************************************
*	File		-	panel_manager.cpp
*	Author		-	Joey Pollack
*	Date		-	2022/02/07 (y/m/d)
*	Mod Date	-	2022/02/07 (y/m/d)
*	Description	-	Manage the editor panels
******************************************************************************/

#include "panel_manager.h"
#include "editor.h"
#include <utils/logger.h>
#include <dearimgui/imgui_internal.h>   // To use the DockWindowXXX methods
#include <filesystem>

// Panels
#include "panels/about.h"
#include "panels/asset_browser.h"
#include "panels/world_tree.h"
#include "panels/world_view.h"
#include "panels/properties_view.h"

using namespace lunarium::gui;

namespace lunarium { namespace editor
{
    PanelManager::PanelManager()
        : mpEditor(nullptr), mResetDockSpace(false), mDockSpaceID(0), mSplitBottom(false)
    {

    }

    OpRes PanelManager::Initialize(Editor* editor, std::string name, bool split_bottom)
    {
        mpEditor = editor;
        mName = name;
        mSplitBottom = split_bottom;

        memset(&mWindowClass, 0, sizeof(ImGuiWindowClass));
        mWindowClass.ClassId = mpEditor->GetNextWindowID();
        
        if (!std::filesystem::exists("imgui.ini"))
        {
            mResetDockSpace = true;
        }

        return OpRes::OK();
    }

    void PanelManager::Shutdown()
    {
        DestoryPanels();
    }

    
    OpRes PanelManager::AddPanel(gui::Panel* panel, uint32_t& id)
    {
        if (mPanelsByName.find(panel->GetName()) != mPanelsByName.end())
        {
            return OpRes::Fail("Cannot add panel - panel already exists. Panel Name: %s", panel->GetName());
        }

        mPanelsByName[panel->GetName()] = panel;
        mPanels.push_back(panel);
        id = mPanels.size() - 1;

        return OpRes::OK();
    }

    void PanelManager::OpenPanel(uint32_t id)
    {
        if (mPanels.size() >= id || id < 0)
            return;

        mPanels[id]->SetOpen(true);
    }

    void PanelManager::ClosePanel(uint32_t id)
    {
        if (mPanels.size() >= id || id < 0)
            return;

        mPanels[id]->SetOpen(false);
    }

    bool PanelManager::IsOpen(uint32_t id)
    {
        if (mPanels.size() >= id || id < 0)
            return false;

        return mPanels[id]->IsOpen();
    }

    Panel* PanelManager::GetPanel(uint32_t id)
    {
        if (id >= 0 && id < mPanels.size())
        {
            return mPanels[id];
        }

        Logger::Warn(mpEditor->GetLogCat(), "Requested panel not found: %d", id);
        return nullptr;
    }

    const ImGuiWindowClass* PanelManager::GetWindowClass() const
    {
        return &mWindowClass;
    }


    bool PanelManager::IsBottomSplit() const
    {
        return mSplitBottom;
    }

    void PanelManager::SetSplitBottom(bool split_bottom)
    {
        mSplitBottom = split_bottom;
    }

    void PanelManager::ResetDocking()
    {
        mResetDockSpace = true;
    }
    
    void PanelManager::OnTick(double delta)
    {
        for (auto iter = mPanels.begin(); iter != mPanels.end(); iter++)
        {
            if ((*iter)->IsOpen())
            {
                (*iter)->Update(delta);
            }
        }
    }

    void PanelManager::RenderPanels()
    {
        for (auto iter = mPanels.begin(); iter != mPanels.end(); iter++)
        {
            if ((*iter)->IsOpen())
            {
                ImGui::SetNextWindowClass(&mWindowClass);
                (*iter)->DoFrame();
            }
        }
    }

    ////////////////////////////////////////////////////////////
    //				HELPER METHODS
    ////////////////////////////////////////////////////////////
    void PanelManager::DestoryPanels()
    {
        for (auto iter = mPanels.begin(); iter != mPanels.end(); iter++)
        {
            delete (*iter);
        }

        mPanels.clear();
    }

    void PanelManager::MakeDockSpaces()
    {
        //ImGuiViewport* Viewport = ImGui::GetMainViewport();
        ImGuiViewport* Viewport = ImGui::GetWindowViewport();
        ImGuiID windowID = mpEditor->GetNextWindowID();
        std::string dock_name = mName;
        dock_name += " DockSpace";
        mDockSpaceID = ImGui::DockSpace(ImGui::GetID(dock_name.c_str()), ImVec2(0, 0), 0, &mWindowClass);
        if (!ImGui::DockBuilderGetNode(mDockSpaceID) || mResetDockSpace) 
        {
            Logger::Trace(mpEditor->GetLogCat(), "Resetting Dockspace: %s", dock_name.c_str());

            mResetDockSpace = false;
            ImGui::DockBuilderRemoveNode(mDockSpaceID);
            ImGui::DockBuilderAddNode(mDockSpaceID, ImGuiDockNodeFlags_DockSpace);
            ImGui::DockBuilderSetNodeSize(mDockSpaceID, Viewport->Size);
            //ImGui::DockBuilderSetNodePos(mDockSpaces.Main, Viewport->WorkPos);

            ImGui::DockBuilderSplitNode(mDockSpaceID, ImGuiDir_Down, 0.25f, &mDockZoneIDs[PanelDockZone::DDZ_BOTTOM], &mDockZoneIDs[PanelDockZone::DDZ_CENTER]);
            
            if (mSplitBottom)
            {
                ImGui::DockBuilderSplitNode(mDockZoneIDs[PanelDockZone::DDZ_BOTTOM], ImGuiDir_Left, 0.2f, &mDockZoneIDs[PanelDockZone::DDZ_BOT_LEFT], &mDockZoneIDs[PanelDockZone::DDZ_BOT_RIGHT]);
            }

            ImGui::DockBuilderSplitNode(mDockZoneIDs[PanelDockZone::DDZ_CENTER], ImGuiDir_Right, 0.2f, &mDockZoneIDs[PanelDockZone::DDZ_RIGHT], &mDockZoneIDs[PanelDockZone::DDZ_CENTER]);
            ImGui::DockBuilderSplitNode(mDockZoneIDs[PanelDockZone::DDZ_CENTER], ImGuiDir_Left, 0.2f, &mDockZoneIDs[PanelDockZone::DDZ_LEFT], &mDockZoneIDs[PanelDockZone::DDZ_CENTER]);
            ImGui::DockBuilderFinish(mDockSpaceID);

            // Dock Panels
            for (auto iter = mPanels.begin(); iter != mPanels.end(); iter++)
            {
                PanelDockZone dz = (*iter)->GetDockZone();
                if (dz != PanelDockZone::DDZ_NONE)
                {
                    // If the panel expects the bottom node to be split but it isn't
                    // we need to change it to just dock to the bottom
                    if ((dz == PanelDockZone::DDZ_BOT_LEFT ||
                        dz == PanelDockZone::DDZ_BOT_RIGHT) &&
                        !mSplitBottom)
                        {
                            dz = PanelDockZone::DDZ_BOTTOM;
                        }

                    // Also need to account for the opposite situation
                    // Default it to the bottom left
                    if (dz == PanelDockZone::DDZ_BOTTOM && mSplitBottom)
                    {
                        dz = PanelDockZone::DDZ_BOT_LEFT;
                    }

                    ImGui::DockBuilderDockWindow((*iter)->GetName(), mDockZoneIDs[dz]);
                }
            }
        }

    }
}}