#include <luna/Buffer.h>
#include <luna/CString.h>
#include <luna/Heap.h>

Buffer::Buffer()
{
}

Buffer::Buffer(u8* data, usize size) : m_data(data), m_size(size)
{
}

Buffer::Buffer(Buffer&& other) : m_data(other.data()), m_size(other.size())
{
    other.m_data = nullptr;
}

Buffer& Buffer::operator=(Buffer&& other)
{
    if (&other == this) return *this;
    if (m_data) free_impl(m_data);
    m_data = other.m_data;
    m_size = other.m_size;
    other.m_data = nullptr;
    return *this;
}

Buffer::~Buffer()
{
    if (m_data) free_impl(m_data);
}

Result<Buffer> Buffer::create_sized(usize size)
{
    u8* data = (u8*)TRY(malloc_impl(size));
    return Buffer { data, size };
}

Result<void> Buffer::try_resize(usize new_size)
{
    m_data = (u8*)TRY(realloc_impl(m_data, new_size));
    m_size = new_size;
    return {};
}

Result<u8*> Buffer::slice_at_end(usize size)
{
    usize old_size = m_size;
    TRY(try_resize(m_size + size));
    return m_data + old_size;
}

Result<u8*> Buffer::slice(usize offset, usize size)
{
    if (offset + size > m_size) { TRY(try_resize(offset + size)); }
    return m_data + offset;
}

Result<void> Buffer::append_data(const u8* data, usize size)
{
    memcpy(TRY(slice_at_end(size)), data, size);

    return {};
}

usize Buffer::dequeue_data(u8* data, usize size)
{
    if (size > m_size) size = m_size;
    if (!size) return 0;

    memcpy(data, m_data, size);

    memmove(m_data, m_data + size, m_size - size);

    m_size -= size;

    return size;
}

u8* Buffer::release_data()
{
    u8* data = m_data;
    m_data = nullptr;
    return data;
}