/**
 * @file windows_render_state.cpp
 * @brief Windows平台特定的窗口渲染状态实现
 *
 * 实现了Windows平台下窗口渲染状态的具体功能，包括Direct3D 11
 * 交换链的创建、管理、重建以及呈现操作。处理与窗口大小改变
 * 相关的资源管理和垂直同步设置。
 */

#include "windows_render_state.h"
#include <iostream>

#include "pixel_format_convert.h"
#include "misc/scope_exit.h"

/**
 * @brief 初始化窗口渲染状态
 * @param in_device Direct3D 11设备
 * @param in_factory DXGI工厂
 * @param in_window_frame_size 窗口框架大小
 * @param in_window_handle Windows HWND句柄
 * @return 初始化是否成功
 *
 * 创建交换链和渲染目标视图，并配置sokol图形库需要的交换链信息。
 * 使用窗口的当前大小和句柄设置交换链参数。
 */
bool windows_render_state::init(ID3D11Device* in_device, IDXGIFactory* in_factory, const Eigen::Vector2i& in_window_frame_size, void* in_window_handle) {
    dx_device    = in_device;
    dxgi_factory = in_factory;

    const auto& size          = in_window_frame_size;
    const auto window_handle = in_window_handle;

#if MIRAGE_USE_HDR
    constexpr auto sg_format = MIRAGE_HDR_FORMAT;
#else
    constexpr auto sg_format = MIRAGE_PIXEL_FORMAT;
#endif
    const auto format = sg_pixel_format_to_dxgi_srv(sg_format);

    // 创建D3D11渲染目标视图
    DXGI_SWAP_CHAIN_DESC swap_chain_desc = {
        .BufferDesc = {
            .Width = static_cast<UINT>(size.x()),
            .Height = static_cast<UINT>(size.y()),
            .RefreshRate = {
                .Numerator = 240,
                .Denominator = 1
            },
            .Format = format,
        },
        .SampleDesc = {
            .Count = 1,
            .Quality = 0
        },
        .BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
        .BufferCount = 2,
        .OutputWindow = (HWND)window_handle,
        .Windowed = TRUE,
        .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
        .Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING
    };

    // 创建交换链
    HRESULT hr = dxgi_factory->CreateSwapChain(dx_device, &swap_chain_desc, &dx_swap_chain);
    if (FAILED(hr)) {
        std::cerr << "mirage: " << "Failed to create DXGI Swap Chain. HRESULT: 0x" << std::hex << hr << std::dec <<
                std::endl;
        return false;
    }

    // 获取后台缓冲区
    ID3D11Texture2D* back_buffer = nullptr;
    hr                           = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &back_buffer);
    if (FAILED(hr)) {
        std::cerr << "mirage: " << "Failed to get back buffer from DXGI Swap Chain. HRESULT: 0x" << std::hex << hr <<
                std::dec << std::endl;
        return false;
    }
    ON_SCOPE_EXIT { back_buffer->Release(); };

    // 创建渲染目标视图
    ID3D11RenderTargetView* render_target_view = nullptr;
    hr = dx_device->CreateRenderTargetView(back_buffer, nullptr, &render_target_view);
    if (FAILED(hr)) {
        std::cerr << "mirage: " << "Failed to create render target view. HRESULT: 0x" << std::hex << hr << std::dec <<
                std::endl;
        return false;
    }

// #ifdef MIRAGE_USE_HDR
//  // 设置HDR元数据
//  DXGI_HDR_METADATA_HDR10 metadata = {};
//  metadata.MaxMasteringLuminance = 1000 * 10000; // 1000尼特
//  metadata.MinMasteringLuminance = 0.001 * 10000; // 0.001尼特
//  // ...设置其他元数据
//  dx_swap_chain->SetHDRMetaData(DXGI_HDR_METADATA_TYPE_HDR10, sizeof(metadata), &metadata);
// #endif

    // 配置sokol交换链信息
    swapchain.d3d11.render_view        = render_target_view;
    swapchain.width                    = static_cast<int>(size.x());
    swapchain.height                   = static_cast<int>(size.y());
    swapchain.color_format             = sg_format;
    swapchain.depth_format             = SG_PIXELFORMAT_NONE;
    swapchain.sample_count             = 1;
    swapchain.d3d11.resolve_view       = nullptr;
    swapchain.d3d11.depth_stencil_view = nullptr;
    return true;
}

/**
 * @brief 清理渲染资源
 *
 * 释放交换链和渲染目标视图资源。
 * 覆盖基类的clear方法。
 */
void windows_render_state::on_clear() {
    dx_swap_chain->Release();
    get_dx_render_target_view()->Release();
}

/**
 * @brief 呈现渲染内容到窗口
 *
 * 根据垂直同步设置和撕裂支持选择合适的呈现标志，
 * 调用交换链的Present方法显示渲染结果。
 * 处理可能的设备丢失和其他错误。
 */
void windows_render_state::present() {
    UINT presentFlags = 0;

    // 只有当垂直同步关闭(vsync=false)时才考虑使用允许撕裂的标志
    // 并且需要系统支持撕裂特性
    if (!vsync && tearing_supported) {
        presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
    }

    // 调用Present并处理可能的错误
    HRESULT hr = dx_swap_chain->Present(vsync ? 1 : 0, presentFlags);

    // 检查呈现结果
    if (FAILED(hr)) {
        if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) {
            // 设备丢失，需要重建设备和所有资源
            HRESULT reason = dx_device->GetDeviceRemovedReason();
            std::cerr << "设备丢失，原因: 0x" << std::hex << reason << std::dec << std::endl;
            // 在这里你可以触发设备重建流程
            // handle_device_lost();
        }
        else {
            std::cerr << "Present失败，错误码: 0x" << std::hex << hr << std::dec << std::endl;
        }
    }
}

/**
 * @brief 重建交换链
 * @param size 新的窗口大小
 *
 * 当窗口大小改变时重建交换链和渲染目标视图。
 * 根据可用的接口版本选择最佳的重建方法。
 * 处理不同版本的DXGI接口，提供向后兼容性。
 */
void windows_render_state::rebuild_swapchain(const Eigen::Vector2i& size) {
    // 检查尺寸是否有效
    if (size.x() == 0 || size.y() == 0) { return; }

    // 获取设备上下文
    ID3D11DeviceContext* context = nullptr;
    dx_device->GetImmediateContext(&context);
    if (!context) {
        std::cerr << "获取设备上下文失败" << std::endl;
        return;
    }

    // 1. 清除所有渲染管线绑定
    ID3D11RenderTargetView* nullViews[] = { nullptr };
    context->OMSetRenderTargets(1, nullViews, nullptr);
    context->ClearState();
    context->Flush();

    // 2. 释放当前渲染目标视图
    DXGI_FORMAT format                = DXGI_FORMAT_B8G8R8A8_UNORM; // 默认格式
    auto        dx_render_target_view = get_dx_render_target_view();
    if (dx_render_target_view) {
        // 保存格式以便稍后使用
        D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {};
        dx_render_target_view->GetDesc(&rtv_desc);
        format = rtv_desc.Format;

        // 释放资源
        dx_render_target_view->Release();
        swapchain.d3d11.render_view = nullptr;
    }

    // 3. 检查是否有更高级别的交换链接口
    HRESULT hr             = E_FAIL;
    UINT    swapChainFlags = 0;

    // 尝试获取IDXGISwapChain4 (Windows 10 1803+)
    IDXGISwapChain4* swapChain4 = nullptr;
    if (SUCCEEDED(dx_swap_chain->QueryInterface(__uuidof(IDXGISwapChain4), (void**)&swapChain4))) {
#if DEBUG
        std::cout << "使用IDXGISwapChain4重建交换链" << std::endl;
#endif

        // 保留当前交换链标志
        DXGI_SWAP_CHAIN_DESC1 desc = {};
        swapChain4->GetDesc1(&desc);
        swapChainFlags = desc.Flags;

        // 重建缓冲区
        hr = swapChain4->ResizeBuffers(
                                       0, // 保持当前缓冲区数量
                                       size.x(),
                                       size.y(), // 新尺寸
                                       DXGI_FORMAT_UNKNOWN, // 保持当前格式
                                       swapChainFlags // 使用当前标志
                                      );

        swapChain4->Release();
    }
    // 尝试获取IDXGISwapChain1 (Windows 8+)
    else {
        IDXGISwapChain1* swapChain1 = nullptr;
        if (SUCCEEDED(dx_swap_chain->QueryInterface(__uuidof(IDXGISwapChain1), (void**)&swapChain1))) {
            std::cout << "使用IDXGISwapChain1重建交换链" << std::endl;

            // 保留当前交换链标志
            DXGI_SWAP_CHAIN_DESC1 desc = {};
            swapChain1->GetDesc1(&desc);
            swapChainFlags = desc.Flags;

            // 重建缓冲区
            hr = swapChain1->ResizeBuffers(
                                           0,
                                           // 保持当前缓冲区数量
                                           size.x(),
                                           size.y(),
                                           // 新尺寸
                                           DXGI_FORMAT_UNKNOWN,
                                           // 保持当前格式
                                           swapChainFlags // 使用当前标志
                                          );

            swapChain1->Release();
        }
        // 回退到基础IDXGISwapChain
        else {
            std::cout << "使用基础IDXGISwapChain重建交换链" << std::endl;

            // 获取基础交换链描述
            DXGI_SWAP_CHAIN_DESC desc = {};
            dx_swap_chain->GetDesc(&desc);
            swapChainFlags = desc.Flags;

            // 重建缓冲区
            hr = dx_swap_chain->ResizeBuffers(
                                              0,
                                              // 保持当前缓冲区数量
                                              size.x(),
                                              size.y(),
                                              // 新尺寸
                                              DXGI_FORMAT_UNKNOWN,
                                              // 保持当前格式
                                              swapChainFlags // 使用当前标志
                                             );
        }
    }

    // 检查交换链重建是否成功
    if (FAILED(hr)) {
        std::cerr << "重建交换链失败，错误码: 0x" << std::hex << hr << std::dec << std::endl;

        // 检查设备是否被移除
        HRESULT deviceRemoveReason = dx_device->GetDeviceRemovedReason();
        if (deviceRemoveReason != S_OK) {
            std::cerr << "设备被移除，原因: 0x" << std::hex << deviceRemoveReason << std::dec << std::endl;
        }

        context->Release();
        return;
    }

    // 4. 创建新的渲染目标视图
    ID3D11Texture2D* backBuffer = nullptr;
    hr                          = dx_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**) &backBuffer);
    if (FAILED(hr)) {
        std::cerr << "获取后台缓冲区失败，错误码: 0x" << std::hex << hr << std::dec << std::endl;
        context->Release();
        return;
    }

    // 创建渲染目标视图
    hr = dx_device->CreateRenderTargetView(
                                           backBuffer,
                                           nullptr,
                                           // 使用默认视图描述
                                           (ID3D11RenderTargetView**) &swapchain.d3d11.render_view
                                          );

    // 释放后台缓冲区
    backBuffer->Release();

    if (FAILED(hr)) {
        std::cerr << "创建渲染目标视图失败，错误码: 0x" << std::hex << hr << std::dec << std::endl;
        context->Release();
        return;
    }

    // 5. 设置渲染目标和视口
    context->OMSetRenderTargets(1, (ID3D11RenderTargetView**) &swapchain.d3d11.render_view, nullptr);

    D3D11_VIEWPORT viewport = {};
    viewport.Width          = static_cast<float>(size.x());
    viewport.Height         = static_cast<float>(size.y());
    viewport.MinDepth       = 0.0f;
    viewport.MaxDepth       = 1.0f;
    context->RSSetViewports(1, &viewport);

    // 释放上下文
    context->Release();

#if DEBUG
    std::cout << "交换链重建成功，新尺寸: " << size.x() << "x" << size.y() << std::endl;
#endif
}
