#ifndef RENDER_DIRECTIONAL_SHADOW_H
#define RENDER_DIRECTIONAL_SHADOW_H

#include "shadow_map.h"

#include <cstdint>
#include <DirectXMath.h>
#include <d3d11.h>
#include <wrl/client.h>

class DirectionalShadow final {
public:

    struct Settings {
        std::uint32_t resolution = 4096;

        // 只对相机附近一定距离生成阴影。
        // 调用者应使用该距离重新构建相机投影矩阵。
        float shadowDistance = 80.0f;

        // 扩大光源正交投影，避免边缘物体突然消失。
        float xyPadding = 2.0f;
        float zPadding = 20.0f;

        // HLSL 深度比较偏移。
        float minBias = 0.0002f;
        float slopeBias = 0.0002f;

        // 阴影深度写入阶段的光栅化偏移。
        // 需要根据场景世界尺寸调整。
        int rasterDepthBias = 1000;
        float rasterSlopeBias = 1.5f;
        float rasterBiasClamp = 0.0f;

        // 必须与主渲染的顶点绕序保持一致。
        bool frontCounterClockwise = false;
    };

    struct alignas(16) ShadowCBData {
        DirectX::XMFLOAT4X4 lightViewProjection{};

        // xy：阴影贴图单个 texel 的 UV 尺寸
        // z ：最小深度偏移
        // w ：基于光照角度的斜率偏移
        DirectX::XMFLOAT4 shadowParams{};
    };

    static_assert(sizeof(ShadowCBData) % 16 == 0);

public:
    void Initialize(ID3D11Device *device, const Settings &settings);

    [[nodiscard]] float ShadowDistance() const noexcept { return m_settings.shadowDistance; }

    // cameraProjection 必须是使用 shadowDistance 构建的投影矩阵。
    void UpdateMatrices(DirectX::FXMMATRIX cameraView,
                        DirectX::CXMMATRIX cameraProjection,
                        const DirectX::XMFLOAT3 &lightDirection);

    // 开始阴影深度阶段。
    // 此函数不负责绑定具体的 Shadow VS 和 InputLayout。
    void BeginDepthPass(ID3D11DeviceContext *context);

    // 结束后不主动恢复主渲染状态。
    // Graphic::BeginMainPass() 应完整设置主 RTV、DSV、Viewport 和状态。
    void EndDepthPass(ID3D11DeviceContext *context) const;

    // 主渲染阶段绑定 b3、t8、s2。
    void BindMainPass(ID3D11DeviceContext *context) const;

    void UnbindShadowSRV(ID3D11DeviceContext *context) const;

private:
    void UploadConstantBuffer(ID3D11DeviceContext *context) const;

private:
    Settings m_settings{};
    ShadowCBData m_cbData{};

    ShadowMap m_shadowMap;

    Microsoft::WRL::ComPtr<ID3D11Buffer> m_shadowCB;
    Microsoft::WRL::ComPtr<ID3D11RasterizerState> m_rasterizerState;
    Microsoft::WRL::ComPtr<ID3D11DepthStencilState> m_depthStencilState;
};


#endif //RENDER_DIRECTIONAL_SHADOW_H
