#pragma once

#include <glad/gl.h>

#include <glm/vec3.hpp>

#include <cstddef>
#include <cstdint>
#include <memory>

namespace kuiper
{

namespace gl
{

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

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

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

        static s_ptr make(GLenum target) noexcept;

      public:
        void destroy() noexcept;
        void bind(std::uint32_t unit = 0) const noexcept;
        void set_storage(GLenum internal_fmt, std::uint32_t width, std::uint32_t height, std::uint32_t depth) noexcept;
        void upload(std::uint32_t x_offset,
                    std::uint32_t y_offset,
                    std::uint32_t z_offset,
                    std::uint32_t width,
                    std::uint32_t height,
                    std::uint32_t depth,
                    GLenum        format,
                    GLenum        type,
                    const void*   data,
                    std::uint32_t mip_level = 0) noexcept;
        void set_param(GLenum name, GLint param) noexcept;
        void gen_mipmaps() noexcept;

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

        GLuint id() const noexcept {
            return m_id;
        }

        GLenum internal_format() const noexcept {
            return m_internal_fmt;
        }

      private:
        GLuint        m_id {0};
        GLenum        m_target {0};
        GLenum        m_internal_fmt {0};
        std::uint32_t m_width {0};
        std::uint32_t m_height {0};
        std::uint32_t m_depth {0};
    };

} // namespace gl

} // namespace kuiper
