#include "Overlay.h"
#include "../../resource.h"
#include "../Backend/Config/Settings.h"
#include "../Backend/Config/Config.h"
#include "../Backend/Utilities/Utilities.h"
#include "../Frontend/Menu/Menu.h"
#include "../Frontend/Renderer/Renderer.h"
#include <algorithm>
#include <chrono>
#include <vector>
#include <cstdio>
#include "../Frontend/Draw/Draw.h"
#include "../Backend/Console/Log/Log.h"

#pragma comment(lib, "winmm.lib")

static auto g_lastFrameTime = std::chrono::high_resolution_clock::now();
static float g_deltaTime = 0.f;

// Export for compatibility
float GetStandaloneDeltaTime()
{
    return g_deltaTime;
}

COverlay* COverlay::Get()
{
    static COverlay instance;
    return &instance;
}

bool COverlay::IsMenuOpen() const
{
    return CMenu::get()->IsMenuOpened();
}

bool COverlay::Initialize()
{
    // Prevent Windows from ghosting our window
    DisableProcessWindowsGhosting();
    
    // Use RL raylib to create the window with transparency support
    RLSetConfigFlags(RL_E_FLAG_WINDOW_UNDECORATED | RL_E_FLAG_WINDOW_TRANSPARENT | RL_E_FLAG_WINDOW_TOPMOST | RL_E_FLAG_WINDOW_MOUSE_PASSTHROUGH | RL_E_FLAG_WINDOW_ALWAYS_RUN | RL_E_FLAG_VSYNC_HINT);
    RLInitWindow(800, 600, "Rcjy");

    // Get actual monitor dimensions and resize window to cover the full workspace
    RECT workArea;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
    m_ScreenWidth = workArea.right - workArea.left;
    m_ScreenHeight = workArea.bottom - workArea.top;
    RLSetWindowSize(m_ScreenWidth, m_ScreenHeight);
    RLSetWindowPosition(0, 0);
    RLSetTraceLogLevel(RL_E_LOG_WARNING);
    Log::RedirectRaylibTrace();  // route raylib output through our Log for QuickEdit guard
    RLSetExitKey(RL_E_KEY_NULL);
    Settings->LoadDefaults();

    // Sync AutoStart state from registry to config
    CConfig::get()->b["autostart"] = AutoStart::IsEnabled();
    
    // High-performance timer resolution
    timeBeginPeriod(1);
    
    // Process priority
    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);

    // === Win32 utilities integration ===
    HWND hwnd = reinterpret_cast<HWND>(RLGetWindowHandle());

    WindowGuard::Install(hwnd, WindowGuard::BlockClose | WindowGuard::BlockMinimize | WindowGuard::BlockHide);

    HICON hIcon = LoadIcon(GetModuleHandle(nullptr), MAKEINTRESOURCE(IDI_MAIN_ICON));
    SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
    SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);

    m_pTray = new SysTray(hwnd, hIcon, L"Rcjy");
    m_pTray->SetOnDoubleClick([]() {
        CMenu::get()->SetMenuOpened(!CMenu::get()->IsMenuOpened());
    });
    m_pTray->AddMenuItem(1001, L"显示/隐藏", [](UINT) {
        CMenu::get()->SetMenuOpened(!CMenu::get()->IsMenuOpened());
    });

    m_pTray->AddMenuItem(1003, L"重启", [this, hwnd](UINT) {
        wchar_t exePath[MAX_PATH];
        GetModuleFileNameW(NULL, exePath, MAX_PATH);

        ShellExecuteW(NULL, L"open", exePath, NULL, NULL, SW_SHOW);

        WindowGuard::SetAllowClose(hwnd, true);
        m_bRunning = false;
    });
    m_pTray->AddMenuItem(1002, L"退出", [this, hwnd](UINT) {
        WindowGuard::SetAllowClose(hwnd, true);
        m_bRunning = false;
    });

    m_pTray->AddIcon();
    m_pTray->StartAutoRefresh(30);

    m_bRunning = true;
    return true;
}

void COverlay::Shutdown()
{
    m_bRunning = false;

    // Auto-save current config before cleanup
    CConfig::get()->AutoSave();

    // Win32 utilities cleanup
    if (m_pTray) {
        HWND hwnd = reinterpret_cast<HWND>(RLGetWindowHandle());
        WindowGuard::SetAllowClose(hwnd, true);
        delete m_pTray;
        m_pTray = nullptr;
        WindowGuard::Uninstall(hwnd);
    }

    // End high-resolution timer
    timeEndPeriod(1);

    Render::Draw->ReleaseObjects();
    if (GamesenseMenuFramework::Globals::Gui_Ctx)
        GamesenseMenuFramework::ui::DeleteContext(GamesenseMenuFramework::Globals::Gui_Ctx);

    RLCloseWindow();
}

void COverlay::Render()
{
    // Check for window close
    if (RLWindowShouldClose())
    {
        m_bRunning = false;
        return;
    }

    // Calculate delta time
    auto now = std::chrono::high_resolution_clock::now();
    g_deltaTime = std::chrono::duration<float>(now - g_lastFrameTime).count();
    g_lastFrameTime = now;

    // Detect resolution changes and resize overlay window
    {
        RECT workArea;
        SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
        int newWidth = workArea.right - workArea.left;
        int newHeight = workArea.bottom - workArea.top;
        if (newWidth != m_ScreenWidth || newHeight != m_ScreenHeight)
        {
            m_ScreenWidth = newWidth;
            m_ScreenHeight = newHeight;
            RLSetWindowSize(m_ScreenWidth, m_ScreenHeight);
            RLSetWindowPosition(0, 0);
        }
    }

    bool menuOpen = CMenu::get()->IsMenuOpened();
    
    // Toggle mouse passthrough based on menu state
    static bool lastMenuState = false;
    if (menuOpen != lastMenuState)
    {
        if (menuOpen)
        {
            RLClearWindowState(RL_E_FLAG_WINDOW_MOUSE_PASSTHROUGH);
            HWND hwnd = reinterpret_cast<HWND>(RLGetWindowHandle());
            SetForegroundWindow(hwnd);
            SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
        }
        else
            RLSetWindowState(RL_E_FLAG_WINDOW_MOUSE_PASSTHROUGH);
    }
    lastMenuState = menuOpen;

    // Handle mouse wheel in menu
    if (menuOpen)
    {
        float wheel = RLGetMouseWheelMove();
        if (wheel != 0.0f)
        {
            auto g = GamesenseMenuFramework::Globals::Gui_Ctx;
            if (g) g->MouseWheel += wheel;
        }
    }

    // RL raylib rendering
    RLBeginDrawing();
    RLClearBackground(BLANK);

    Render::Draw->Init();
    // Update font DPI scale from config (runs every frame, even when menu closed)
    {
        //auto g = GamesenseMenuFramework::Globals::Gui_Ctx;
        //if (g)
        //{
        //    const float scales[] = { 1.0f, 1.25f, 1.5f, 1.75f, 2.0f };
        //    int idx = std::clamp(CConfig::get()->i["menu_scale"], 0, 4);
        //    g->dpiScale = scales[idx];
        //    Render::Draw->SetDpiScale(g->dpiScale);
        //}
    }
    Render::Draw->Reset();

    CMenu::get()->Initialize();

    // Always update input state before any rendering (needed for menu toggle key detection)
    GamesenseMenuFramework::ui::GetInputFromWindow("Rcjy");

    Render::DrawOverlay();
    
    CMenu::get()->Draw();

    // Sync AutoStart config ↔ registry
    {
        CConfig* cfg = CConfig::get();
        static bool prevAutoStart = cfg->b["autostart"];
        if (cfg->b["autostart"] != prevAutoStart) {
            prevAutoStart = cfg->b["autostart"];
            if (prevAutoStart)
                AutoStart::Enable();
            else
                AutoStart::Disable();
        }
    }

    // Sync taskbar icon visibility
    {
        CConfig* cfg = CConfig::get();
        static bool prevHideTaskbar = cfg->b["hide_taskbar_icon"];
        if (cfg->b["hide_taskbar_icon"] != prevHideTaskbar) {
            prevHideTaskbar = cfg->b["hide_taskbar_icon"];
            HWND hwnd = reinterpret_cast<HWND>(RLGetWindowHandle());
            LONG_PTR exStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
            if (prevHideTaskbar) {
                exStyle &= ~WS_EX_APPWINDOW;
                exStyle |= WS_EX_TOOLWINDOW;
            } else {
                exStyle &= ~WS_EX_TOOLWINDOW;
                exStyle |= WS_EX_APPWINDOW;
            }
            SetWindowLongPtr(hwnd, GWL_EXSTYLE, exStyle);
            // Force shell to re-evaluate taskbar presence
            ShowWindow(hwnd, SW_HIDE);
            SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
            ShowWindow(hwnd, SW_SHOW);
        }
    }

    // Menu toggle
    {
        CConfig* cfg = CConfig::get();
        int keyStyle = cfg->i["menu_keystyle"];

        if (keyStyle == 4) {
            // === Multi-key rising-edge ===
            const char* raw = cfg->s["menu_key_bind"];
            if (!raw || !raw[0]) goto end_toggle;

            // Build VK list (stack only, no heap)
            int vks[16] = {}; int n = 0;
            for (const char* p = raw; *p && n < 16; ) {
                int vk = atoi(p);
                if (vk > 0 && vk < 256) vks[n++] = vk;
                const char* c = strchr(p, ',');
                if (!c) break;
                p = c + 1;
            }
            if (!n) goto end_toggle;

            // All-held check (accept either left/right modifier pairs)
            // VK pairs: LCONTROL=162↔RCONTROL=163, LSHIFT=160↔RSHIFT=161, LMENU=164↔RMENU=165
            bool allDown = true;
            for (int i = 0; i < n; i++) {
                int vk = vks[i];
                bool held = (GetAsyncKeyState(vk) & 0x8000) != 0;
                if (!held) {
                    // Try paired modifier
                    int pair = 0;
                    if      (vk == 162) pair = 163;  // left  Ctrl → right Ctrl
                    else if (vk == 163) pair = 162;  // right Ctrl → left  Ctrl
                    else if (vk == 160) pair = 161;  // left  Shift → right Shift
                    else if (vk == 161) pair = 160;  // right Shift → left  Shift
                    else if (vk == 164) pair = 165;  // left  Alt → right Alt
                    else if (vk == 165) pair = 164;  // right Alt → left  Alt
                    if (pair) held = (GetAsyncKeyState(pair) & 0x8000) != 0;
                }
                if (!held) { allDown = false; break; }
            }

            // Rising-edge
            static bool prev = false;
            if (allDown && !prev)
                CMenu::get()->SetMenuOpened(!CMenu::get()->IsMenuOpened());
            prev = allDown;
        } else {
            // === Single-key rising-edge ===
            int menuKey = cfg->i["menu_key"];
            if (menuKey <= 0) menuKey = VK_INSERT;

            static bool prev = false;
            bool down = (GetAsyncKeyState(menuKey) & 0x8000) != 0;
            if (down && !prev)
                CMenu::get()->SetMenuOpened(!CMenu::get()->IsMenuOpened());
            prev = down;
        }
    }
    end_toggle:

    RLEndDrawing();
}

float GetOverlayDeltaTime()
{
    return g_deltaTime;
}
 