#pragma once
#include "WickedEngine.h"

namespace voxel {

// ── Deferred GPU Buffer ─────────────────────────────────────────
// Encapsulates the repeated pattern of:
//   1. CPU staging data prepared during Update()
//   2. GPU buffer with capacity-based growth (25% headroom)
//   3. Dirty flag for deferred upload in Render()
//
// Eliminates ~50 lines of boilerplate per buffer and centralizes
// the invariants (capacity >= count, CreateBuffer with nullptr,
// UpdateBuffer with actual data size).

struct DeferredGPUBuffer {
    wi::graphics::GPUBuffer gpu;
    mutable uint32_t capacity = 0;  // in elements
    mutable bool dirty = false;
    uint32_t stride = 0;            // bytes per element

    // Ensure GPU buffer has enough capacity for elementCount elements.
    // Creates/recreates buffer only when capacity is insufficient.
    // Returns true if buffer was (re)created.
    bool ensureCapacity(wi::graphics::GraphicsDevice* device,
                        uint32_t elementCount,
                        uint32_t elementStride,
                        wi::graphics::BindFlag bindFlags,
                        wi::graphics::ResourceMiscFlag miscFlags = wi::graphics::ResourceMiscFlag::BUFFER_STRUCTURED)
    {
        stride = elementStride;
        if (gpu.IsValid() && capacity >= elementCount) return false;

        capacity = elementCount + elementCount / 4; // 25% headroom
        wi::graphics::GPUBufferDesc desc;
        desc.size = (uint64_t)capacity * stride;
        desc.bind_flags = bindFlags;
        desc.misc_flags = miscFlags;
        desc.stride = (miscFlags == wi::graphics::ResourceMiscFlag::BUFFER_STRUCTURED) ? stride : 0;
        desc.usage = wi::graphics::Usage::DEFAULT;
        device->CreateBuffer(&desc, nullptr, &gpu);
        dirty = true;
        return true;
    }

    // Upload data to GPU. Call from Render() with a valid CommandList.
    // dataCount = number of elements to upload (may be < capacity).
    void upload(wi::graphics::GraphicsDevice* device,
                wi::graphics::CommandList cmd,
                const void* data,
                uint32_t dataCount) const
    {
        if (!dirty || !gpu.IsValid() || dataCount == 0 || !data) return;
        size_t uploadSize = (size_t)dataCount * stride;
        size_t bufferSize = (size_t)capacity * stride;
        if (uploadSize <= bufferSize) {
            device->UpdateBuffer(&gpu, data, cmd, uploadSize);
        }
        dirty = false;
    }

    // Mark as needing upload (call after staging data changes).
    void markDirty() { dirty = true; }

    bool isValid() const { return gpu.IsValid(); }
};

} // namespace voxel
