/*******************************************************************************************
*
*   raylib-extras [ImGui] example - asset browser
*
*	This is a more complex ImGui Integration
*	It shows how to build windows on top of 2d and 3d views using a render texture
*
*   Copyright (c) 2024 Jeffery Myers
*
********************************************************************************************/

#define  _CRT_NONSTDC_NO_WARNINGS

#include "asset_browser.h"
#include "imgui_utils.h"
#include "imgui.h"
#include "imgui_internal.h"
#include "raylib.h"
#include "extras/IconsFontAwesome6.h"

#include <algorithm>


AssetBrowserPanel::AssetBrowserPanel()
{
    AssetRoot = GetWorkingDirectory();
    RebuildFolderTree();
    SetCurrentFolder(&FolderRoot);

    CurrentView = &ListView;
}

void AssetBrowserPanel::Show()
{
    ShowHeader();

    ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_Borders;
  
    if (ImGui::BeginTable("AssetBrowserTab", 2, flags, ImGui::GetContentRegionAvail()))
    {
        ImGui::TableSetupColumn("FolderView", ImGuiTableColumnFlags_None, 0.25f);
        ImGui::TableSetupColumn("AssetView", ImGuiTableColumnFlags_None, 0.75f);
        ImGui::TableNextRow();
        ImGui::TableNextColumn();

        if (ImGui::BeginChild("FolderList", ImGui::GetContentRegionAvail(),ImGuiChildFlags_None, ImGuiWindowFlags_None))
        {
            ShowFolderTree();
            ImGui::EndChild();
        }

        ImGui::TableNextColumn();
        ShowFilePane();
        ImGui::EndTable();
    }
}

void AssetBrowserPanel::RebuildFolderTree()
{
    FolderRoot.Children.clear();

    FolderRoot.FullPath = AssetRoot;
    FolderRoot.Name = GetFileNameWithoutExt(AssetRoot.c_str());
    FolderRoot.Parent = nullptr;
    FolderRoot.Icon = ICON_FA_SERVER;
    FolderRoot.ForceOpenNextFrame = true;
    FolderRoot.PopulateChildren();
}

void AssetBrowserPanel::SetCurrentFolder(FolderInfo* folder)
{
    if (CurrentFolderContents.Folder == folder)
        return;

    CurrentFolderContents.Folder = folder;
    CurrentFolderContents.Files.clear();

    if (folder == nullptr)
        return;

    FolderInfo* openFolder = folder;
    while (openFolder != nullptr)
    {
        openFolder->ForceOpenNextFrame = true;
        openFolder = openFolder->Parent;
    }

    auto files = LoadDirectoryFiles(CurrentFolderContents.Folder->FullPath.c_str());

    for (unsigned int i = 0; i < files.count; i++)
    {
        if (DirectoryExists(files.paths[i]))
            continue;

        const char* name = GetFileName(files.paths[i]);
        if (!name || *name == '.')
            continue;

        FileInfo& file = CurrentFolderContents.Files.emplace_back();
        file.FullPath = files.paths[i];
        file.Name = name;
        file.Icon = GetFileIcon(name);
    }

    UnloadDirectoryFiles(files);
}

void AssetBrowserPanel::FolderInfo::PopulateChildren()
{
    constexpr Color folderColor = { 255,255,145,255 };

    auto folders = LoadDirectoryFiles(FullPath.c_str());

    for (unsigned int i = 0; i < folders.count; i++)
    {
        if (DirectoryExists(folders.paths[i]))
        {
            const char* name = GetFileNameWithoutExt(folders.paths[i]);
            if (!name || *name == '.')
                continue;
  
            FolderInfo& child = Children.emplace_back();
            child.FullPath = folders.paths[i];
            child.Name = name;
            child.Parent = this;
            child.Tint = folderColor;
            child.Icon = ICON_FA_FOLDER;
            child.PopulateChildren();
        }
    }
    UnloadDirectoryFiles(folders);
}

bool AssetBrowserPanel::ShowFolderTreeNode(FolderInfo& info)
{
    ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
    if (info.Children.empty())
        flags |= ImGuiTreeNodeFlags_Leaf;

    if (CurrentFolderContents.Folder == &info)
        flags |= ImGuiTreeNodeFlags_Selected;

    if (info.ForceOpenNextFrame)
    {
        ImGui::SetNextItemOpen(true);
    }

    bool open = ImGui::TreeNodeEx(info.Name.c_str(), flags, "%s %s", info.Icon.c_str(), info.Name.c_str());

    if (info.ForceOpenNextFrame && CurrentFolderContents.Folder == &info)
        ImGui::ScrollToItem(ImGuiScrollFlags_KeepVisibleCenterY);

    info.ForceOpenNextFrame = false;
    if (ImGui::IsItemClicked())
        SetCurrentFolder(&info);
    
    if (open)
    {
        for (auto& node : info.Children)
            ShowFolderTreeNode(node);

        ImGui::TreePop();
    }

    return CurrentFolderContents.Folder == &info;
}  

void AssetBrowserPanel::ShowHeader()
{
    ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 1);
    if (ImGui::BeginChild("Header", ImVec2{ ImGui::GetContentRegionAvail().x, ImGui::GetFrameHeight() }))
    {
        ImGui::Text("%s Root", ICON_FA_FOLDER_OPEN);
        ImGui::SameLine();
        ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x * 0.33f);
        ImGui::InputText("###Path", (char*)(CurrentFolderContents.Folder->FullPath.c_str() + AssetRoot.size()), CurrentFolderContents.Folder->FullPath.size(), ImGuiInputTextFlags_ReadOnly);

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

void AssetBrowserPanel::ShowFolderTree()
{
    ShowFolderTreeNode(FolderRoot);
}

void AssetBrowserPanel::ShowFilePane()
{
    if (ImGui::BeginChild("FileList", ImGui::GetContentRegionAvail(), ImGuiChildFlags_None, ImGuiWindowFlags_None))
    {
        if (CurrentView)
        {
            auto *item = CurrentView->Show(CurrentFolderContents);
            if (item)
            {
                AssetItemInfo* assetItem = static_cast<AssetItemInfo*>(item);
                if (!assetItem->IsFile())
                    SetCurrentFolder(static_cast<FolderInfo*>(item));
            }
        }
        ImGui::EndChild();
    }
}

ViewableItem* AssetBrowserPanel::AssetContainer::Reset()
{
    FileItr = Files.begin();
    FolderItr = Folder->Children.begin();
    if (FileItr != Files.end())
        return &(*FileItr);
    if (FolderItr != Folder->Children.end())
        return &(*FolderItr);

    return nullptr;
}

size_t AssetBrowserPanel::AssetContainer::Count()
{
    return Files.size() + Folder->Children.size();
}

ViewableItem* AssetBrowserPanel::AssetContainer::Next()
{
    if (FileItr != Files.end())
    {
        FileItr++;
        if (FileItr != Files.end())
            return &(*FileItr);
        else
        {
            if (FolderItr != Folder->Children.end())
                return &(*FolderItr);
            else
                return nullptr;
        }
    }

    if (FolderItr != Folder->Children.end())
    {
        FolderItr++;
        if (FolderItr != Folder->Children.end())
            return &(*FolderItr);
    }

    return nullptr;
}

const char* AssetBrowserPanel::GetFileIcon(const char* filename)
{
    const char* e = GetFileExtension(filename);

    if (e == nullptr)
        return ICON_FA_FILE;

    std::string ext = e;

    std::transform(ext.begin(), ext.end(), ext.begin(),
        [](unsigned char c) { return std::tolower(c); } // correct
    );

    if (!ext.empty())
    {
        if (ext==".png")
            return ICON_FA_FILE_IMAGE;

        if (ext==".wav" || ext==".mp3" || ext==".oog")
            return ICON_FA_FILE_AUDIO;

        if (ext==".ttf" || ext==".otf" || ext==".fnt")
            return ICON_FA_FONT;

        if (ext==".txt" || ext==".md")
            return ICON_FA_FILE_LINES;

        if (ext==".lua" || ext==".c" || ext==".h" || ext==".cpp")
            return ICON_FA_FILE_CODE;
    }
    return ICON_FA_FILE;
}