#include "ImGuiContext.h"
#include "../utils/Logger.h"
#include "../utils/GLExt.h"
#include "../utils/PathUtils.h"
#include <imgui.h>
#include <imgui_impl_win32.h>
#include <imgui_impl_opengl3.h>
#include <filesystem>

// Forward declare message handler from imgui_impl_win32.cpp
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

namespace UsdLayerManager {

ImGuiContext::ImGuiContext()
    : m_hwnd(nullptr)
    , m_hdc(nullptr)
    , m_hglrc(nullptr)
    , m_shouldClose(false)
    , m_width(1280)
    , m_height(720) {
}

ImGuiContext::~ImGuiContext() {
    Shutdown();
}

bool ImGuiContext::Initialize(const std::string& windowTitle, int width, int height) {
    m_width = width;
    m_height = height;
    
    LOG_INFO("Initializing ImGui context...");
    
    // Create application window
    WNDCLASSEXW wc = { 
        sizeof(wc), 
        CS_OWNDC, 
        WndProc, 
        0L, 
        0L, 
        GetModuleHandle(nullptr), 
        nullptr, 
        nullptr, 
        nullptr, 
        nullptr, 
        L"UsdLayerManager", 
        nullptr 
    };
    ::RegisterClassExW(&wc);
    
    m_hwnd = ::CreateWindowW(
        wc.lpszClassName, 
        L"USD Layer Manager", 
        WS_OVERLAPPEDWINDOW, 
        100, 100, 
        m_width, m_height, 
        nullptr, 
        nullptr, 
        wc.hInstance, 
        nullptr
    );
    
    if (!m_hwnd) {
        LOG_ERROR("Failed to create window");
        return false;
    }
    
    // Store this pointer in window user data
    ::SetWindowLongPtr(m_hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
    
    // Initialize OpenGL
    if (!CreateDeviceWGL()) {
        LOG_ERROR("Failed to initialize OpenGL");
        ::DestroyWindow(m_hwnd);
        ::UnregisterClassW(wc.lpszClassName, wc.hInstance);
        return false;
    }

    // Initialize OpenGL extensions
    if (!GL::InitExtensions()) {
        LOG_ERROR("Failed to initialize OpenGL extensions");
    }

    // Show the window
    ::ShowWindow(m_hwnd, SW_SHOWDEFAULT);
    ::UpdateWindow(m_hwnd);
    
    // Setup Dear ImGui context
    IMGUI_CHECKVERSION();
    ImGui::CreateContext();
    ImGuiIO& io = ImGui::GetIO();
    // DockingEnable only — NavEnableKeyboard is intentionally omitted for a DCC
    // viewport application that manages its own keyboard shortcuts.
    // With NavEnableKeyboard set, io.WantCaptureKeyboard becomes true whenever any
    // ImGui window is focused, which would block all viewport hotkeys (F, A, etc.).
    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
    // Dock only while Shift is held during a title-bar/tab drag (the dock
    // position overlay appears then); a plain drag just moves the window,
    // so casual rearranging can't accidentally re-dock panels.
    io.ConfigDockingWithShift = true;
    // Windows move only via their title bar/tab, never by dragging empty
    // content. Without this, a press-drag that a content widget doesn't
    // capture falls through to ImGui's "drag body to move window" behaviour —
    // which, in the node-editor canvas (whose background button doesn't
    // reliably claim the drag in this nested-child embedding), meant dragging
    // in the graph moved the whole Material Editor window. Title-bar dragging
    // and Shift-to-dock are unaffected; this is also the desired behaviour for
    // every other content area (viewports, timelines) where a content drag
    // must never move the host window.
    io.ConfigWindowsMoveFromTitleBarOnly = true;

    // Store imgui.ini in %APPDATA%\UsdLayerManager\ so it doesn't litter the CWD.
    if (const char* appData = getenv("APPDATA")) {
        std::filesystem::path iniDir =
            std::filesystem::path(appData) / "UsdLayerManager";
        std::filesystem::create_directories(iniDir);
        m_iniFilePath = (iniDir / "imgui.ini").string();
        io.IniFilename = m_iniFilePath.c_str();
    }

    // Load Inter font.
    // The build directory copies the font as Inter.ttc; the install step renames
    // it to Inter.ttf.  Try .ttf first (install layout), fall back to .ttc (build).
    {
        ImFontConfig fontConfig;
        fontConfig.FontNo = 0; // select Regular face from the .ttc collection
        // Build exe-relative font paths so they work from both build and install dirs
        std::string fontPath0 = ResourcePath("resources/font/Inter.ttf");
        std::string fontPath1 = ResourcePath("resources/font/Inter.ttc");
        const char* tryPaths[] = { fontPath0.c_str(), fontPath1.c_str() };
        bool loaded = false;
        for (const char* p : tryPaths) {
            ImFont* f = io.Fonts->AddFontFromFileTTF(p, 15.0f, &fontConfig);
            if (f) {
                LOG_INFO(std::string("Loaded Inter font from ") + p);
                loaded = true;
                break;
            }
        }
        if (!loaded) {
            LOG_WARNING("Could not load Inter font; using built-in default");
            io.Fonts->AddFontDefault();
        }
    }

    // Setup Dear ImGui style
    ImGui::StyleColorsDark();
    
    // Setup Platform/Renderer backends
    ImGui_ImplWin32_Init(m_hwnd);
    ImGui_ImplOpenGL3_Init("#version 130");
    
    LOG_INFO("ImGui context initialized successfully");
    return true;
}

void ImGuiContext::Shutdown() {
    if (m_hwnd) {
        LOG_INFO("Shutting down ImGui context...");
        
        ImGui_ImplOpenGL3_Shutdown();
        ImGui_ImplWin32_Shutdown();
        ImGui::DestroyPlatformWindows();
        ImGui::DestroyContext();
        
        CleanupDeviceWGL();
        
        ::DestroyWindow(m_hwnd);
        ::UnregisterClassW(L"UsdLayerManager", ::GetModuleHandle(nullptr));
        
        m_hwnd = nullptr;
    }
}

void ImGuiContext::NewFrame() {
    ImGui_ImplOpenGL3_NewFrame();
    ImGui_ImplWin32_NewFrame();
    ImGui::NewFrame();
}

void ImGuiContext::Render() {
    ImGui::Render();
    glViewport(0, 0, m_width, m_height);
    glClearColor(0.45f, 0.55f, 0.60f, 1.00f);
    glClear(GL_COLOR_BUFFER_BIT);
    ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
    
    ::SwapBuffers(m_hdc);
}

bool ImGuiContext::ProcessEvents() {
    MSG msg;
    while (::PeekMessage(&msg, nullptr, 0U, 0U, PM_REMOVE)) {
        ::TranslateMessage(&msg);
        ::DispatchMessage(&msg);
        if (msg.message == WM_QUIT) {
            m_shouldClose = true;
        }
    }
    return !m_shouldClose;
}

bool ImGuiContext::CreateDeviceWGL() {
    m_hdc = ::GetDC(m_hwnd);
    
    PIXELFORMATDESCRIPTOR pfd = {};
    pfd.nSize = sizeof(pfd);
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 24;
    pfd.cStencilBits = 8;
    
    int pixelFormat = ::ChoosePixelFormat(m_hdc, &pfd);
    if (pixelFormat == 0) {
        LOG_ERROR("ChoosePixelFormat failed");
        return false;
    }
    
    if (!::SetPixelFormat(m_hdc, pixelFormat, &pfd)) {
        LOG_ERROR("SetPixelFormat failed");
        return false;
    }
    
    m_hglrc = ::wglCreateContext(m_hdc);
    if (!m_hglrc) {
        LOG_ERROR("wglCreateContext failed");
        return false;
    }
    
    if (!::wglMakeCurrent(m_hdc, m_hglrc)) {
        LOG_ERROR("wglMakeCurrent failed");
        return false;
    }
    
    return true;
}

void ImGuiContext::CleanupDeviceWGL() {
    if (m_hglrc) {
        ::wglMakeCurrent(nullptr, nullptr);
        ::wglDeleteContext(m_hglrc);
        m_hglrc = nullptr;
    }
    
    if (m_hdc) {
        ::ReleaseDC(m_hwnd, m_hdc);
        m_hdc = nullptr;
    }
}

LRESULT WINAPI ImGuiContext::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) {
        return true;
    }
    
    ImGuiContext* context = reinterpret_cast<ImGuiContext*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
    
    switch (msg) {
        case WM_SIZE:
            if (context && wParam != SIZE_MINIMIZED) {
                context->m_width = LOWORD(lParam);
                context->m_height = HIWORD(lParam);
            }
            return 0;
        case WM_SYSCOMMAND:
            if ((wParam & 0xfff0) == SC_KEYMENU) // Disable ALT application menu
                return 0;
            break;
        case WM_DESTROY:
            ::PostQuitMessage(0);
            return 0;
    }
    
    return ::DefWindowProcW(hWnd, msg, wParam, lParam);
}

} // namespace UsdLayerManager
