#pragma once
#include <d3d11.h>
#include <spdlog/spdlog.h>

#include "dx_renderer.h"
#include "core/renderer/renderer_buffer.h"

class dx_buffer : public renderer_buffer {
public:
    explicit dx_buffer(buffer_type in_type, int32_t in_element_byte, int32_t in_size): renderer_buffer(in_type, in_element_byte, in_size) {
        const auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
        int32_t byte_size = in_element_byte * in_size;
        // 对齐到16字节
        byte_size = (byte_size + 15) & ~15;

        D3D11_BUFFER_DESC buffer_desc = {};
        buffer_desc.Usage = D3D11_USAGE_DYNAMIC;
        buffer_desc.ByteWidth = byte_size;
        buffer_desc.BindFlags = get_dx_buffer_type();
        buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

        auto hr = d3d_device->CreateBuffer(&buffer_desc, nullptr, &buffer);
        if (FAILED(hr)) {
            spdlog::critical("无法创建缓冲区: {0}", hr);
        }
    }

    ~dx_buffer() override { if (buffer) { buffer->Release(); } }

    void* lock() override {
        const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
        D3D11_MAPPED_SUBRESOURCE mapped_resource;
        HRESULT hr = d3d_context->Map(buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource);
        if (SUCCEEDED(hr)) { return mapped_resource.pData; }
        return {};
    }
    void unlock() override {
        const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
        d3d_context->Unmap(buffer, 0);
    }

    [[nodiscard]] unsigned int get_dx_buffer_type() const {
        switch (this->get_type()) {
        case buffer_type::vertex:
            return D3D11_BIND_VERTEX_BUFFER;
        case buffer_type::index:
            return D3D11_BIND_INDEX_BUFFER;
        case buffer_type::constant:
            return D3D11_BIND_CONSTANT_BUFFER;
        default:
            return 0;
        }
    }
    [[nodiscard]] void* get_native_handle() override {
        return buffer;
    }
protected:
    void on_resize(int32_t new_count) override {
        const auto d3d_device = aorii::get_renderer<dx_renderer>()->get_d3d_device();
        const auto d3d_context = aorii::get_renderer<dx_renderer>()->get_d3d_context();
        // 获取原始缓冲区的描述
        D3D11_BUFFER_DESC old_desc = {};
        if (buffer) {
            buffer->GetDesc(&old_desc);
        }
        int32_t byte_size = element_byte * new_count;
        // 对齐到16字节
        byte_size = (byte_size + 15) & ~15;

        D3D11_BUFFER_DESC buffer_desc = old_desc;
        buffer_desc.ByteWidth = byte_size;
        ID3D11Buffer* new_buffer = nullptr;
        auto hr = d3d_device->CreateBuffer(&buffer_desc, nullptr, &new_buffer);
        if (FAILED(hr)) {
            spdlog::critical("无法创建缓冲区: {0}", hr);
            return;
        }
        if (new_buffer && buffer) {
            // 计算要复制的实际大小（取原始大小和新大小的较小值）
            const auto copy_size = std::min(old_desc.ByteWidth, buffer_desc.ByteWidth);
            // 复制旧数据到新缓冲区
            D3D11_BOX source_region;
            source_region.left = 0;
            source_region.right = copy_size;
            source_region.top = 0;
            source_region.bottom = 1;
            source_region.front = 0;
            source_region.back = 1;
            d3d_context->CopySubresourceRegion(new_buffer, 0, 0, 0, 0, buffer, 0, &source_region);
        }
        if (buffer)
            buffer->Release();
        buffer = new_buffer;
    }
private:
    ID3D11Buffer* buffer = nullptr;
};
