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

// [新增] WIC 用于从 PNG/JPG/BMP/TIFF 等普通图片文件解码 RGBA8 像素。
#include <wincodec.h>
#pragma comment(lib, "windowscodecs.lib")
#pragma comment(lib, "ole32.lib")


namespace {
std::unordered_map<std::string, Texture2D *> map_Name_LoadedTex{};
Microsoft::WRL::ComPtr<ID3D11SamplerState> cp_defaultSampler{};

// [新增] WIC 解码后的临时 RGBA8 数据。数据只需存活到 UpdateSubresource/CreateTexture2D 完成。
struct WicImageData {
    UINT width = 0;
    UINT height = 0;
    UINT rowPitch = 0;
    UINT slicePitch = 0;
    std::vector<std::uint8_t> pixels;
};

// [新增] 统一管理当前线程上的 COM 初始化和 WIC Factory 生命周期。
class WicFactoryScope final {
public:
    bool Initialize() {
        const HRESULT comResult = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
        if (SUCCEEDED(comResult)) {
            m_shouldUninitialize = true;
        } else if (comResult != RPC_E_CHANGED_MODE) {
            return false;
        }

        return SUCCEEDED(CoCreateInstance(
            CLSID_WICImagingFactory,
            nullptr,
            CLSCTX_INPROC_SERVER,
            IID_PPV_ARGS(m_factory.ReleaseAndGetAddressOf())));
    }

    ~WicFactoryScope() {
        if (m_shouldUninitialize) CoUninitialize();
    }

    [[nodiscard]] IWICImagingFactory *Get() const noexcept {
        return m_factory.Get();
    }

private:
    bool m_shouldUninitialize = false;
    Microsoft::WRL::ComPtr<IWICImagingFactory> m_factory;
};

// [新增] 把任意 WIC 支持的输入像素格式统一转换为 32 位 RGBA。
bool LoadWicRgba8(IWICImagingFactory *factory,
                  const std::filesystem::path &path,
                  WicImageData &outImage) {
    if (!factory || path.empty()) return false;

    Microsoft::WRL::ComPtr<IWICBitmapDecoder> decoder;
    HRESULT hr = factory->CreateDecoderFromFilename(
        path.c_str(), nullptr, GENERIC_READ,
        WICDecodeMetadataCacheOnLoad, decoder.GetAddressOf());
    if (FAILED(hr)) return false;

    Microsoft::WRL::ComPtr<IWICBitmapFrameDecode> frame;
    hr = decoder->GetFrame(0, frame.GetAddressOf());
    if (FAILED(hr)) return false;

    UINT width = 0;
    UINT height = 0;
    hr = frame->GetSize(&width, &height);
    if (FAILED(hr) || width == 0 || height == 0) return false;

    WICPixelFormatGUID sourceFormat{};
    hr = frame->GetPixelFormat(&sourceFormat);
    if (FAILED(hr)) return false;

    Microsoft::WRL::ComPtr<IWICFormatConverter> converter;
    hr = factory->CreateFormatConverter(converter.GetAddressOf());
    if (FAILED(hr)) return false;

    BOOL canConvert = FALSE;
    hr = converter->CanConvert(sourceFormat, GUID_WICPixelFormat32bppRGBA, &canConvert);
    if (FAILED(hr) || !canConvert) return false;

    hr = converter->Initialize(
        frame.Get(), GUID_WICPixelFormat32bppRGBA,
        WICBitmapDitherTypeNone, nullptr, 0.0,
        WICBitmapPaletteTypeCustom);
    if (FAILED(hr)) return false;

    if (width > std::numeric_limits<UINT>::max() / 4u) return false;
    const UINT rowPitch = width * 4u;
    const std::uint64_t totalBytes =
            static_cast<std::uint64_t>(rowPitch) * static_cast<std::uint64_t>(height);
    if (totalBytes == 0 || totalBytes > std::numeric_limits<UINT>::max()) return false;

    WicImageData decoded{};
    decoded.width = width;
    decoded.height = height;
    decoded.rowPitch = rowPitch;
    decoded.slicePitch = static_cast<UINT>(totalBytes);
    decoded.pixels.resize(static_cast<std::size_t>(totalBytes));

    hr = converter->CopyPixels(
        nullptr, decoded.rowPitch, decoded.slicePitch, decoded.pixels.data());
    if (FAILED(hr)) return false;

    outImage = std::move(decoded);
    return true;
}

void WriteTextureCubeLoadError(const std::filesystem::path &path,
                               const wchar_t *reason) {
    std::wstring message = L"TextureCube 导入失败：";
    message += reason;
    if (!path.empty()) {
        message += L"\n文件：";
        message += path.wstring();
    }
    message += L"\n";
    OutputDebugStringW(message.c_str());
}
}

//region class TexSampler
ID3D11SamplerState *TexSampler::S_DefaultColorSamper() {
    return cp_defaultSampler.Get();
}

bool TexSampler::S_InitOnWmCreate(HWND hwnd, ID3D11Device *device) {
    D3D11_SAMPLER_DESC sdesc{};
    sdesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
    sdesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
    sdesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
    sdesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
    sdesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
    sdesc.MinLOD = 0.0f;
    sdesc.MaxLOD = D3D11_FLOAT32_MAX;
    HRESULT hr = device->CreateSamplerState(&sdesc, cp_defaultSampler.ReleaseAndGetAddressOf());
    if (FAILED(hr)) {
        MessageBoxW(hwnd, L"创建默认ID3D11SamplerState失败",
                    L"resouces::TexSampler::S_InitSamplers",MB_OK);
        return false;
    }
    return true;
}
//endregion class TexSamler


//region class Texture2d
namespace {
struct DefaultTexture {
    Microsoft::WRL::ComPtr<ID3D11Texture2D> texture;
    Microsoft::WRL::ComPtr<ID3D11ShaderResourceView> srv;
};
bool g_initialized{false};

DefaultTexture g_whiteSRGB;
DefaultTexture g_flatNormalLinear;
DefaultTexture g_defaultOrmLinear;
DefaultTexture g_blackSRGB;

void ResetAll() {
    g_whiteSRGB = {};
    g_flatNormalLinear = {};
    g_defaultOrmLinear = {};
    g_blackSRGB = {};
    g_initialized = false;
}
bool CreateSolidTexture(ID3D11Device *device, const std::array<unsigned char, 4> &rgba,
                        DXGI_FORMAT format, DefaultTexture &outTexture) {

    D3D11_TEXTURE2D_DESC desc{};
    desc.Width = 1;
    desc.Height = 1;
    desc.MipLevels = 1;
    desc.ArraySize = 1;
    desc.Format = format;
    desc.SampleDesc.Count = 1;
    desc.Usage = D3D11_USAGE_IMMUTABLE;
    desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

    D3D11_SUBRESOURCE_DATA init{};
    init.pSysMem = rgba.data();
    init.SysMemPitch = 4;
    init.SysMemSlicePitch = 4;

    HRESULT hr = device->CreateTexture2D(&desc, &init, outTexture.texture.ReleaseAndGetAddressOf());
    if (FAILED(hr)) return false;

    hr = device->CreateShaderResourceView(outTexture.texture.Get(), nullptr,
                                          outTexture.srv.ReleaseAndGetAddressOf());
    return SUCCEEDED(hr);
}
}

int Texture2D::S_AddTexture(const Z::Task::AddTextureResource *data, ID3D11Device *device) {
    auto it = map_Name_LoadedTex.find(data->texName);
    if (it != map_Name_LoadedTex.end()) {
        MessageBoxW(nullptr, L"texture名称重复,未添加",
                    L"Resouce::Texture2D::S_AddTexture",MB_OK);
        return -1;
    }
    int id = static_cast<int>(map_Name_LoadedTex.size());
    Texture2D *tex{};
    try {
        tex = new Texture2D(id, data, device);
    } catch (...) {
        return -1;
    }
    map_Name_LoadedTex.emplace(std::pair{tex->name, tex});
    return id;
}
Texture2D *Texture2D::S_GetTexture2D(const char *texStemName) {
    std::string name{texStemName};
    auto it = map_Name_LoadedTex.find(name);
    if (it != map_Name_LoadedTex.end()) {
        return it->second;
    }
    return nullptr;
}
std::unique_ptr<Texture2D> Texture2D::S_CreateImmutable(ID3D11Device *device, const RawCreateDesc &desc) {
    if (!device || !desc.data || desc.width == 0 || desc.height == 0 ||
        desc.rowPitch == 0 || desc.format == DXGI_FORMAT_UNKNOWN) {
        return nullptr;
    }

    const UINT slicePitch = desc.slicePitch != 0
                                ? desc.slicePitch
                                : desc.rowPitch * desc.height;
    auto texture = std::unique_ptr<Texture2D>(
        new Texture2D(desc.name, desc.rowPitch, slicePitch));

    D3D11_TEXTURE2D_DESC textureDesc{};
    textureDesc.Width = desc.width;
    textureDesc.Height = desc.height;
    textureDesc.MipLevels = 1;
    textureDesc.ArraySize = 1;
    textureDesc.Format = desc.format;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.Usage = D3D11_USAGE_IMMUTABLE;
    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;

    D3D11_SUBRESOURCE_DATA initialData{};
    initialData.pSysMem = desc.data;
    initialData.SysMemPitch = desc.rowPitch;
    initialData.SysMemSlicePitch = slicePitch;

    if (FAILED(device->CreateTexture2D(
        &textureDesc, &initialData, texture->cp_tex2D.GetAddressOf()))) {
        return nullptr;
    }

    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{};
    srvDesc.Format = desc.format;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2D.MostDetailedMip = 0;
    srvDesc.Texture2D.MipLevels = 1;

    if (FAILED(device->CreateShaderResourceView(
        texture->cp_tex2D.Get(), &srvDesc, texture->cp_srv.GetAddressOf()))) {
        return nullptr;
    }

    return texture;
}

//[IBL-add]
std::unique_ptr<Texture2D> Texture2D::CreateRenderTarget(ID3D11Device *device,
                                                         const RenderTargetCreateDesc &desc) {
    if (!device || desc.width == 0 || desc.height == 0 ||
        desc.format == DXGI_FORMAT_UNKNOWN) {
        return nullptr;
    }

    // auto texture = std::unique_ptr<Texture2D>(
    //     new Texture2D(desc.name, 0u, 0u));

    std::unique_ptr<Texture2D> texture{new Texture2D{desc.name, 0u, 0u}};

    D3D11_TEXTURE2D_DESC textureDesc{};
    textureDesc.Width = desc.width;
    textureDesc.Height = desc.height;
    textureDesc.MipLevels = 1;
    textureDesc.ArraySize = 1;
    textureDesc.Format = desc.format;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.Usage = D3D11_USAGE_DEFAULT;
    textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;

    if (FAILED(device->CreateTexture2D(
        &textureDesc, nullptr, texture->cp_tex2D.GetAddressOf()))) {
        return nullptr;
    }

    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{};
    srvDesc.Format = desc.format;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    srvDesc.Texture2D.MostDetailedMip = 0;
    srvDesc.Texture2D.MipLevels = 1;

    if (FAILED(device->CreateShaderResourceView(
        texture->cp_tex2D.Get(), &srvDesc, texture->cp_srv.GetAddressOf()))) {
        return nullptr;
    }

    return texture;

}

ID3D11ShaderResourceView *Texture2D::S_WhiteSRGB() noexcept { return g_whiteSRGB.srv.Get(); }
ID3D11ShaderResourceView *Texture2D::S_FlatNormalLinear() noexcept { return g_flatNormalLinear.srv.Get(); }
ID3D11ShaderResourceView *Texture2D::S_DefaultOrmLinear() noexcept { return g_defaultOrmLinear.srv.Get(); }
ID3D11ShaderResourceView *Texture2D::S_BlackSRGB() noexcept { return g_blackSRGB.srv.Get(); }
bool Texture2D::S_InitOnWmCreate(HWND hwnd, ID3D11Device *device) {
    if (g_initialized) return true;
    if (!device) return false;
    // BaseColor 缺省为白色；SRV 自动把采样值从 sRGB 转为线性。
    if (!CreateSolidTexture(device, {255, 255, 255, 255},
                            DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, g_whiteSRGB)) {
        ResetAll();
        return false;
    }
    // 切线空间 +Z，即 (0.5, 0.5, 1.0)。
    if (!CreateSolidTexture(device, {128, 128, 255, 255},
                            DXGI_FORMAT_R8G8B8A8_UNORM, g_flatNormalLinear)) {
        ResetAll();
        return false;
    }
    // ORM: AO=1, Roughness=1, Metallic=0。
    if (!CreateSolidTexture(device, {255, 255, 0, 255},
                            DXGI_FORMAT_R8G8B8A8_UNORM, g_defaultOrmLinear)) {
        ResetAll();
        return false;
    }
    // Emissive 缺省为黑色。
    if (!CreateSolidTexture(device, {0, 0, 0, 255},
                            DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, g_blackSRGB)) {
        ResetAll();
        return false;
    }
    g_initialized = true;
    return true;
}

Texture2D::Texture2D(int index, const Z::Task::AddTextureResource *tex, ID3D11Device *device) :
name(tex->texName), rowPitch(tex->rowPitch), size(tex->size) {
    std::cout << "Create Texture2D: " << tex->texName <<
            "\nsize=" << tex->size <<
            "\nrowPitch=" << tex->rowPitch <<
            "\nbytesPerPixel=" << tex->bytesPerPixel << std::endl;

    this->index = index;
    this->type = tex->type;
    this->bpp16 = tex->bpp16;
    this->srgb = tex->SRGB;

    // bool b_srgb = tex->type == Z::TextureType::DIFFUSE || tex->type == Z::TextureType::BASE_COLOR;
    bool b_srgb = tex->SRGB;
    DXGI_FORMAT format = b_srgb ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB : DXGI_FORMAT_R8G8B8A8_UNORM;

    D3D11_TEXTURE2D_DESC desc{};
    desc.Width = tex->rowPitch / tex->bytesPerPixel;//TODO 根据BPP16重新计算
    desc.Height = tex->size / tex->rowPitch;        //     重新计算，在导入时也重算
    desc.Format = format;
    desc.MipLevels = 1;
    desc.MiscFlags = 0;
    desc.ArraySize = 1;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.Usage = D3D11_USAGE_DEFAULT;
    desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    desc.CPUAccessFlags = DXGI_CPU_ACCESS_NONE;

    D3D11_SUBRESOURCE_DATA init{};
    init.pSysMem = tex->dataptr;
    init.SysMemPitch = tex->rowPitch;
    init.SysMemSlicePitch = tex->size;

    HRESULT hr = device->CreateTexture2D(&desc, &init, cp_tex2D.GetAddressOf());
    if (FAILED(hr)) {
        MessageBoxW(nullptr, L"创建Tex2D失败", L"resources.cpp Texture2D::构造器",MB_OK);
        throw std::runtime_error{"create tex2d failed"};
    }

    D3D11_SHADER_RESOURCE_VIEW_DESC sd{};
    sd.Format = format;
    sd.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
    sd.Texture2D.MostDetailedMip = 0;
    sd.Texture2D.MipLevels = 1;

    hr = device->CreateShaderResourceView(cp_tex2D.Get(), &sd, cp_srv.GetAddressOf());
    if (FAILED(hr)) {
        MessageBoxW(nullptr, L"创建SRV失败", L"resources.cpp Texture2D::构造器",MB_OK);
        throw std::runtime_error{"create srv failed"};
    }
}

Texture2D::Texture2D(std::string textureName, unsigned int textureRowPitch, unsigned int textureSize)
: type{}, index(std::numeric_limits<unsigned int>::max()), name(std::move(textureName)),
  rowPitch(textureRowPitch), size(textureSize) {}

//endregion class Texture2D


//region class TextureCube
//=public
std::unique_ptr<TextureCube> TextureCube::S_Create(ID3D11Device *device, const CreateDesc &desc) {
    if (!device || desc.size == 0 || desc.mipLevels == 0 ||
        desc.format == DXGI_FORMAT_UNKNOWN) {
        return nullptr;
    }

    const std::size_t expectedSubresources =
            static_cast<std::size_t>(6) * desc.mipLevels;
    if (desc.subresources.size() != expectedSubresources) return nullptr;

    std::vector<D3D11_SUBRESOURCE_DATA> initialData(expectedSubresources);
    for (std::size_t i = 0; i < expectedSubresources; ++i) {
        const SubresourceData &src = desc.subresources[i];
        if (!src.data || src.rowPitch == 0) return nullptr;

        initialData[i].pSysMem = src.data;
        initialData[i].SysMemPitch = src.rowPitch;
        initialData[i].SysMemSlicePitch = src.slicePitch;
    }

    auto cube = std::unique_ptr<TextureCube>(
        new TextureCube(desc.name, desc.size, desc.mipLevels, desc.format));

    D3D11_TEXTURE2D_DESC textureDesc{};
    textureDesc.Width = desc.size;
    textureDesc.Height = desc.size;
    textureDesc.MipLevels = desc.mipLevels;
    textureDesc.ArraySize = 6;
    textureDesc.Format = desc.format;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.Usage = D3D11_USAGE_IMMUTABLE;
    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;

    if (FAILED(device->CreateTexture2D(
        &textureDesc, initialData.data(), cube->m_texture.GetAddressOf()))) {
        return nullptr;
    }

    if (!S_CreateCubeSRV(device, *cube)) return nullptr;
    return cube;
}

std::unique_ptr<TextureCube> TextureCube::S_CreateWithGeneratedMips(
    ID3D11Device *device, ID3D11DeviceContext *context, std::string name, UINT size,
    DXGI_FORMAT format, const std::array<SubresourceData, 6> &faces) {

    if (!device || !context || size == 0 || format == DXGI_FORMAT_UNKNOWN ||
        !S_SupportsGeneratedMips(device, format)) {
        return nullptr;
    }

    for (const SubresourceData &face: faces) {
        if (!face.data || face.rowPitch == 0) return nullptr;
    }

    std::unique_ptr<TextureCube> cube{
        new TextureCube(std::move(name), size, 0, format)
    };

    D3D11_TEXTURE2D_DESC textureDesc{};
    textureDesc.Width = size;
    textureDesc.Height = size;
    textureDesc.MipLevels = 0;
    textureDesc.ArraySize = 6;
    textureDesc.Format = format;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.Usage = D3D11_USAGE_DEFAULT;
    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
    textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE |
                            D3D11_RESOURCE_MISC_GENERATE_MIPS;

    if (FAILED(device->CreateTexture2D(
        &textureDesc, nullptr, cube->m_texture.GetAddressOf()))) {
        return nullptr;
    }

    D3D11_TEXTURE2D_DESC actualDesc{};
    cube->m_texture->GetDesc(&actualDesc);
    cube->m_mipLevels = actualDesc.MipLevels;

    if (!S_CreateCubeSRV(device, *cube)) return nullptr;

    for (UINT face = 0; face < 6; ++face) {
        const UINT subresource = D3D11CalcSubresource(0, face, cube->m_mipLevels);
        context->UpdateSubresource(
            cube->m_texture.Get(), subresource, nullptr,
            faces[face].data, faces[face].rowPitch, faces[face].slicePitch);
    }

    context->GenerateMips(cube->m_srv.Get());
    return cube;
}

std::unique_ptr<TextureCube> TextureCube::S_CreateFromTexture2Ds(
    ID3D11Device *device, ID3D11DeviceContext *context, std::string name,
    const std::array<const Texture2D *, 6> &faces, bool generateMips) {
    if (!device || !context || !faces[0] || !faces[0]->Resource()) return nullptr;

    D3D11_TEXTURE2D_DESC sourceDesc{};
    faces[0]->Resource()->GetDesc(&sourceDesc);

    if (sourceDesc.Width == 0 || sourceDesc.Width != sourceDesc.Height ||
        sourceDesc.ArraySize != 1 || sourceDesc.SampleDesc.Count != 1 ||
        sourceDesc.Format == DXGI_FORMAT_UNKNOWN) {
        return nullptr;
    }

    if (generateMips && !S_SupportsGeneratedMips(device, sourceDesc.Format)) {
        return nullptr;
    }

    for (const Texture2D *face: faces) {
        if (!face || !face->Resource()) return nullptr;

        D3D11_TEXTURE2D_DESC current{};
        face->Resource()->GetDesc(&current);
        if (current.Width != sourceDesc.Width ||
            current.Height != sourceDesc.Height ||
            current.Format != sourceDesc.Format ||
            current.ArraySize != 1 ||
            current.SampleDesc.Count != 1) {
            return nullptr;
        }
    }

    auto cube = std::unique_ptr<TextureCube>(
        new TextureCube(std::move(name), sourceDesc.Width, 0, sourceDesc.Format));

    D3D11_TEXTURE2D_DESC cubeDesc{};
    cubeDesc.Width = sourceDesc.Width;
    cubeDesc.Height = sourceDesc.Height;
    cubeDesc.MipLevels = generateMips ? 0u : 1u;
    cubeDesc.ArraySize = 6;
    cubeDesc.Format = sourceDesc.Format;
    cubeDesc.SampleDesc.Count = 1;
    cubeDesc.Usage = D3D11_USAGE_DEFAULT;
    cubeDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
    cubeDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;

    if (generateMips) {
        cubeDesc.BindFlags |= D3D11_BIND_RENDER_TARGET;
        cubeDesc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
    }

    if (FAILED(device->CreateTexture2D(
        &cubeDesc, nullptr, cube->m_texture.GetAddressOf()))) {
        return nullptr;
    }

    D3D11_TEXTURE2D_DESC actualDesc{};
    cube->m_texture->GetDesc(&actualDesc);
    cube->m_mipLevels = actualDesc.MipLevels;

    for (UINT face = 0; face < 6; ++face) {
        const UINT destination = D3D11CalcSubresource(0, face, cube->m_mipLevels);
        context->CopySubresourceRegion(
            cube->m_texture.Get(), destination, 0, 0, 0,
            faces[face]->Resource(), 0, nullptr);
    }

    if (!S_CreateCubeSRV(device, *cube)) return nullptr;
    if (generateMips) context->GenerateMips(cube->m_srv.Get());

    return cube;
}

//[ibl-add]
std::unique_ptr<TextureCube> TextureCube::LoadFromFiles(ID3D11Device *device, ID3D11DeviceContext *context,
                                                        std::string name, const FacePaths &facePaths, bool useSrgbView, bool generateMips) {
    if (!device || !context) return nullptr;

    WicFactoryScope wic;
    if (!wic.Initialize()) {
        WriteTextureCubeLoadError({}, L"初始化 WIC/COM 失败");
        return nullptr;
    }

    std::array<WicImageData, 6> decodedFaces{};
    for (std::size_t faceIndex = 0; faceIndex < decodedFaces.size(); ++faceIndex) {
        if (!LoadWicRgba8(wic.Get(), facePaths[faceIndex], decodedFaces[faceIndex])) {
            WriteTextureCubeLoadError(facePaths[faceIndex], L"WIC 无法解码该图片");
            return nullptr;
        }
    }

    const UINT faceSize = decodedFaces[0].width;
    if (faceSize == 0 || decodedFaces[0].height != faceSize) {
        WriteTextureCubeLoadError(facePaths[0], L"Cube 面必须为正方形");
        return nullptr;
    }

    for (std::size_t faceIndex = 1; faceIndex < decodedFaces.size(); ++faceIndex) {
        if (decodedFaces[faceIndex].width != faceSize ||
            decodedFaces[faceIndex].height != faceSize) {
            WriteTextureCubeLoadError(facePaths[faceIndex], L"六张图片的尺寸必须完全一致");
            return nullptr;
        }
    }

    const DXGI_FORMAT format = useSrgbView
                                   ? DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
                                   : DXGI_FORMAT_R8G8B8A8_UNORM;

    std::array<SubresourceData, 6> faces{};
    for (std::size_t faceIndex = 0; faceIndex < faces.size(); ++faceIndex) {
        faces[faceIndex].data = decodedFaces[faceIndex].pixels.data();
        faces[faceIndex].rowPitch = decodedFaces[faceIndex].rowPitch;
        faces[faceIndex].slicePitch = decodedFaces[faceIndex].slicePitch;
    }

    if (generateMips) {
        auto cube = S_CreateWithGeneratedMips(
            device, context, std::move(name), faceSize, format, faces);
        if (!cube) {
            WriteTextureCubeLoadError({}, L"创建带 mip 链的 GPU TextureCube 失败");
        }
        return cube;
    }

    CreateDesc createDesc{};
    createDesc.name = std::move(name);
    createDesc.size = faceSize;
    createDesc.mipLevels = 1;
    createDesc.format = format;
    createDesc.subresources.reserve(6);
    for (const SubresourceData &face: faces) {
        createDesc.subresources.push_back(face);
    }

    auto cube = S_Create(device, createDesc);
    if (!cube) {
        WriteTextureCubeLoadError({}, L"创建 GPU TextureCube 失败");
    }
    return cube;
}

//[ibl-add]
std::unique_ptr<TextureCube> TextureCube::CreateRenderTarget(ID3D11Device *device, std::string name,
                                                             UINT size, UINT mipLevels, DXGI_FORMAT format) {
    if (!device || size == 0 || mipLevels == 0 || format == DXGI_FORMAT_UNKNOWN) {
        return nullptr;
    }

    // auto cube = std::unique_ptr<TextureCube>(
    //     new TextureCube(std::move(name), size, mipLevels, format));

    std::unique_ptr<TextureCube> cube{new TextureCube{std::move(name), size, mipLevels, format}};

    D3D11_TEXTURE2D_DESC textureDesc{};
    textureDesc.Width = size;
    textureDesc.Height = size;
    textureDesc.MipLevels = mipLevels;
    textureDesc.ArraySize = 6;
    textureDesc.Format = format;
    textureDesc.SampleDesc.Count = 1;
    textureDesc.Usage = D3D11_USAGE_DEFAULT;
    textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
    textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;

    if (FAILED(device->CreateTexture2D(
        &textureDesc, nullptr, cube->m_texture.GetAddressOf()))) {
        return nullptr;
    }

    if (!S_CreateCubeSRV(device, *cube)) return nullptr;
    return cube;

}

//=private
bool TextureCube::S_SupportsGeneratedMips(ID3D11Device *device, DXGI_FORMAT format) {
    if (!device) return false;

    UINT support = 0;
    if (FAILED(device->CheckFormatSupport(format, &support))) return false;

    constexpr UINT required = D3D11_FORMAT_SUPPORT_SHADER_SAMPLE |
                              D3D11_FORMAT_SUPPORT_RENDER_TARGET |
                              D3D11_FORMAT_SUPPORT_MIP_AUTOGEN;
    return (support & required) == required;
}

bool TextureCube::S_CreateCubeSRV(ID3D11Device *device, TextureCube &cube) {
    if (!device || !cube.m_texture || cube.m_mipLevels == 0) return false;

    D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc{};
    srvDesc.Format = cube.m_format;
    srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
    srvDesc.TextureCube.MostDetailedMip = 0;
    srvDesc.TextureCube.MipLevels = cube.m_mipLevels;

    return SUCCEEDED(device->CreateShaderResourceView(
        cube.m_texture.Get(), &srvDesc, cube.m_srv.ReleaseAndGetAddressOf()));
}

TextureCube::TextureCube(std::string name, UINT size, UINT mipLevels, DXGI_FORMAT format)
: m_name(std::move(name)), m_size(size), m_mipLevels(mipLevels), m_format(format) {}



//endregion class TextureCube

















//
