#pragma once

#include <glad/gl.h>

#include <cstddef>
#include <memory>

namespace kuiper
{

namespace gl
{

    class buffer {
      public:
        using s_ptr = std::shared_ptr<buffer>;

        explicit buffer(GLuint id, GLenum target)
            : m_id(id), m_target(target) {}
        ~buffer() {
            destroy();
        }

        buffer(const buffer& other)                = delete;
        buffer& operator=(const buffer& other)     = delete;
        buffer(buffer&& other) noexcept            = default;
        buffer& operator=(buffer&& other) noexcept = default;

        static s_ptr make(GLenum target) noexcept;

      public:
        GLuint id() const noexcept {
            return m_id;
        }

        std::size_t size() const noexcept {
            return m_size;
        }

        void destroy() noexcept;
        void bind() const noexcept;
        void bind_to_location(std::uint32_t location) const noexcept;
        void upload(GLenum usage, std::size_t size, const void* data) noexcept;
        void update(std::size_t offset, std::size_t size, const void* data) noexcept;
        void set_vertex_attrib(std::uint32_t index,
                               std::size_t   size,
                               GLenum        type,
                               std::size_t   stride,
                               std::size_t   offset,
                               bool          normalised = false) noexcept;

      private:
        GLuint      m_id {0};
        GLenum      m_target {0};
        std::size_t m_size {0};
    };

} // namespace gl

} // namespace kuiper
