/*
	ApplicationFramework
	by John Ryland
	Copyright (c) 2023
*/

////////////////////////////////////////////////////////////////////////////////////
//	Dear ImGui Ui System

#include "DearImGuiUiSystem.h"
#include "GlfwPlatform.h"
#include "IconsFontAwesome5.h"
#include "imgui.h"
#include "misc/freetype/imgui_freetype.h"

namespace ApplicationFramework {

static const bool s_UseDarkStyle = true;
static const bool s_OrangeButtons = true;
static const bool s_VSCodeDarkBackground = true;

DearImGuiUiSystem::DearImGuiUiSystem()
{
}

// virtual
DearImGuiUiSystem::~DearImGuiUiSystem()
{
}

/*
// virtual
void DearImGuiUiSystem::SetApplication(IApplication* application)
{
    m_application = application;
    // SetStyleOption(8);
}
*/

// virtual
void DearImGuiUiSystem::Initialize()
{
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    if (!ImGui::GetCurrentContext())
        ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;       // Enable Keyboard Controls
    io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;        // Enable Gamepad Controls
    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;           // Enable Docking
    io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;         // Enable Multi-Viewport / Platform Windows
    
    //io.ConfigViewportsNoAutoMerge = true;
    //io.ConfigViewportsNoTaskBarIcon = true;

    // Setup Dear ImGui style
    if (s_UseDarkStyle)
        ImGui::StyleColorsDark();
    else
        ImGui::StyleColorsLight();

    // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
    ImGuiStyle& style = ImGui::GetStyle();
    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
    {
        style.WindowRounding = 0.0f;
        style.Colors[ImGuiCol_WindowBg].w = 1.0f;
    }

    // My color preferences
    ImVec4* colors = style.Colors;
    colors[ImGuiCol_WindowBg]               = ImVec4(0.18f, 0.18f, 0.18f, 1.00f);
    colors[ImGuiCol_ChildBg]                = ImVec4(0.23f, 0.25f, 0.28f, 0.00f);
    colors[ImGuiCol_Border]                 = ImVec4(0.29f, 0.29f, 0.36f, 0.50f);
    colors[ImGuiCol_FrameBg]                = ImVec4(0.20f, 0.28f, 0.40f, 0.54f);
    colors[ImGuiCol_TitleBg]                = ImVec4(0.19f, 0.21f, 0.25f, 1.00f);
    colors[ImGuiCol_TitleBgActive]          = ImVec4(0.22f, 0.26f, 0.31f, 1.00f);
    colors[ImGuiCol_MenuBarBg]              = ImVec4(0.18f, 0.18f, 0.18f, 1.00f);
    colors[ImGuiCol_Separator]              = ImVec4(0.16f, 0.16f, 0.19f, 0.50f);
    colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.18f, 0.27f, 0.36f, 0.78f);
    colors[ImGuiCol_SeparatorActive]        = ImVec4(0.19f, 0.30f, 0.42f, 1.00f);
    colors[ImGuiCol_Tab]                    = ImVec4(0.38f, 0.39f, 0.40f, 0.86f);
    colors[ImGuiCol_TabHovered]             = ImVec4(0.26f, 0.59f, 0.98f, 0.80f);
    colors[ImGuiCol_TabActive]              = ImVec4(0.35f, 0.53f, 0.76f, 1.00f);
    colors[ImGuiCol_TabUnfocused]           = ImVec4(0.34f, 0.37f, 0.40f, 0.97f);
    colors[ImGuiCol_TabUnfocusedActive]     = ImVec4(0.32f, 0.46f, 0.63f, 1.00f);
    // Variations:
    if (s_VSCodeDarkBackground)
        colors[ImGuiCol_FrameBg]            = ImVec4(0.15f, 0.17f, 0.18f, 1.00f);  // Dark blue frame background
    if (s_OrangeButtons)
        colors[ImGuiCol_Button]             = ImVec4(0.94f, 0.55f, 0.10f, 0.76f);  // Orange buttons

    ImFontConfig normalConfig;

    ImFontConfig tweakedConfig;       // JR: My tweaked settings - looks good on Linux with DejaVuSans.ttf at 14.0.
    tweakedConfig.OversampleH = 5;
    tweakedConfig.OversampleV = 2;
    tweakedConfig.RasterizerMultiply = 0.8f;
    // tweakedConfig.GlyphExtraSpacing.x = 0.0f;
    tweakedConfig.GlyphExtraAdvanceX = 0.0f;

    ImFontConfig tweakedConfigWithHinting = tweakedConfig;
    tweakedConfigWithHinting.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_ForceAutoHint;   // This helps make things less blurry

    ImFontConfig subpixelConfig;
    subpixelConfig.OversampleH = 3;
    subpixelConfig.OversampleV = 1;
    subpixelConfig.RasterizerMultiply = 1.15f;
    subpixelConfig.FontLoaderFlags |= ImGuiFreeTypeLoaderFlags_ForceAutoHint | ImGuiFreeTypeLoaderFlags_LoadColor;
    // subpixelConfig.GlyphExtraSpacing.x = 0.0f;
    subpixelConfig.GlyphExtraAdvanceX = 0.0f;

    ImFontConfig unicodeConfig;
    unicodeConfig.OversampleH = 1;
    unicodeConfig.OversampleV = 1;
    unicodeConfig.MergeMode = true;

    const char* ubuntuFontPaths[] = {
            "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",                 // regular
            "/usr/share/fonts/truetype/ubuntu/UbuntuMono-R.ttf",               // fixed
            "/usr/share/fonts/truetype/font-awesome/fontawesome-webfont.ttf",  // icon
            "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",          // unicode
            "/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf",       // fallback
    };

    // Customized user font settings:
    //     HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Windows NT\CurrentVersion\FontSubstitutes
    // TODO: not tested on windows yet
    const char* windowsFontPaths[] = {
            "c:\\Windows\\Fonts\\segoeui.ttf",                                 // regular
            // "c:\\Windows\\Fonts\\trebuc.ttf",                                  // regular
            "c:\\Windows\\Fonts\\consola.ttf",                                 // fixed
            "c:\\Windows\\Fonts\\arial.ttf",                                   // icon
            "c:\\Windows\\Fonts\\l_10646.ttf",                                 // unicode
            "c:\\Windows\\Fonts\\arial.ttf",                                   // fallback
    };

    // TODO: this hardcoded path is no-good
    static const char*   iconFontPath = "/home/jryland/Coding/GameEngine/.modules/Font-Awesome/webfonts/" FONT_ICON_FILE_NAME_FAS;
    static const ImWchar iconRanges[] = { ICON_MIN_FA, ICON_MAX_16_FA, 0 };

#ifdef _WIN32
    const char** fontPaths = windowsFontPaths;
    float fontScale = 96.0 / 72.0;
#else
    const char** fontPaths = ubuntuFontPaths;
    fontPaths[2] = iconFontPath;
    float fontScale = 1.0;
#endif

    // Normal UI font (3 config variations)
    io.Fonts->AddFontFromFileTTF(fontPaths[0], 17.0f * fontScale, &tweakedConfigWithHinting);
    io.Fonts->AddFontFromFileTTF(fontPaths[0], 17.0f * fontScale, &normalConfig);
    io.Fonts->AddFontFromFileTTF(fontPaths[0], 17.0f * fontScale, &subpixelConfig);
    m_defaultFontIndex = 0;

    // Monospace/Code/Fixed font
    io.Fonts->AddFontFromFileTTF(fontPaths[1], 11.5f * fontScale, &tweakedConfig);    // Mono font looks better without the hinting
    m_fixedFontIndex = 3;

    // Icon font
    io.Fonts->AddFontFromFileTTF(fontPaths[2], 14.0f * fontScale, &normalConfig, iconRanges);
    m_iconFontIndex = 4;

    // Unicode font
    io.Fonts->AddFontFromFileTTF(fontPaths[3], 14.0f * fontScale, &unicodeConfig, io.Fonts->GetGlyphRangesDefault());
    io.Fonts->AddFontFromFileTTF(fontPaths[3], 14.0f * fontScale, &unicodeConfig, io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
    io.Fonts->AddFontFromFileTTF(fontPaths[3], 14.0f * fontScale, &unicodeConfig, io.Fonts->GetGlyphRangesKorean());
    io.Fonts->AddFontFromFileTTF(fontPaths[3], 14.0f * fontScale, &unicodeConfig, io.Fonts->GetGlyphRangesJapanese());
    m_unicodeFontIndex = 5;

    // Fallback font (ImGui built-in proggy font)
    io.Fonts->AddFontDefault();
    m_fallbackFontIndex = 6;
}

// virtual
void DearImGuiUiSystem::Shutdown()
{
    ImGui_ImplGlfw_Shutdown();
    ImGui::DestroyContext();
}

// virtual
void DearImGuiUiSystem::Prepare()
{
    // Start the Dear ImGui frame
    ImGui_ImplGlfw_NewFrame();
    ImGui::NewFrame();
}

// virtual
void DearImGuiUiSystem::Update()
{
}

// virtual
void DearImGuiUiSystem::Render()
{
    // Rendering
    ImGui::Render();

    // Update and Render additional Platform Windows
    ImGuiIO& io = ImGui::GetIO(); (void)io;
    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
    {
        ImGui::UpdatePlatformWindows();
        ImGui::RenderPlatformWindowsDefault();
    }
}

// virtual
void DearImGuiUiSystem::Present()
{
}

} // ApplicationFramework namespace
