// IMGUI相关库包含
#include "imgui.h"
#include "imgui_impl_win32.h"
#include "imgui_impl_dx11.h"

// ImNodes相关
#include "imnodes.h"

extern "C"
{
// Mingw 相关库
#include "d3d11.h"
#include "tchar.h"
#include "wchar.h"
#include "locale.h"
}

// 自定义头文件
#include "Config.hpp"           // 配置文件
#include "Global_Variables.hpp" // 全局变量相关
#include "UI_Layout.hpp"        // UI布局相关
#include "Command_Solve.hpp"    // 命令处理相关

// 辅助函数声明
bool CreateDeviceD3D(HWND hWnd); // 创建 Direct 3D 设备
void CleanupDeviceD3D();         // 清理 Direct3D 设备
void CreateRenderTarget();       // 创建渲染目标
void CleanupRenderTarget();      // 清理渲染目标

// Windows窗体事件处理函数声明
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// 重定义窗体消息处理函数 >> imgui_impl_win32.cpp
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// 入口主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    // 设置本地化以支持特殊字符
    setlocale(LC_ALL, ".UTF-8");

    // 命令行前置处理
    Pre_Command_Solve();

    // 启用DPI响应并获取主显示器分辨率
    ImGui_ImplWin32_EnableDpiAwareness();
    float main_scale = ImGui_ImplWin32_GetDpiScaleForMonitor(MonitorFromPoint(POINT{0, 0}, MONITOR_DEFAULTTOPRIMARY));

    // 窗体类结构体
    WNDCLASSEXW wc;                                       // Windows 扩展窗体类
    wc.cbSize = sizeof(wc);                               // 类结构大小
    wc.style = CS_CLASSDC;                                // 绘制样式使用类设备上下文 Class Device Context
    wc.lpfnWndProc = WndProc;                             // 窗体事件处理回调函数
    wc.cbClsExtra = 0L;                                   // 为窗口类额外分配的字节数，通常为 0
    wc.cbWndExtra = 0L;                                   // 为窗口实例额外分配的字节数，通常为 0
    wc.hInstance = hInstance;                             // 应用程序实例的句柄。
    wc.hIcon = LoadIconW(hInstance, IDI_APPLICATION);     // 应用图标样式
    wc.hCursor = LoadCursorW(hInstance, IDC_ARROW);       // 应用光标样式
    wc.hbrBackground = (HBRUSH)COLOR_WINDOWFRAME;         // 窗体背景颜色刷
    wc.lpszMenuName = nullptr;                            // 窗体菜单名称
    wc.lpszClassName = L"DLT_Splitter_Base_Window_Class"; // 窗体注册类名
    wc.hIconSm = LoadIconW(hInstance, IDI_APPLICATION);   // 应用小图标样式

    // 注册窗体类
    RegisterClassExW(&wc);

    // 创建窗体
    Main_Window_hWnd = CreateWindowW(wc.lpszClassName,                                                                // 创建的窗体类名
                                     MAIN_FRAME_TITTLE,                                                               // 窗体标题
                                     WS_OVERLAPPEDWINDOW,                                                             // 窗体风格为标准可重叠的顶层窗口
                                     (GetSystemMetrics(SM_CXSCREEN) - (MAIN_FRAME_SIZE_WIDTH_MIN * main_scale)) / 2,  // 起始坐标x，设置为屏幕正中
                                     (GetSystemMetrics(SM_CYSCREEN) - (MAIN_FRAME_SIZE_HEIGHT_MIN * main_scale)) / 2, // 起始坐标y，设置为屏幕正中
                                     (int)(MAIN_FRAME_SIZE_WIDTH_MIN * main_scale),                                   // 窗体长
                                     (int)(MAIN_FRAME_SIZE_HEIGHT_MIN * main_scale),                                  // 窗体宽
                                     nullptr,                                                                         // 父窗体句柄
                                     nullptr,                                                                         // 菜单句柄
                                     wc.hInstance,                                                                    // 创建实例句柄
                                     nullptr);                                                                        // 额外的事件参数指针

    // 在窗体内创建 Direct 3D 设备
    if (!CreateDeviceD3D(Main_Window_hWnd))
    {
        // 未创建完成时清理 Direct 3D 设备并清理窗体注册类
        CleanupDeviceD3D();
        UnregisterClassW(wc.lpszClassName, wc.hInstance);

        // 返回错误代码
        return -1;
    }

    // 启用文件拖拽输入
    DragAcceptFiles(Main_Window_hWnd, TRUE);

    // 显示窗体并强制更新一次窗体
    ShowWindow(Main_Window_hWnd, SW_SHOWDEFAULT);
    UpdateWindow(Main_Window_hWnd);

    // 检查版本
    IMGUI_CHECKVERSION();

    // 建立 Dear ImGui 上下文
    ImGui::CreateContext();

    // 建立ImNodes 上下文
    ImNodes::CreateContext();

    // 获取ImGui IO 设备配置结构体
    ImGuiIO &io = ImGui::GetIO();
    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;
    // io.ConfigDockingAlwaysTabBar = true;
    // io.ConfigDockingTransparentPayload = true;
    io.IniFilename = nullptr; // 不生成ini布局配置文件

    // 主题为亮/暗
    ImGui::StyleColorsLight();
    // ImGui::StyleColorsDark();

    // Setup scaling
    // 获取风格渲染配置并整缩放比例
    ImGuiStyle &style = ImGui::GetStyle();
    style.ScaleAllSizes(main_scale);   // Bake a fixed style scale. (until we have a solution for dynamic style scaling, changing this requires resetting Style + calling this again)
    style.FontScaleDpi = main_scale;   // Set initial font scale. (using io.ConfigDpiScaleFonts=true makes this unnecessary. We leave both here for documentation purpose)
    io.ConfigDpiScaleFonts = true;     // [Experimental] Automatically overwrite style.FontScaleDpi in Begin() when Monitor DPI changes. This will scale fonts but _NOT_ scale sizes/padding for now.
    io.ConfigDpiScaleViewports = true; // [Experimental] Scale Dear ImGui and Platform Windows when Monitor DPI changes.

    // 调整视窗移动出主窗体范围外时的风格以匹配原生风格
    // When viewports are enabled we tweak WindowRounding/WindowBg so platform windows can look identical to regular ones.
    if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
    {
        style.WindowRounding = 0.0f;
        style.Colors[ImGuiCol_WindowBg].w = 1.0f;
    }

    // 初始化渲染器后端(Renderer backends)
    ImGui_ImplWin32_Init(Main_Window_hWnd);
    ImGui_ImplDX11_Init(g_pd3dDevice, g_pd3dDeviceContext);

    // 字体配置相关
    {
        // Load Fonts
        // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
        // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
        // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
        // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
        // - Read 'docs/FONTS.md' for more instructions and details. If you like the default font but want it to scale better, consider using the 'ProggyVector' from the same author!
        // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
        // style.FontSizeBase = 20.0f;
        // io.Fonts->AddFontDefault();
        // io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf");
        // io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf");
        // io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf");
        // io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf");
        // ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf");
        // IM_ASSERT(font != nullptr);

        // 加载字体为微软雅黑
        io.Fonts->AddFontFromFileTTF("c:/windows/Fonts/msyh.ttc", 15.0f * main_scale, NULL, io.Fonts->GetGlyphRangesChineseFull());
    }

    // 主事件循环
    bool done = false;
    while (!done)
    {
        // 非阻塞式获取所有窗体消息，Pick消息后从消息队列删除，并转译和派发消息
        MSG msg;
        while (PeekMessageW(&msg, nullptr, 0U, 0U, PM_REMOVE))
        {
            TranslateMessage(&msg); // 转译
            DispatchMessageW(&msg); // 派发

            // 窗体关闭事件
            if (msg.message == WM_QUIT)
                done = true;
        }
        if (done)
            break;

        // 处理窗体最小化或屏幕被锁定
        if (g_SwapChainOccluded && g_pSwapChain->Present(0, DXGI_PRESENT_TEST) == DXGI_STATUS_OCCLUDED)
        {
            // 休眠10ms并跳过渲染
            continue;
        }
        g_SwapChainOccluded = false;

        // 开始创建当前绘制帧(创建顺序:DirectX -> Win32 -> ImGui)
        ImGui_ImplDX11_NewFrame();
        ImGui_ImplWin32_NewFrame();
        ImGui::NewFrame();

        // 此处进行UI布局
        UI_Layout();

        // 渲染部分
        {
            // 调用 ImGui 渲染器进行预渲染
            ImGui::Render();

            // 设置D3D渲染目标
            g_pd3dDeviceContext->OMSetRenderTargets(1, &g_mainRenderTargetView, nullptr);

            // 创建一个带透明度通道的RGB色
            ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
            const float clear_color_with_alpha[4] = {clear_color.x * clear_color.w,
                                                     clear_color.y * clear_color.w,
                                                     clear_color.z * clear_color.w,
                                                     clear_color.w};
            // 使用创建的RGB色擦除D3D渲染画布
            g_pd3dDeviceContext->ClearRenderTargetView(g_mainRenderTargetView, clear_color_with_alpha);

            // 渲染 ImGui 原始画面
            ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());

            // 启用脱离窗口渲染时更新主窗体外的部分
            if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
            {
                ImGui::UpdatePlatformWindows();
                ImGui::RenderPlatformWindowsDefault();
            }

            // 呈现当前帧到画布
            // HRESULT hr = g_pSwapChain->Present(0, 0); // 非垂直同步
            HRESULT hr = g_pSwapChain->Present(1, 0); // 垂直同步
            // HRESULT hr = g_pSwapChain->Present(2, 0); // 等待双倍的垂直同步时间

            // 更新交换链阻塞状态
            g_SwapChainOccluded = (hr == DXGI_STATUS_OCCLUDED);
        }
    }

    // 收尾清理工作
    ImGui_ImplDX11_Shutdown();  // 关闭 ImGui DX11 组件
    ImGui_ImplWin32_Shutdown(); // 关闭 ImGui Win32 组件
    ImGui::DestroyContext();    // 销毁 ImGui 上下文
    ImNodes::DestroyContext();  // 销毁 ImNodes 上下文

    CleanupDeviceD3D();                               // 清理 Direct 3D 设备
    DestroyWindow(Main_Window_hWnd);                  // 销毁窗体
    UnregisterClassW(wc.lpszClassName, wc.hInstance); // 取消窗体注册类

    return 0;
}

// 创建 Direct 3D 设备
bool CreateDeviceD3D(HWND hWnd)
{
    // Setup swap chain
    DXGI_SWAP_CHAIN_DESC sd;
    ZeroMemory(&sd, sizeof(sd));
    sd.BufferCount = 2;
    sd.BufferDesc.Width = 0;
    sd.BufferDesc.Height = 0;
    sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    sd.BufferDesc.RefreshRate.Numerator = 60;
    sd.BufferDesc.RefreshRate.Denominator = 1;
    sd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
    sd.OutputWindow = hWnd;
    sd.SampleDesc.Count = 1;
    sd.SampleDesc.Quality = 0;
    sd.Windowed = TRUE;
    sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;

    UINT createDeviceFlags = 0;
    // createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
    D3D_FEATURE_LEVEL featureLevel;
    const D3D_FEATURE_LEVEL featureLevelArray[2] = {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_0,
    };
    HRESULT res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
    if (res == DXGI_ERROR_UNSUPPORTED) // Try high-performance WARP software driver if hardware is not available.
        res = D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, createDeviceFlags, featureLevelArray, 2, D3D11_SDK_VERSION, &sd, &g_pSwapChain, &g_pd3dDevice, &featureLevel, &g_pd3dDeviceContext);
    if (res != S_OK)
        return false;

    // Disable DXGI's default Alt+Enter fullscreen behavior.
    // - You are free to leave this enabled, but it will not work properly with multiple viewports.
    // - This must be done for all windows associated to the device. Our DX11 backend does this automatically for secondary viewports that it creates.
    IDXGIFactory *pSwapChainFactory;
    if (SUCCEEDED(g_pSwapChain->GetParent(IID_PPV_ARGS(&pSwapChainFactory))))
        pSwapChainFactory->MakeWindowAssociation(hWnd, DXGI_MWA_NO_ALT_ENTER);

    CreateRenderTarget();
    return true;
}

// 清理 Direct3D 设备
void CleanupDeviceD3D()
{
    CleanupRenderTarget();
    if (g_pSwapChain)
    {
        g_pSwapChain->Release();
        g_pSwapChain = nullptr;
    }
    if (g_pd3dDeviceContext)
    {
        g_pd3dDeviceContext->Release();
        g_pd3dDeviceContext = nullptr;
    }
    if (g_pd3dDevice)
    {
        g_pd3dDevice->Release();
        g_pd3dDevice = nullptr;
    }
}

// 创建渲染目标
void CreateRenderTarget()
{
    ID3D11Texture2D *pBackBuffer;
    g_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&pBackBuffer));
    g_pd3dDevice->CreateRenderTargetView(pBackBuffer, nullptr, &g_mainRenderTargetView);
    pBackBuffer->Release();
}

// 清理渲染目标
void CleanupRenderTarget()
{
    if (g_mainRenderTargetView)
    {
        g_mainRenderTargetView->Release();
        g_mainRenderTargetView = nullptr;
    }
}

// Windows窗体事件处理函数
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    // 优先让ImGui处理消息
    if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
        return true;

    // 筛查并处理余下的消息事件
    switch (msg)
    {
    case WM_DROPFILES: // 文件拖拽事件
    {
        HDROP hDrop = (HDROP)wParam; // 拖拽数据句柄
        // 获取拖拽的文件总数
        UINT fileCount = DragQueryFileW(hDrop, 0xFFFFFFFF, NULL, 0);

        // 非法输入检测
        bool with_illegal_input = false;
        bool with_duplicated_input = false;

        // 添加到文件列表
        for (UINT count = 0; count < fileCount; count++)
        {
            wchar_t filePath[MAX_PATH] = {L'\0'};
            DragQueryFileW(
                hDrop,    // 拖拽句柄
                count,    // 文件索引（0 表示第一个文件）
                filePath, // 存储路径的缓冲区
                MAX_PATH  // 缓冲区大小
            );

            char file_path_multi_byte[MAX_PATH] = {'\0'};
            int size_needed = WideCharToMultiByte(CP_UTF8, 0, filePath, -1, NULL, 0, NULL, NULL);
            WideCharToMultiByte(CP_UTF8, 0, filePath, -1, file_path_multi_byte, size_needed, NULL, NULL);

            // 重复文件剔除
            bool duplicated = false;
            for (Input_File_Node *p_file_node = input_dlt_file_list_head; p_file_node != nullptr; p_file_node = p_file_node->p_next)
                if (!strcmp(p_file_node->file_name_str, file_path_multi_byte))
                {
                    duplicated = true;
                    with_duplicated_input = true;
                    break;
                }
            if (duplicated)
                continue;

            // 文件类型后缀过滤
            const char *p_extension_name = file_path_multi_byte + (strlen(file_path_multi_byte) - 4);
            if (p_extension_name[0] != '.' || toupper(p_extension_name[1]) != 'D' || toupper(p_extension_name[2]) != 'L' || toupper(p_extension_name[3]) != 'T')
            {
                with_illegal_input = true;
                continue;
            }

            // 开辟新文件节点
            Input_File_Node *p_new_file_node = (Input_File_Node *)malloc(sizeof(Input_File_Node));
            p_new_file_node->file_name_str = (char *)malloc(strlen(file_path_multi_byte) + 1);
            memset(p_new_file_node->file_name_str, '\0', strlen(file_path_multi_byte) + 1);
            memcpy(p_new_file_node->file_name_str, file_path_multi_byte, strlen(file_path_multi_byte) + 1);
            p_new_file_node->p_next = nullptr;

            // 插入文件列表
            if (input_dlt_file_list_head == nullptr)
                input_dlt_file_list_head = p_new_file_node;
            else
            {
                Input_File_Node *p_target_node = input_dlt_file_list_head;
                for (size_t count = 1; count < input_dlt_file_count; count++)
                    p_target_node = p_target_node->p_next;

                p_target_node->p_next = p_new_file_node;
            }

            input_dlt_file_count++;
        }

        // 释放拖拽资源（必须调用，否则内存泄漏）
        DragFinish(hDrop);

        if (with_duplicated_input)
            MessageBoxW(hWnd, L"存在重复输入", L"", MB_OK | MB_ICONWARNING);

        if (with_illegal_input)
            MessageBoxW(hWnd, L"存在非法输入，仅支持 .dlt 文件", L"", MB_OK | MB_ICONWARNING);

        return 0;
    }
    case WM_SIZE:                     // 窗体大小变事件
        if (wParam != SIZE_MINIMIZED) // 窗体未处于最小化时进行重绘
        {
            // 清理渲染器并重新创建对应窗体大小的渲染器目标 低字为宽，高字为长
            CleanupRenderTarget();
            g_pSwapChain->ResizeBuffers(0, (UINT)LOWORD(lParam), (UINT)HIWORD(lParam), DXGI_FORMAT_UNKNOWN, 0);
            CreateRenderTarget();
        }
        return 0;
    case WM_SYSCOMMAND:                      // 系统指令事件
        if ((wParam & 0xfff0) == SC_KEYMENU) // 禁用ALT键菜单响应，交给IMGUI实现 Disable ALT
            return 0;
        break;
    case WM_DESTROY:        // 窗体销毁事件
        PostQuitMessage(0); // 派发应用退出(Quit)消息
        return 0;
    }

    // 剩余的消息交给默认的消息处理函数
    return DefWindowProcW(hWnd, msg, wParam, lParam);
}
