#include <psemek/gfx/texture.hpp>
#include <optional>

namespace psemek::gfx
{

#ifndef PSEMEK_GLES
	template struct basic_texture<1, gl::TEXTURE_1D>;
	template struct basic_texture<2, gl::TEXTURE_1D_ARRAY>;
#endif
	template struct basic_texture<2, gl::TEXTURE_2D>;
	template struct basic_texture<3, gl::TEXTURE_2D_ARRAY>;
	template struct basic_texture<3, gl::TEXTURE_3D>;

	template struct basic_texture<2, gl::TEXTURE_CUBE_MAP>;

	namespace detail
	{

		static std::optional<float> get_max_anisotropy()
		{
			if (!gl::sys::ext_ARB_texture_filter_anisotropic()) return std::nullopt;

			float level;
			gl::GetFloatv(gl::MAX_TEXTURE_MAX_ANISOTROPY, &level);
			return level;
			return 0.f;
		}

		std::optional<float> max_anisotropy()
		{
			static std::optional<float> level = get_max_anisotropy();

			return level;
		}

	}

	void texture_cubemap::load(int f, GLint internal_format, math::vector<std::size_t, 2> const & size, GLenum format, GLenum type, const void * data)
	{
		bind();
		gl::TexImage2D(face_to_gl(f), 0, internal_format, size[0], size[1], 0, format, type, data);
		size_ = size;
	}

#ifndef PSEMEK_GLES
	void texture_cubemap::pixels(int f, GLenum format, GLenum type, void * data) const
	{
		bind();
		gl::GetTexImage(face_to_gl(f), 0, format, type, data);
	}
#endif

	GLenum texture_cubemap::face_to_gl(int f)
	{
		return gl::TEXTURE_CUBE_MAP_POSITIVE_X + f;
	}

#ifndef PSEMEK_GLES

	texture_2d_multisample::texture_2d_multisample()
	{
		gl::GenTextures(1, &id_);
	}

	texture_2d_multisample::texture_2d_multisample(texture_2d_multisample && other)
		: id_(other.id_)
		, size_(other.size_)
		, samples_(other.samples_)
	{
		other.id_ = 0;
		other.size_ = {0, 0};
		other.samples_ = 0;
	}

	texture_2d_multisample & texture_2d_multisample::operator = (texture_2d_multisample && other)
	{
		if (this != &other)
		{
			reset();
			id_ = other.id_;
			size_ = other.size_;
			samples_ = other.samples_;
			other.id_ = 0;
			other.size_ = {0, 0};
			other.samples_ = 0;
		}

		return *this;
	}

	texture_2d_multisample::~texture_2d_multisample()
	{
		reset();
	}

	texture_2d_multisample texture_2d_multisample::null()
	{
		return texture_2d_multisample(nullptr);
	}

	void texture_2d_multisample::reset()
	{
		gl::DeleteTextures(1, &id_);
	}

	void texture_2d_multisample::bind() const
	{
		gl::BindTexture(target, id_);
	}

	void texture_2d_multisample::bind(int texture_unit) const
	{
		gl::ActiveTexture(gl::TEXTURE0 + texture_unit);
		bind();
	}

	void texture_2d_multisample::load(GLint internal_format, math::vector<std::size_t, 2> const & size, int samples, bool fixed_sample_locations)
	{
		bind();
		gl::TexImage2DMultisample(target, samples, internal_format, size[0], size[1], fixed_sample_locations ? gl::TRUE : gl::FALSE);
		size_ = size;
		samples_ = samples;
	}

	texture_2d_multisample::texture_2d_multisample(std::nullptr_t)
	{}

#endif

}
