#include <gltexturend.hpp>

#include <functional>
#include <numeric>
#include <utility>

#include <globject.hpp>
#include <gltexture.hpp>

// NOLINTNEXTLINE
#define STR_EXCEPTION GLObject<>::Exception
#include <str.hpp>


/// Special member functions


template<std::size_t N>
inline GLTextureND<N>::GLTextureND(
    // NOLINTNEXTLINE
    std::string object_label,
    GLenum      target,
    GLenum      binding,
    GLenum      size_max_name,
    Size        size,
    GLenum      internal_format,
    GLenum      wrap,
    GLenum      min_filter,
    GLenum      mag_filter
)
:
    GLTexture(
        STR_JOIN(" ", it, it, {
            str_size_(size),
            std::move(object_label)
        }),
        target,
        binding,
        internal_format,
        wrap,
        min_filter,
        mag_filter
    ),
    size_{size}
{
    check_size_max_(size_max_name);
}


/// Core


template<std::size_t N>
std::size_t GLTextureND<N>::data_size_() const
{
    return std::accumulate(
        size_.begin(),
        size_.end(),
        std::size_t{1},
        std::multiplies<std::size_t>{}
    );
}


/// Check


template<std::size_t N>
void GLTextureND<N>::check_size_max_(GLenum size_max_name)
{
    auto static const size_max = integer(size_max_name);
    for (auto size : size_)
        if (size > size_max)
            STR_THROW(
                "Expected max size " << size_max         << ", " <<
                "got "               << str_size_(size_) << "."
            );
}


/// String


template<std::size_t N>
std::string GLTextureND<N>::str_size_(Size size)
{
    return STR("{" << STR_JOIN(", ", it, it, size) << "}");
}


/// Explicit template instantiation


template class GLTextureND<1>;
template class GLTextureND<2>;
template class GLTextureND<3>;
