#pragma once

#include "graphics/opengl/framebuffer.hpp"

#include <glad/gl.h>
#include <GLFW/glfw3.h>

#include <cstdint>
#include <type_traits>

namespace kuiper
{

namespace gl
{

    void initialise();

    void dump_hardware_info();

#if !defined(NDEBUG)
    void debug_callback(GLenum        source,
                        GLenum        type,
                        GLuint        id,
                        GLenum        severity,
                        GLsizei       length,
                        const GLchar* message,
                        const void*   userParam) noexcept;
#endif

    class render_context {
      public:
        /// Binds a framebuffer to the renderer pipeline
        static void bind_framebuffer(const framebuffer::s_ptr& fb) noexcept;

        /// Binds the default framebuffer
        static void bind_default_framebuffer() noexcept;

        /// Set the viewport size over the currently bound framebuffer
        static void set_viewport_size(std::uint32_t width, std::uint32_t height) noexcept;

        /// Enables an OpenGL feature
        static void enable_feature(GLenum feature) noexcept;

        /// Disables an OpenGL feature
        static void disable_feature(GLenum feature) noexcept;

        template<typename T>
        static T get(GLenum param) noexcept {
            T ret {};

            if constexpr (std::is_same<T, GLint>::value) {
                glGetIntegerv(param, &ret);
            } else if constexpr (std::is_same<T, GLint64>::value) {
                glGetInteger64v(param, &ret);
            } else if constexpr (std::is_same<T, GLfloat>::value) {
                glGetFloatv(param, &ret);
            } else if constexpr (std::is_same<T, GLdouble>::value) {
                glGetDoublev(param, &ret);
            } else if constexpr (std::is_same<T, GLboolean>::value) {
                glGetBooleanv(param, &ret);
            } else {
                static_assert(!"non-exhaustive if-constexpr!");
            }

            return ret;
        }

        static constexpr const char* GLSL_VERSION = "#version 460 core";
    };

} // namespace gl

} // namespace kuiper