#ifndef GLTEXTURE3D_HPP_
#define GLTEXTURE3D_HPP_


#include <string>
#include <utility>

#include <gltexturend.hpp>


/// Class

class GLTexture3D : public GLTextureND<3>
{
public:

    /// Special member functions

    explicit GLTexture3D(
        std::string object_label,
        Size        size,
        GLenum      internal_format = GL_RGBA8,
        GLenum      wrap            = GL_CLAMP_TO_EDGE,
        GLenum      min_filter      = GL_LINEAR,
        GLenum      mag_filter      = GL_LINEAR
    );

    /// Core

    GLTexture3D static const & empty();

private:

    /// Core

    void virtual data_(
        void const * data,
        GLenum       target,
        GLenum       format,
        GLenum       type
    ) override;
};


/// Special member functions

inline GLTexture3D::GLTexture3D(
    std::string object_label,
    Size        size,
    GLenum      internal_format,
    GLenum      wrap,
    GLenum      min_filter,
    GLenum      mag_filter
)
:
    GLTextureND(
        std::move(object_label),
        GL_TEXTURE_3D,
        GL_TEXTURE_BINDING_3D,
        GL_MAX_3D_TEXTURE_SIZE,
        size,
        internal_format,
        wrap,
        min_filter,
        mag_filter
    )
{
    try
    {
        glTexImage3D(
            target_, 0,
            (GLint)internal_format,
            size[0], size[1], size[2], 0,
            GL_RED, GL_UNSIGNED_BYTE, nullptr
        );
        check_error_(glGetError());
    }
    catch (...)
    {
        fail_action_("create");
    }
}

/// Core

inline GLTexture3D const & GLTexture3D::empty()
{
    return empty_<GLTexture3D>();
}

inline void GLTexture3D::data_(
    void const * data,
    GLenum       target,
    GLenum       format,
    GLenum       type
)
{
    glTexSubImage3D(
        target, 0,
        0, 0, 0,
        size()[0], size()[1], size()[2],
        format, type, data
    );
}


#endif // GLTEXTURE3D_HPP_
