#include "menu.h"

#include <algorithm>
#include <cmath>
#include <filesystem>
#include <functional>

#include "resources.h"
#include "../../module/ModuleManager.h"
#include "../../module/hotkeys.h"
#include "../../util/globals.h"
#include "../../util/i18n.h"
#include "../../util/utils.h"


namespace menu
{
    namespace
    {
        bool draw_imgui = false;
        float widthh;
        float heightt;
        float scale_factor;
        float font_size;
        float large_font_size;
        float hud_font_size;
        ImFont* poppins_regular;
        ImFont* poppins_regular_hud;
        ImFont* sfui_light;
        ImFont* sfui_bold;
        ImFont* icons;

        ModuleCategory selected_tab = Aimbot;

        void build_unicode_glyph_ranges(ImGuiIO& io, ImVector<ImWchar>& out)
        {
            ImFontGlyphRangesBuilder builder;
            builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
            builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
            builder.AddRanges(io.Fonts->GetGlyphRangesGreek());
            builder.AddRanges(io.Fonts->GetGlyphRangesVietnamese());
            builder.AddRanges(io.Fonts->GetGlyphRangesThai());
            builder.AddRanges(io.Fonts->GetGlyphRangesKorean());
            builder.AddRanges(io.Fonts->GetGlyphRangesJapanese());
            builder.AddRanges(io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
            builder.BuildRanges(&out);
        }

        ImFont* load_ui_font(ImGuiIO& io, const float size, const ImWchar* glyph_ranges)
        {
            ImFontConfig cfg{};
            cfg.OversampleH = 2;
            cfg.OversampleV = 1;
            for (const char* font_path : {
                     "C:\\Windows\\Fonts\\segoeui.ttf",
                     "C:\\Windows\\Fonts\\arial.ttf"})
            {
                if (!std::filesystem::exists(font_path))
                    continue;

                if (ImFont* font = io.Fonts->AddFontFromFileTTF(font_path, size, &cfg, glyph_ranges))
                    return font;
            }

            return io.Fonts->AddFontFromMemoryCompressedBase85TTF(poppins_regular_data, size, &cfg, glyph_ranges);
        }

        void merge_font_glyphs(ImFont* dst, const float size, const char* font_path, const ImWchar* glyph_ranges)
        {
            if (!dst || !font_path || !std::filesystem::exists(font_path))
                return;

            ImFontConfig cfg{};
            cfg.MergeMode = true;
            cfg.DstFont = dst;
            cfg.OversampleH = 1;
            cfg.OversampleV = 1;
            ImGui::GetIO().Fonts->AddFontFromFileTTF(font_path, size, &cfg, glyph_ranges);
        }

        void merge_cjk_fallbacks(ImFont* dst, const float size, ImGuiIO& io)
        {
            merge_font_glyphs(dst, size, "C:\\Windows\\Fonts\\msyh.ttc",
                io.Fonts->GetGlyphRangesChineseSimplifiedCommon());
            merge_font_glyphs(dst, size, "C:\\Windows\\Fonts\\malgun.ttf", io.Fonts->GetGlyphRangesKorean());
            merge_font_glyphs(dst, size, "C:\\Windows\\Fonts\\meiryo.ttc", io.Fonts->GetGlyphRangesJapanese());
            merge_font_glyphs(dst, size, "C:\\Windows\\Fonts\\YuGothR.ttc", io.Fonts->GetGlyphRangesJapanese());
        }
    }

    float get_hud_font_size()
    {
        return hud_font_size;
    }

    ImFont* get_hud_font()
    {
        return poppins_regular_hud;
    }

    ImFont* get_bold_font()
    {
        return sfui_bold;
    }

    float get_scale_factor()
    {
        return scale_factor;
    }

    float get_width()
    {
        return widthh;
    }

    float get_height()
    {
        return heightt;
    }

    bool is_active()
    {
        return draw_imgui == true;
    }

    void setup()
    {
        ImGuiIO io = ImGui::GetIO();
        int horizontal = 0;
        int vertical = 0;
        utils::get_desktop_resolution(horizontal, vertical);
        widthh = static_cast<float>(horizontal);
        heightt = static_cast<float>(vertical);
        scale_factor = (static_cast<float>(horizontal) / 1920.0f + static_cast<float>(vertical) / 1080.0f) / 2.0f;
        font_size = 16 * scale_factor;
        large_font_size = 18 * scale_factor;
        hud_font_size = 256 * scale_factor;

        ImVector<ImWchar> unicode_glyph_ranges;
        build_unicode_glyph_ranges(io, unicode_glyph_ranges);

        poppins_regular = load_ui_font(io, font_size, unicode_glyph_ranges.Data);
        merge_cjk_fallbacks(poppins_regular, font_size, io);

        ImFontConfig icon_cfg{};
        icon_cfg.MergeMode = true;
        icon_cfg.DstFont = poppins_regular;
        icons = io.Fonts->AddFontFromMemoryCompressedTTF(icons_compressed_data, icons_compressed_size,
            font_size, &icon_cfg);

        sfui_light = load_ui_font(io, font_size, unicode_glyph_ranges.Data);
        merge_cjk_fallbacks(sfui_light, font_size, io);

        sfui_bold = load_ui_font(io, large_font_size, unicode_glyph_ranges.Data);
        merge_cjk_fallbacks(sfui_bold, large_font_size, io);

        poppins_regular_hud = load_ui_font(io, hud_font_size, unicode_glyph_ranges.Data);
        if (poppins_regular_hud)
            merge_cjk_fallbacks(poppins_regular_hud, hud_font_size, io);
    }

    void draw()
    {

        auto* mm = ModuleManager::get_instance();
        if (selected_tab == Aimbot && !mm->has_module("Aimbot")) {
            selected_tab = Visuals;
        }
        if (selected_tab == MapEditor && !mm->config_module->show_map_editor_menu->get_value()) {
            selected_tab = Visuals;
        }
        // draw menu
        theme();
        apply_modern_style();
        ImGui::GetMouseCursor();
        ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);
        ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
        ImGui::GetIO().WantCaptureMouse = is_active();

        // smooth open/close animation (fade) — keep drawing while fading out
        static float menu_anim = 0.f;
        const float dt = ImGui::GetIO().DeltaTime;
        const float anim_target = draw_imgui ? 1.f : 0.f;
        menu_anim += (anim_target - menu_anim) * (std::min)(1.f, dt * 14.f);
        const float ease = menu_anim * menu_anim * (3.f - 2.f * menu_anim); // smoothstep

        if (menu_anim > 0.004f)
        {
            const float sf = scale_factor;
            constexpr float width = 840;
            constexpr float height = 560;
            ImGui::SetNextWindowSize(ImVec2(width * sf, height * sf), ImGuiCond_Once);
            ImGui::SetNextWindowBgAlpha(ease);
            ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ease);
            ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));

            if (ImGui::Begin(globals::get_display_title().c_str(), &draw_imgui,
                ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar))
            {
                ImDrawList* dl = ImGui::GetWindowDrawList();

                // ---- pulsing outer glow ----
                {
                    const ImVec2 wp = ImGui::GetWindowPos();
                    const ImVec2 ws = ImGui::GetWindowSize();
                    ImDrawList* fg = ImGui::GetForegroundDrawList();
                    const float pulse = 0.5f + 0.5f * sinf(static_cast<float>(ImGui::GetTime()) * 2.2f);
                    const float rnd = ImGui::GetStyle().WindowRounding;
                    for (int g = 1; g <= 4; ++g)
                    {
                        const float o = g * 2.0f * sf;
                        const float a = (0.18f - g * 0.035f) * (0.6f + 0.4f * pulse) * ease;
                        if (a <= 0.f) continue;
                        fg->AddRect(ImVec2(wp.x - o, wp.y - o), ImVec2(wp.x + ws.x + o, wp.y + ws.y + o),
                                    ImGui::GetColorU32(menu::with_alpha(menu::palette::accent, a)), rnd + o, 0, 2.0f);
                    }
                }

                const ImVec2 work_min = ImGui::GetCursorScreenPos();
                const float work_w = ImGui::GetContentRegionAvail().x;
                const float work_h = ImGui::GetContentRegionAvail().y;
                const float sidebar_w = 158.f * sf;

                // ---- interactive animated background: drifting dot grid that reacts to the mouse ----
                {
                    const ImVec2 mouse = ImGui::GetIO().MousePos;
                    const float t = static_cast<float>(ImGui::GetTime());
                    const float spacing = 28.f * sf;
                    const float infl = 115.f * sf;          // mouse influence radius
                    const float infl_sq = infl * infl;

                    dl->PushClipRect(work_min, ImVec2(work_min.x + work_w, work_min.y + work_h), true);

                    for (float gy = work_min.y + spacing * 0.5f; gy < work_min.y + work_h; gy += spacing)
                    {
                        for (float gx = work_min.x + spacing * 0.5f; gx < work_min.x + work_w; gx += spacing)
                        {
                            // subtle drift so the field feels alive
                            const float px = gx + sinf(t * 0.6f + gx * 0.01f + gy * 0.013f) * 2.f * sf;
                            const float py = gy + cosf(t * 0.5f + gy * 0.011f + gx * 0.009f) * 2.f * sf;

                            const float ddx = px - mouse.x;
                            const float ddy = py - mouse.y;
                            const float d2 = ddx * ddx + ddy * ddy;
                            const float prox = d2 < infl_sq ? (1.f - sqrtf(d2) / infl) : 0.f;

                            const float r = (0.8f + prox * 2.4f) * sf;
                            ImVec4 col = prox > 0.02f ? menu::palette::accent2 : menu::palette::line;
                            col.w = (0.05f + prox * 0.6f) * ease;
                            dl->AddCircleFilled(ImVec2(px, py), r, ImGui::GetColorU32(col), 8);
                        }
                    }

                    // soft accent glow following the cursor
                    if (mouse.x >= work_min.x && mouse.x <= work_min.x + work_w &&
                        mouse.y >= work_min.y && mouse.y <= work_min.y + work_h)
                    {
                        for (int g = 0; g < 5; ++g)
                        {
                            const float gr = (28.f + g * 22.f) * sf;
                            const float ga = (0.055f - g * 0.01f) * ease;
                            if (ga <= 0.f) continue;
                            dl->AddCircleFilled(mouse, gr,
                                                ImGui::GetColorU32(menu::with_alpha(menu::palette::accent, ga)), 32);
                        }
                    }

                    dl->PopClipRect();
                }

                // sidebar background + divider
                dl->AddRectFilled(work_min, ImVec2(work_min.x + sidebar_w, work_min.y + work_h),
                                  ImGui::GetColorU32(menu::with_alpha(menu::palette::surface, 0.55f)));
                dl->AddLine(ImVec2(work_min.x + sidebar_w, work_min.y + 8 * sf),
                            ImVec2(work_min.x + sidebar_w, work_min.y + work_h - 8 * sf),
                            ImGui::GetColorU32(menu::with_alpha(menu::palette::line, 0.6f)), 1.f);

                // sidebar header
                ImGui::SetCursorPos(ImVec2(20 * sf, 18 * sf));
                ImGui::PushFont(sfui_bold);
                ImGui::TextColored(menu::palette::accent, "TARKOV");
                ImGui::PopFont();
                ImGui::SetCursorPosX(20 * sf);
                ImGui::TextColored(menu::palette::muted, "ADDONS ++");

                // nav items
                struct nav_def { const char* label; ModuleCategory cat; };
                nav_def navs[8];
                int n = 0;
                if (mm->has_module("Aimbot")) navs[n++] = {"nav_aimbot", Aimbot};
                navs[n++] = {"nav_visuals", Visuals};
                if (mm->has_module("Map Editor") && mm->config_module->show_map_editor_menu->get_value())
                    navs[n++] = {"nav_map_editor", MapEditor};
                navs[n++] = {"nav_minimap", Minimap};
                navs[n++] = {"nav_exploit", Exploit};
                navs[n++] = {"nav_misc", Misc};
                navs[n++] = {"nav_configs", Config};

                const float nav_top = 64.f * sf;
                const float nav_h = 40.f * sf;
                float active_y0 = work_min.y + nav_top, active_y1 = active_y0 + nav_h;

                for (int i = 0; i < n; ++i)
                {
                    ImGui::SetCursorPos(ImVec2(10 * sf, nav_top + i * nav_h));
                    ImGui::PushID(i);
                    if (ImGui::InvisibleButton("nav", ImVec2(sidebar_w - 20 * sf, nav_h - 6 * sf)))
                        selected_tab = navs[i].cat;
                    const bool hov = ImGui::IsItemHovered();
                    ImGui::PopID();

                    const ImVec2 rmin = ImGui::GetItemRectMin();
                    const ImVec2 rmax = ImGui::GetItemRectMax();
                    const bool sel = selected_tab == navs[i].cat;
                    if (sel) { active_y0 = rmin.y; active_y1 = rmax.y; }

                    if (sel)
                        dl->AddRectFilled(rmin, rmax, ImGui::GetColorU32(menu::with_alpha(menu::palette::accent, 0.14f)), 6.f);
                    else if (hov)
                        dl->AddRectFilled(rmin, rmax, ImGui::GetColorU32(menu::with_alpha(menu::palette::accent2, 0.08f)), 6.f);

                    const ImU32 col = sel ? ImGui::GetColorU32(menu::palette::txt)
                                          : ImGui::GetColorU32(hov ? menu::palette::accent2 : menu::palette::muted);
                    const std::string nav_label = i18n::tr(navs[i].label);
                    const ImVec2 nav_size = ImGui::CalcTextSize(nav_label.c_str());
                    dl->AddText(ImVec2(rmin.x + 16 * sf, rmin.y + (rmax.y - rmin.y - nav_size.y) * 0.5f),
                        col, nav_label.c_str());
                }

                // sliding vertical indicator on sidebar left edge
                static float ind_y0 = 0, ind_y1 = 0;
                static bool ind_init = false;
                if (!ind_init) { ind_y0 = active_y0; ind_y1 = active_y1; ind_init = true; }
                ind_y0 += (active_y0 - ind_y0) * (std::min)(1.f, dt * 16.f);
                ind_y1 += (active_y1 - ind_y1) * (std::min)(1.f, dt * 16.f);
                dl->AddRectFilled(ImVec2(work_min.x + 4 * sf, ind_y0 + 4 * sf),
                                  ImVec2(work_min.x + 7 * sf, ind_y1 - 4 * sf),
                                  ImGui::GetColorU32(menu::palette::accent), 2.f);

                // sidebar footer
                ImGui::SetCursorPos(ImVec2(16 * sf, work_h - 26 * sf));
                ImGui::TextColored(menu::with_alpha(menu::palette::muted, 0.7f), "%s", globals::credits.c_str());

                // ---- CONTENT ----
                auto cat_name = [](ModuleCategory c) -> std::string
                {
                    switch (c)
                    {
                    case Aimbot: return i18n::tr("cat_aimbot");
                    case Visuals: return i18n::tr("cat_visuals");
                    case MapEditor: return i18n::tr("cat_map_editor");
                    case Minimap: return i18n::tr("cat_minimap");
                    case Exploit: return i18n::tr("cat_exploit");
                    case Misc: return i18n::tr("cat_misc");
                    case Config: return i18n::tr("cat_configs");
                    default: return {};
                    }
                };

                const float content_x = sidebar_w + 22 * sf;
                ImGui::SetCursorPos(ImVec2(content_x, 18 * sf));
                ImGui::PushFont(sfui_bold);
                ImGui::TextColored(menu::palette::txt, "%s", cat_name(selected_tab).c_str());
                ImGui::PopFont();

                const float content_avail = work_w - content_x - 16 * sf;
                const float col_gap = 10 * sf;
                const float col_w = (content_avail - col_gap) * 0.5f;
                const float content_h = work_h - 50 * sf - 14 * sf;
                const bool config_tab = selected_tab == Config;

                auto draw_card_frame = [&](const char* title, const std::function<void()>& body)
                {
                    ImDrawList* cdl = ImGui::GetWindowDrawList();
                    ImDrawListSplitter splitter;
                    splitter.Split(cdl, 2);
                    splitter.SetCurrentChannel(cdl, 1);

                    const ImVec2 card_min = ImGui::GetCursorScreenPos();
                    const float card_w = ImGui::GetContentRegionAvail().x;

                    ImGui::Indent(14 * sf);
                    ImGui::Dummy(ImVec2(0, 10 * sf));
                    ImGui::PushFont(sfui_bold);
                    ImGui::TextColored(menu::palette::accent2, "%s", title);
                    ImGui::PopFont();
                    ImGui::Dummy(ImVec2(0, 6 * sf));
                    body();
                    ImGui::Dummy(ImVec2(0, 10 * sf));
                    ImGui::Unindent(14 * sf);

                    const ImVec2 card_max = ImVec2(card_min.x + card_w, ImGui::GetCursorScreenPos().y);

                    splitter.SetCurrentChannel(cdl, 0);
                    cdl->AddRectFilled(card_min, card_max,
                                       ImGui::GetColorU32(menu::with_alpha(menu::palette::surface, 0.5f)), 8.f);
                    cdl->AddRect(card_min, card_max,
                                 ImGui::GetColorU32(menu::with_alpha(menu::palette::line, 0.55f)), 8.f, 0, 1.f);
                    cdl->AddRectFilled(card_min, ImVec2(card_min.x + 3 * sf, card_max.y),
                                       ImGui::GetColorU32(menu::with_alpha(menu::palette::accent, 0.85f)), 8.f,
                                       ImDrawFlags_RoundCornersLeft);
                    splitter.Merge(cdl);

                    ImGui::Dummy(ImVec2(0, 9 * sf));
                };

                auto draw_card = [&](Module* m)
                {
                    draw_card_frame(i18n::tr(m->get_display_key()).c_str(), [&]()
                    {
                        m->prepare_menu_draw();
                        m->apply([](ConfigValue* c) { c->draw(); });
                        m->draw_menu_extra();
                    });
                };

                ImGui::SetCursorPos(ImVec2(content_x, 50 * sf));
                ImGui::BeginChild("##col1", ImVec2(col_w, content_h), false);
                bool switched = false;
                ModuleManager::get_instance()->apply([&](Module* m)
                {
                    if (m->category != selected_tab) return;
                    draw_card(m);
                    if (!config_tab && !switched && ImGui::GetCursorPosY() >= content_h * 0.5f)
                    {
                        switched = true;
                        ImGui::EndChild();
                        ImGui::SetCursorPos(ImVec2(content_x + col_w + col_gap, 50 * sf));
                        ImGui::BeginChild("##col2", ImVec2(col_w, content_h), false);
                    }
                });
                ImGui::EndChild();

                if (config_tab)
                {
                    ImGui::SetCursorPos(ImVec2(content_x + col_w + col_gap, 50 * sf));
                    ImGui::BeginChild("##col2", ImVec2(col_w, content_h), false);
                    draw_card_frame(i18n::tr("hotkeys_sec").c_str(), []() { hotkeys::draw_panel(); });
                    ImGui::EndChild();
                }
            }
            ImGui::End();
            ImGui::PopStyleVar(2); // Alpha + WindowPadding
        }

        // listen to menu key
        if (GetAsyncKeyState(VK_INSERT) & 1)
        {
            draw_imgui = !draw_imgui;
        }

        // draw overlay
        auto draw_list = ImGui::GetBackgroundDrawList();
        ModuleManager::get_instance()->apply([draw_list](Module* m)
            {
                m->draw_overlay(draw_list);
            });
    }
}
