#pragma once

#include "graphics/opengl/texture.hpp"

#include <glad/gl.h>

#include <glm/vec2.hpp>

#include <array>
#include <cstdint>
#include <memory>

namespace kuiper
{

namespace gl
{

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

        // OpenGL mandates at least 8 colour buffers
        static constexpr std::uint32_t max_colour_attachments = 8;
        using col_tex_storage                                 = std::array<texture::s_ptr, max_colour_attachments>;

        explicit framebuffer(GLuint id)
            : m_id(id) {}
        ~framebuffer() {
            destroy();
        }

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

        static s_ptr make() noexcept;
        static s_ptr make_default(std::uint32_t width, std::uint32_t height) noexcept;

      public:
        void destroy() noexcept;

        void bind() const noexcept;

        void resize(std::uint32_t width, std::uint32_t height) noexcept;

        GLuint id() const noexcept {
            return m_id;
        }

        const col_tex_storage& colour_tex() const noexcept {
            return m_colour_texs;
        }

        const texture::s_ptr& depth_tex() const noexcept {
            return m_depth_tex;
        }

        glm::u32vec2 dimensions() const noexcept {
            return {m_width, m_height};
        }

        // Attach a colour texture to the framebuffer, optionally at a given attachment point. Takes ownership of `tex`.
        void attach_colour(texture::s_ptr&& tex, std::uint32_t attachment_point = 0) noexcept;
        // Attach a depth AND stencil texture to the framebuffer. Takes ownership of `tex`.
        void attach_depth(texture::s_ptr&& tex) noexcept;

      private:
        GLuint          m_id {0};
        col_tex_storage m_colour_texs {};
        texture::s_ptr  m_depth_tex {nullptr};
        std::uint32_t   m_width {0};
        std::uint32_t   m_height {0};
    };

} // namespace gl

} // namespace kuiper
