/******************************************************************************
*	File		-	assetBrowser.cpp
*	Author		-	Joey Pollack
*	Date		-	2021/11/10 (y/m/d)
*	Mod Date	-	2021/11/10 (y/m/d)
*	Description	-	Browse and manage project assets
******************************************************************************/

#include "asset_browser.h"
#include <core/core.h>
#include <input/input_manager.h>
#include <gui/gui.h>
#include <utils/logger.h>
#include <dearimgui/imgui.h>
#include <internal_data/data_manager.h>
#include <editor/editor.h>
#include <editor/contents/content_manager.h>
#include <editor/contents/editor_asset.h>
#include <editor/contents/world.h>
#include <editor/contents/script.h>
#include <renderer/texture.h>
#include <utils/helpers.h>

namespace lunarium
{
namespace editor
{
    AssetBrowser::AssetBrowser(std::filesystem::path dir, Editor* pEditor)
        : Panel("Asset Browser", PanelDockZone::DDZ_BOTTOM, true, (ImGuiWindowFlags)ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar),  
            mAssetDirectory(dir), mpEditor(pEditor), mSyncTree(false)
    {
        DefinePopups();
    }

    void AssetBrowser::SetAssetDirectory(std::filesystem::path dir)
    {
        mAssetDirectory = dir;
        mSelectedDir = dir;
    }
    

    std::filesystem::path AssetBrowser::GetSelectedDirectory()
    {
        return mSelectedDir;
    }

    ///////////////////////////////////////////////////////////////////
    //          DO FRAME        DO FRAME        DO FRAME 
    ///////////////////////////////////////////////////////////////////

    void AssetBrowser::DoFrame()
    {

        // Directory tree
        // TODO: Figure out a better way to size stuff other 
        //          than using this guess-work magic numbers
        float row_height = ImGui::GetFrameHeightWithSpacing() + 2.0f;
        ImVec2 wind_size = ImGui::GetWindowSize();
        wind_size.y -= (row_height + 8.0f);
        wind_size.x -= 15.0f;
        ImGui::SetCursorPosY(row_height);
        if (ImGui::BeginChild("Directory Tree", ImVec2(wind_size.x * 0.15f, wind_size.y), true, ImGuiWindowFlags_NoCollapse))
        {
            
            ImGui::SetNextItemOpen(true);
            DoDirTree(mAssetDirectory);
            mSyncTree = false;
        }
        ImGui::EndChild();

        ImGui::SetCursorPosY(row_height);
        ImGui::SetCursorPosX(ImGui::GetCursorPosX() + wind_size.x * 0.15f);

        // Contents
        
        DoContentArea(wind_size);

        //HandlePopupActions();
    }

    ///////////////////////////////////////////////////////////////////
    //          FRAME HELPERS           FRAME HELPERS
    ///////////////////////////////////////////////////////////////////

    //////////////////////////////////////////////////////////////
    // DIRECTORY TREE
    void AssetBrowser::DoDirTree(std::filesystem::path dir)
    {
        if ("" == dir)
            return;

        if (!std::filesystem::is_directory(dir))
            return;

        std::string next_node_name = dir.filename().string();
        if (mSyncTree)
        {
            if (mSelectedDir.string().find(next_node_name) != std::string::npos)
            {
                ImGui::SetNextItemOpen(true);
            }
            else
            {
                ImGui::SetNextItemOpen(false);
            }
        }

        // Doing this to prevent arrows from showing up on items that have no child directories
        // Seems a bit clumsy to iterate the directory twice though so this can probably
        // be cleaned up a bit
        int num_child_dirs = 0;
        for(auto const& dir_entry: std::filesystem::directory_iterator{dir})
        {   
            if (std::filesystem::is_directory(dir_entry))
            {
                num_child_dirs++;
            }
        }

        if (num_child_dirs < 1)
        {
            ImGui::SetCursorPosX(ImGui::GetCursorPosX() + ImGui::GetTreeNodeToLabelSpacing());
            if (ImGui::Selectable(next_node_name.c_str()))
            {
                mSelectedDir = dir;
            }
        }

        else 
        {
            if (ImGui::TreeNode(next_node_name.c_str()))
            {
                if (!mSyncTree)
                {
                    if (ImGui::IsItemClicked())
                    {
                        mSelectedDir = dir;
                        ImGui::SetNextItemOpen(false);
                    }
                    HandleAssetDrop(dir);                                           
                }
                for(auto const& dir_entry: std::filesystem::directory_iterator{dir})
                {
                    DoDirTree(dir_entry);
                }
                ImGui::TreePop();
            }
            else
            {
                if (ImGui::IsItemClicked())
                {
                    mSelectedDir = dir;
                    ImGui::SetNextItemOpen(false);
                }
            }
        }
    }

    //////////////////////////////////////////////////////////////
    // CONTENT AREA
    void AssetBrowser::DoContentArea(ImVec2 wind_size)
    {
        if (ImGui::BeginChild("Contents", ImVec2(wind_size.x * 0.85f, wind_size.y), true, ImGuiWindowFlags_NoCollapse))
        {
            if (std::filesystem::exists(mSelectedDir))
            {
                
                float yPos = ImGui::GetCursorPosY();
                float xPos = ImGui::GetCursorPosX();
                float left = xPos;

                
                ImGui::BeginChild("ToolBar", ImVec2(0, ImGui::GetFrameHeightWithSpacing() * 1.5f), false, ImGuiWindowFlags_NoScrollbar);
                ImGui::PushFont(GUI::GetInstance().GetFont(GuiFont::FONT_ROBO_SMALL));
                ImGui::TextUnformatted(mpEditor->GetProject()->MakeRelativeToAssets(mSelectedDir, true).string().c_str());
                ImGui::PopFont();

                // Prevent the back button from leaving the projects asset directory
                std::string parent = mSelectedDir.parent_path().lexically_normal().string();
                std::string assets_str = mpEditor->GetProject()->GetAssetDirectory().lexically_normal().string();
                size_t loc = parent.find(assets_str);
                if (ImGui::Button("Back") && (loc != std::string::npos))
                {
                    mSelectedDir = mSelectedDir.parent_path();
                    mSyncTree = true;
                }

                ImGui::SameLine();
                if (ImGui::ImageButton((ImTextureID)DataManager::mNewFolderIcon->GetGLID64(), 
                        ImVec2(DataManager::mNewFolderIcon->GetWidth(), DataManager::mNewFolderIcon->GetHeight()), 
                        ImVec2(0, 0), ImVec2(1, 1), 4))
                { 
                    
                    OpenPopup(PopUp::NEW_FOLDER).LogIfFailed(Editor::LogCat); 
                }

                ImGui::EndChild();

                ImGui::Separator();
                ImGui::BeginChild("Files");
                
                // List directories
                for (auto const &dir_entry : std::filesystem::directory_iterator{mSelectedDir})
                {
                    if (dir_entry.is_directory())
                    {
                        ImGui::Image((ImTextureID)DataManager::mFolderIcon->GetGLID64(),
                                     ImVec2(DataManager::mFolderIcon->GetWidth(), DataManager::mFolderIcon->GetHeight()));
                        ImGui::SameLine();
                        ImGui::Selectable(dir_entry.path().filename().string().c_str());
                        HandleAssetDrop(dir_entry.path());
                        if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
                        {
                            // TODO: Open relevant editor
                            //Logger::Info(Editor::LogCat, "Directory double clicked on. Open Folder");

                            // This will force open the tree nodes on the next update so the tree is synced with the content window
                            mSelectedDir = dir_entry.path();
                            mSyncTree = true;
                        }
                    }
                }


                // List Assets
                std::filesystem::path selected_final = mpEditor->GetProject()->MakeRelativeToAssets(mSelectedDir);
                std::vector<EditorAsset *> assets;
                ContentManager::GetInstance().GetAllAssetsInDirectory(assets, selected_final);

                for (auto iter = assets.begin(); iter != assets.end(); iter++)
                {
                    if (ImGui::Selectable((*iter)->GetFileLocation().stem().string().c_str()))
                    {
                        // Show properties if this is new selection (meaning wasn't selected last frame)
                        // Logger::Info(Editor::LogCat, "Asset selected. Properties shold show in the PropertiesView Panel");
                        mpEditor->OnAssetSelected((*iter));
                    }
                    if (ImGui::BeginDragDropSource())
                    {
                        // Need to pass a pointer to the payload data. This means we need to pass a
                        // pointer to the EditorAsset pointer (EditorAsset**) which &(*iter) becomes
                        ImGui::SetDragDropPayload("EditorAsset", (void *)&(*iter), sizeof(EditorAsset *));
                        ImGui::Text("%s", (*iter)->GetFileLocation().stem().string().c_str());
                        ImGui::EndDragDropSource();
                    }

                    if (ImGui::IsItemHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
                    {
                        // Logger::Info(Editor::LogCat, "Asset double clicked on. Relevant editor should open!");
                        //HandleAssetDoubleClick((*iter));

                        mpEditor->OnAssetOpen((*iter));
                    }

                    if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Right))
                    {

                        // TODO: CONTEXT MENU FOR ITEMS - Test Removing/Trashing
                    }
                }
                
                DoContentContextMenu();

                ImGui::EndChild();
            }
        }
        ImGui::EndChild();
    }

    //////////////////////////////////////////////////////////////
    // CONTEXT MENUS
    void AssetBrowser::DoContentContextMenu()
    {
        if (ImGui::BeginPopupContextWindow(0, 1, false))
        {
            if (ImGui::Button("New Folder"))
            {
                //mNewFolder = true;     
                OpenPopup(PopUp::NEW_FOLDER).LogIfFailed(Editor::LogCat);
            }

            if (ImGui::BeginMenu("New Asset"))
            {
                if (ImGui::MenuItem("New World"))
                {
                    OpenPopup(PopUp::NEW_WORLD).LogIfFailed(Editor::LogCat);
                }

                if (ImGui::MenuItem("New Script"))
                {
                    OpenPopup(PopUp::NEW_SCRIPT).LogIfFailed(Editor::LogCat);
                }
                ImGui::EndMenu();
            }

            if (ImGui::BeginMenu("Import Asset"))
            {
                if (ImGui::MenuItem("Image"))
                {
                    std::filesystem::path outPath;

                    // prepare filters for the dialog
                    FileSystem::FilterItem filterItem[3] = {{"Image file", "png"}, {"Image File", "jpg"}, {"Image File", "jpeg"}};

                    // show the dialog
                    FileSystem::DialogResult result = FileSystem::OpenFileDialog(outPath, filterItem, 3);
                    if (result == FileSystem::DialogResult::OK)
                    {
                        Logger::Info(Editor::LogCat, "Importing asset: %s", outPath.string().c_str());

                        LUUID id = 0;
                        ContentManager::GetInstance().ImportFile(outPath, mSelectedDir / outPath.filename(), 
                                                                    AssetType::EATYPE_IMAGE, id).LogIfFailed(Editor::LogCat);
                    }
                }
                ImGui::EndMenu();
            }

            // TODO: Buttons for creating/importing new assets
            ImGui::EndPopup();
        }
    }

    //////////////////////////////////////////////////////////////
    // ASSET DROP HELPER
    void AssetBrowser::HandleAssetDrop(std::filesystem::path dir)
    {
        if (ImGui::BeginDragDropTarget())
        {
            if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("EditorAsset"))
            {
                EditorAsset* pAsset = *((EditorAsset**) payload->Data);                
                ContentManager::GetInstance().MoveAsset(pAsset, dir / pAsset->GetFileLocation().filename());
            }
            ImGui::EndDragDropTarget();
        }
    }

    ///////////////////////////////////////////////////////////////////
    //          POPUP PANELS            POPUP PANELS   
    ///////////////////////////////////////////////////////////////////

    void AssetBrowser::DefinePopups()
    {
        // New Folder
        AddPopup(PopUp::NEW_FOLDER, "New Folder Name", [](Panel *p)
            {
                bool stay_open = true;
                AssetBrowser* ab = (AssetBrowser*)p;
                char name_buf[64] = "New Folder";
                ImGui::TextUnformatted("Folder Name: ");
                ImGui::SameLine();
                if (ImGui::InputText("##Folder Name", name_buf, 64, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue))
                {
                    std::filesystem::create_directory(ab->mSelectedDir / name_buf);
                    stay_open = false;
                    ImGui::CloseCurrentPopup();
                }

                if (Core::Input().IsKeyDown(KeyCode::ESCAPE, true))
                {
                    stay_open = false;
                    ImGui::CloseCurrentPopup();
                }
                return stay_open; 
            }).LogIfFailed(Editor::LogCat);

            // New World
            AddPopup(PopUp::NEW_WORLD, "New World Name", [](Panel *p)
            {
                bool stay_open = true;
                AssetBrowser* ab = (AssetBrowser*)p;
                char name_buf[64] = "New World";
                ImGui::TextUnformatted("World Name: ");
                ImGui::SameLine();
                if (ImGui::InputText("##World Name", name_buf, 64, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue))
                {
                    auto assets_dir = ab->mpEditor->GetProject()->MakeRelativeToAssets(ab->mSelectedDir);
                    EditorAsset* pAsset = new editor::World(assets_dir / name_buf);
                    LUUID id;
                    
                    ContentManager::GetInstance().AddGeneratedAsset(pAsset, id).LogIfFailed(Editor::LogCat);
                    stay_open = false;
                    ImGui::CloseCurrentPopup();
                }

                if (Core::Input().IsKeyDown(KeyCode::ESCAPE, true))
                {
                    stay_open = false;
                    ImGui::CloseCurrentPopup();
                }
                return stay_open; 
            }).LogIfFailed(Editor::LogCat);

            // New script asset
            AddPopup(PopUp::NEW_SCRIPT, "New Script Name", [](Panel *p)
            {
                bool stay_open = true;
                AssetBrowser* ab = (AssetBrowser*)p;
                char name_buf[64] = "New Script";
                ImGui::TextUnformatted("Script Name: ");
                ImGui::SameLine();
                if (ImGui::InputText("##Script Name", name_buf, 64, ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue))
                {
                    auto assets_dir = ab->mpEditor->GetProject()->MakeRelativeToAssets(ab->mSelectedDir);
                    std::string sname_buf = name_buf;
                    sname_buf.append(".wren");
                    std::filesystem::path location = assets_dir / sname_buf;
                    EditorAsset* pAsset = new editor::Script(location, ab->mpEditor->GetProject()->GetAssetDirectory());
                    LUUID id;
                    
                    ContentManager::GetInstance().AddGeneratedAsset(pAsset, id).LogIfFailed(Editor::LogCat);
                    stay_open = false;
                    ImGui::CloseCurrentPopup();
                }

                if (Core::Input().IsKeyDown(KeyCode::ESCAPE, true))
                {
                    stay_open = false;
                    ImGui::CloseCurrentPopup();
                }
                return stay_open; 
            }).LogIfFailed(Editor::LogCat);

    }

    
    
    // void AssetBrowser::HandleAssetDoubleClick(EditorAsset* pAsset)
    // {
    //     if (pAsset->GetType() == AssetType::EATYPE_WORLD)
    //     {
    //         World* pAssetWorld = (World*)pAsset;
    //         mpEditor->ChangeWorld(pAssetWorld->GetWorld());
    //     }
    // }
    
}
}
