/******************************************************************************
*	File		-	fileBrowser.cpp
*	Author		-	Joey Pollack
*	Date		-	2021/11/04 (y/m/d)
*	Mod Date	-	2021/11/04 (y/m/d)
*	Description	-	File browser dialog window. Can be used for opening
*                   and saving files.
******************************************************************************/

#include "file_browser.h"

#include <algorithm>
#include <cstring>
#include <core/core.h>
#include <assets/types/image.h>
#include <graphics/graphics.h>
#include <internal_data/dataManager.h>
#include <utils/stb/stb_image.h>
#include "dearimgui/imgui.h"

namespace lunarium
{
    FileBrowser::FileBrowser()
        : mIsOpen(false), mSelectionMode(SelectionMode::FILES_ONLY), mWarnOnExisting(false),  mResult(Result::CANCEL)
            
    {
        mPrompt = "Select an item";
        memset(mDirNameBuffer, 0, mBufferSize);
        memset(mInputBuffer, 0, mBufferSize);
    }

    /// If the given path does not exist this will default to the
    /// current working directory
    bool FileBrowser::OpenInDirectory(std::filesystem::path path)
    {
        if (mIsOpen)
            return false;

        if (!std::filesystem::exists(path))
            path = std::filesystem::current_path();

        if (!std::filesystem::is_directory(path))
            return false;

        mCurrentDirectory = path;
        mResult = Result::STILL_WAITING;

        // Get list of items in the current directory
        ReloadItems();

        mIsOpen = true;
        return true;
    }
    
    bool FileBrowser::DoFrame()
    {
        if (!mIsOpen)
            return false;

        ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_Appearing);
        if (!ImGui::Begin("File Browser", &mIsOpen, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking))
        {   
            ImGui::End();
            return false;
        }

        ImGui::TextUnformatted(mPrompt.c_str());
        ImGui::Separator();

        ImVec2 iconSize(DataManager::mNewFolderIcon->GetWidth(), DataManager::mNewFolderIcon->GetHeight());
        if (ImGui::ImageButton((ImTextureID) DataManager::mNewFolderIcon->GetGLTextureID64(), iconSize))
        {
            ImGui::OpenPopup("New Folder Name");
            memset(mDirNameBuffer, 0, mBufferSize);
        }

        if (ImGui::BeginPopup("New Folder Name"))
        {
            if (ImGui::InputText("Folder Name", mDirNameBuffer, mBufferSize, ImGuiInputTextFlags_EnterReturnsTrue))
            {
                std::filesystem::create_directory(mDirNameBuffer);
                ImGui::CloseCurrentPopup();
                ReloadItems();
            }
            ImGui::EndPopup();
        }

        ImGui::SameLine();
        iconSize = ImVec2(DataManager::mUpArrowIcon->GetWidth(), DataManager::mUpArrowIcon->GetHeight());
        if (ImGui::ImageButton((ImTextureID) DataManager::mUpArrowIcon->GetGLTextureID64(), iconSize))
        {
            mCurrentDirectory = mCurrentDirectory.parent_path();
            ReloadItems();
        }

        ImGui::SameLine();
        ImGui::TextUnformatted(mCurrentDirectory.string().c_str());
        
        DoListBox();

        ImGui::Separator();
        
        if (ImGui::InputText("selection", mInputBuffer, mBufferSize))
        {
            mInputSelection = mCurrentDirectory / mInputBuffer;
            mpSelectedItem = &mInputSelection;
        }

        ImGui::Separator();

        bool res = DoButtons();

        ImGui::End();
        return res;
    }
    
    void FileBrowser::AddExtensionFilter(std::string ext)
    {
        mExtensionsFilter.push_back(ext);
    }
    
    void FileBrowser::SetSelectionMode(SelectionMode mode)
    {
        mSelectionMode = mode;
    }
    
    void FileBrowser::WarnOnExistingFileSelection(bool warn)
    {
        mWarnOnExisting = warn;
    }

    void FileBrowser::SetPrompt(std::string prompt)
    {
        mPrompt = prompt;
    }
    

    /// returns nullptr if the dialog has not been opened yet
    const std::filesystem::path* FileBrowser::GetSelectedItem() const
    {
        return mpSelectedItem;
    }

    FileBrowser::Result FileBrowser::GetResult() const
    {
        return mResult;
    }

    bool FileBrowser::IsOpen() const
    {
        return mIsOpen;
    }

    void FileBrowser::Close()
    {
        mIsOpen = false;
    }
    
    void FileBrowser::ReloadItems()
    {         
        mpSelectedItem = nullptr;
        mItemsInDir.clear();
        for(auto const& dir_entry: std::filesystem::directory_iterator{mCurrentDirectory})
        {
            mItemsInDir.push_back(dir_entry.path());
        }

        // Sort by type and then by name
        std::sort(mItemsInDir.begin(), mItemsInDir.end(), [](std::filesystem::path& lhs, std::filesystem::path& rhs)
        {
            if (std::filesystem::is_directory(lhs) && std::filesystem::is_directory(rhs))
            {
                return strcmp(lhs.filename().string().c_str(), rhs.filename().string().c_str()) < 0;
            }

            if (std::filesystem::is_directory(lhs) && !std::filesystem::is_directory(rhs))
            {
                return true;
            }

            return false;
        });
    }

    ////////////////////////////////////////////////////////////
    //				RENDER HELPER METHODS
    ////////////////////////////////////////////////////////////
    void FileBrowser::DoListBox()
    {
        if (ImGui::BeginListBox("", ImVec2(ImGui::GetWindowWidth(), ImGui::GetWindowHeight() * .575f)))
        {
            for (int i = 0; i < mItemsInDir.size(); i++)
            {
                if (std::filesystem::is_directory(mItemsInDir[i]))
                {
                    ImVec2 size(DataManager::mFolderIcon->GetWidth(), DataManager::mFolderIcon->GetHeight());
                    ImTextureID id = (ImTextureID) DataManager::mFolderIcon->GetGLTextureID64();
                    ImGui::Image(id, size);
                    ImGui::SameLine();
                }
                if (ImGui::Selectable(mItemsInDir[i].filename().string().c_str()))
                {
                    if (std::filesystem::is_directory(mItemsInDir[i]))
                    {
                        mCurrentDirectory /= mItemsInDir[i];
                        ReloadItems();

                        if (mSelectionMode == SelectionMode::DIRECTORIES_ONLY || mSelectionMode == SelectionMode::ANY)
                        {
                            mpSelectedItem = &mCurrentDirectory;
                        }
                    }
                    else
                    {
                        if (mSelectionMode == SelectionMode::FILES_ONLY || mSelectionMode == SelectionMode::ANY)
                        {
                            mpSelectedItem = &mItemsInDir[i];
                            memset(mInputBuffer, 0, mBufferSize);
                            memcpy(mInputBuffer, mpSelectedItem->string().c_str(), mpSelectedItem->string().size());
                        }
                    }
                }
            }

            ImGui::EndListBox();
        }
    }

    bool FileBrowser::DoButtons()
    {
        ImGui::SetCursorPosX(ImGui::GetWindowSize().x - 100.0f);
        if (mpSelectedItem && ImGui::Button("OK"))
        {
            if (std::filesystem::exists(*mpSelectedItem) && mWarnOnExisting)
            {
                ImGui::OpenPopup("Warn");
            }
            else
            {
                mResult = Result::OK;
                mIsOpen = false;
                return false;
            }
        }

        if (ImGui::BeginPopup("Warn"))
        {
            ImGui::Text("This file already exists. Are you sure you want to overwrite it?");
            if (ImGui::Button("Yes"))
            {
                mResult = Result::OK;
                mIsOpen = false;
                ImGui::EndPopup();
                return false;
            }

            ImGui::SameLine();
            if (ImGui::Button("No"))
            {
                ImGui::CloseCurrentPopup();
            }
            
            ImGui::EndPopup();
        }

        ImGui::SameLine();

        if (ImGui::Button("Cancel"))
        {
            mResult = Result::CANCEL;
            mIsOpen = false;
            return false;
        }

        return true;
    }
}
    
