#include <Vk/Buffer/Buffer.hpp>
#include <Utilities/Utilities.hpp>

Vk::Buffer::Buffer(
    std::shared_ptr<Vk::Device> device,
    size_t size,
    VkBufferUsageFlags usage,
    VkMemoryPropertyFlags memoryProps,
    VkSharingMode sharingMode
) : m_device(device), m_size(size), m_hostdata(nullptr)
{
    VkBufferCreateInfo bufferInfo{};
    bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    bufferInfo.size = size;
    bufferInfo.usage = usage;
    bufferInfo.sharingMode = sharingMode;

    VmaAllocationCreateInfo allocInfo = {};
    allocInfo.usage = VMA_MEMORY_USAGE_UNKNOWN;
    allocInfo.requiredFlags = memoryProps;

    if (vmaCreateBufferWithAlignment(
        device->getAllocator(),
        &bufferInfo,
        &allocInfo,
        16,
        &m_buffer,
        &m_allocation,
        nullptr
    ) != VK_SUCCESS)
    {
        crash("Failed to create a buffer!");
    }

    if (memoryProps & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
    {
        vmaMapMemory(device->getAllocator(), m_allocation, &m_hostdata);
    }
}

Vk::Buffer::~Buffer()
{
    if (m_hostdata != nullptr)
    {
        vmaUnmapMemory(m_device->getAllocator(), m_allocation);
    }

    vmaDestroyBuffer(m_device->getAllocator(), m_buffer, m_allocation);
}

VkBuffer Vk::Buffer::inner()
{
    return m_buffer;
}

void* Vk::Buffer::hostdata()
{
    if (m_hostdata == nullptr)
    {
        crash("hostdata() called on a non-host-visible buffer!");
    }

    return m_hostdata;
}

void Vk::Buffer::flush(VkDeviceSize offset, VkDeviceSize size)
{
    vmaFlushAllocation(m_device->getAllocator(), m_allocation, offset, size);
}

size_t Vk::Buffer::getSize()
{
    return m_size;
}