#include "skybox_renderer.h"

#include "texture.h"

#include <d3dcompiler.h>
#include <string>

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

namespace {
UINT GetShaderCompileFlags() {
    UINT flags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS;
#ifdef _DEBUG
    flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
    flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
#endif
    return flags;
}

bool CompileSkyboxShader(const std::filesystem::path &path,
                         const char *entryPoint,
                         const char *target,
                         Microsoft::WRL::ComPtr<ID3DBlob> &outBlob) {
    outBlob.Reset();
    Microsoft::WRL::ComPtr<ID3DBlob> errorBlob;

    const HRESULT hr = D3DCompileFromFile(
        path.c_str(), nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE,
        entryPoint, target, GetShaderCompileFlags(), 0,
        outBlob.GetAddressOf(), errorBlob.GetAddressOf());
    if (SUCCEEDED(hr)) return true;

    if (errorBlob) {
        OutputDebugStringA(static_cast<const char *>(errorBlob->GetBufferPointer()));
    }

    std::wstring message = L"编译天空盒 Shader 失败。\n文件：";
    message += path.wstring();
    MessageBoxW(nullptr, message.c_str(), L"SkyboxRenderer", MB_OK);
    return false;
}
}

bool SkyboxRenderer::Initialize(ID3D11Device *device,
                                const std::filesystem::path &shaderPath) {
    if (!device || shaderPath.empty()) return false;
    if (m_vertexShader.Get() && m_pixelShader.Get() && m_sampler.Get() &&
        m_rasterizerState.Get() && m_depthStencilState.Get()) {
        return true;
    }

    Microsoft::WRL::ComPtr<ID3DBlob> vertexBlob;
    Microsoft::WRL::ComPtr<ID3DBlob> pixelBlob;
    if (!CompileSkyboxShader(shaderPath, "VSMain", "vs_5_0", vertexBlob) ||
        !CompileSkyboxShader(shaderPath, "PSMain", "ps_5_0", pixelBlob)) {
        return false;
    }

    if (FAILED(device->CreateVertexShader(
            vertexBlob->GetBufferPointer(), vertexBlob->GetBufferSize(),
            nullptr, m_vertexShader.ReleaseAndGetAddressOf()))) {
        return false;
    }
    if (FAILED(device->CreatePixelShader(
            pixelBlob->GetBufferPointer(), pixelBlob->GetBufferSize(),
            nullptr, m_pixelShader.ReleaseAndGetAddressOf()))) {
        return false;
    }

    D3D11_SAMPLER_DESC samplerDesc{};
    samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
    samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
    samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
    samplerDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
    samplerDesc.MinLOD = 0.0f;
    samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
    if (FAILED(device->CreateSamplerState(
            &samplerDesc, m_sampler.ReleaseAndGetAddressOf()))) {
        return false;
    }

    // [新增] Shader 内置 36 个顶点，关闭剔除可避免顶点绕序影响天空盒。
    D3D11_RASTERIZER_DESC rasterizerDesc{};
    rasterizerDesc.FillMode = D3D11_FILL_SOLID;
    rasterizerDesc.CullMode = D3D11_CULL_NONE;
    rasterizerDesc.DepthClipEnable = TRUE;
    if (FAILED(device->CreateRasterizerState(
            &rasterizerDesc, m_rasterizerState.ReleaseAndGetAddressOf()))) {
        return false;
    }

    // [新增] 天空盒强制输出 z=w，只在深度仍为 1 的背景像素通过；不修改深度缓冲。
    D3D11_DEPTH_STENCIL_DESC depthDesc{};
    depthDesc.DepthEnable = TRUE;
    depthDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
    depthDesc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL;
    depthDesc.StencilEnable = FALSE;
    if (FAILED(device->CreateDepthStencilState(
            &depthDesc, m_depthStencilState.ReleaseAndGetAddressOf()))) {
        return false;
    }

    return true;
}

void SkyboxRenderer::SetTexture(const TextureCube *textureCube) noexcept {
    m_cubeSrv.Reset();
    if (textureCube && textureCube->SRV()) {
        // [新增] 保留 SRV 的 COM 引用，避免外层包装对象移动时产生悬空资源。
        m_cubeSrv = textureCube->SRV();
    }
}

void SkyboxRenderer::ClearTexture() noexcept {
    m_cubeSrv.Reset();
}

bool SkyboxRenderer::IsReady() const noexcept {
    return m_vertexShader.Get() != nullptr &&
           m_pixelShader.Get() != nullptr &&
           m_cubeSrv.Get() != nullptr &&
           m_sampler.Get() != nullptr &&
           m_rasterizerState.Get() != nullptr &&
           m_depthStencilState.Get() != nullptr;
}

void SkyboxRenderer::Draw(ID3D11DeviceContext *context) const {
    if (!context || !IsReady()) return;

    // [新增] VS 根据 SV_VertexID 读取内置立方体顶点，不需要 InputLayout/VB/IB。
    context->IASetInputLayout(nullptr);
    context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    context->VSSetShader(m_vertexShader.Get(), nullptr, 0);
    context->PSSetShader(m_pixelShader.Get(), nullptr, 0);
    context->RSSetState(m_rasterizerState.Get());
    context->OMSetDepthStencilState(m_depthStencilState.Get(), 0);

    ID3D11ShaderResourceView *srv = m_cubeSrv.Get();
    context->PSSetShaderResources(TextureSlot, 1, &srv);

    ID3D11SamplerState *sampler = m_sampler.Get();
    context->PSSetSamplers(SamplerSlot, 1, &sampler);

    context->Draw(36, 0);

    // [新增] 主动解绑，后续若要把该 Cube 作为 RTV 重烘焙，不会产生 SRV/RTV 冲突。
    ID3D11ShaderResourceView *nullSrv = nullptr;
    context->PSSetShaderResources(TextureSlot, 1, &nullSrv);
}
