// Copyright (C) 2023 Vitaliy Lobachevskiy
// Copyright (C) 2015 Patryk Nadrowski
// Copyright (C) 2009-2010 Amundis
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in Irrlicht.h

#pragma once

#include <unordered_set>

#include "EDriverFeatures.h"
#include "irrTypes.h"
#include "os.h"

#include "Common.h"
#include <mt_opengl.h> // must be after Common.h

#include "COGLESCoreExtensionHandler.h"

namespace irr
{
namespace video
{

	class COpenGL3ExtensionHandler : public COGLESCoreExtensionHandler
	{
	public:
		COpenGL3ExtensionHandler() : COGLESCoreExtensionHandler() {}

		void initExtensionsOld();
		void initExtensionsNew();

		/// Checks whether a named extension is present
		bool queryExtension(const std::string &name) const noexcept;

		bool queryFeature(video::E_VIDEO_DRIVER_FEATURE feature) const
		{
			switch (feature)
			{
			case EVDF_RENDER_TO_TARGET:
			case EVDF_HARDWARE_TL:
			case EVDF_MULTITEXTURE:
			case EVDF_BILINEAR_FILTER:
			case EVDF_MIP_MAP:
			case EVDF_MIP_MAP_AUTO_UPDATE:
			case EVDF_VERTEX_SHADER_1_1:
			case EVDF_PIXEL_SHADER_1_1:
			case EVDF_PIXEL_SHADER_1_2:
			case EVDF_PIXEL_SHADER_2_0:
			case EVDF_VERTEX_SHADER_2_0:
			case EVDF_ARB_GLSL:
			case EVDF_TEXTURE_NSQUARE:
			case EVDF_TEXTURE_NPOT:
			case EVDF_FRAMEBUFFER_OBJECT:
			case EVDF_VERTEX_BUFFER_OBJECT:
			case EVDF_COLOR_MASK:
			case EVDF_ALPHA_TO_COVERAGE:
			case EVDF_POLYGON_OFFSET:
			case EVDF_BLEND_OPERATIONS:
			case EVDF_BLEND_SEPARATE:
			case EVDF_TEXTURE_MATRIX:
			case EVDF_TEXTURE_CUBEMAP:
				return true;
			case EVDF_ARB_VERTEX_PROGRAM_1:
			case EVDF_ARB_FRAGMENT_PROGRAM_1:
			case EVDF_GEOMETRY_SHADER:
			case EVDF_MULTIPLE_RENDER_TARGETS:
			case EVDF_MRT_BLEND:
			case EVDF_MRT_COLOR_MASK:
			case EVDF_MRT_BLEND_FUNC:
			case EVDF_OCCLUSION_QUERY:
				return false;
			case EVDF_STENCIL_BUFFER:
				return StencilBuffer;
			default:
				return false;
			};
		}

		static GLint GetInteger(GLenum key) {
			GLint val = 0;
			GL.GetIntegerv(key, &val);
			return val;
		};

		inline void irrGlActiveTexture(GLenum texture)
		{
			GL.ActiveTexture(texture);
		}

		inline void irrGlCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border,
			GLsizei imageSize, const void* data)
		{
			os::Printer::log("Compressed textures aren't supported", ELL_ERROR);
		}

		inline void irrGlCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height,
			GLenum format, GLsizei imageSize, const void* data)
		{
			os::Printer::log("Compressed textures aren't supported", ELL_ERROR);
		}

		inline void irrGlUseProgram(GLuint prog)
		{
			GL.UseProgram(prog);
		}

		inline void irrGlBindFramebuffer(GLenum target, GLuint framebuffer)
		{
			GL.BindFramebuffer(target, framebuffer);
		}

		inline void irrGlDeleteFramebuffers(GLsizei n, const GLuint *framebuffers)
		{
			GL.DeleteFramebuffers(n, framebuffers);
		}

		inline void irrGlGenFramebuffers(GLsizei n, GLuint *framebuffers)
		{
			GL.GenFramebuffers(n, framebuffers);
		}

		inline GLenum irrGlCheckFramebufferStatus(GLenum target)
		{
			return GL.CheckFramebufferStatus(target);
		}

		inline void irrGlFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
		{
			GL.FramebufferTexture2D(target, attachment, textarget, texture, level);
		}

		inline void irrGlGenerateMipmap(GLenum target)
		{
			GL.GenerateMipmap(target);
		}

		inline void irrGlDrawBuffer(GLenum mode)
		{
			GL.DrawBuffer(mode);
		}

		inline void irrGlDrawBuffers(GLsizei n, const GLenum *bufs)
		{
			GL.DrawBuffers(n, bufs);
		}

		inline void irrGlBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)
		{
			GL.BlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
		}

		inline void irrGlBlendEquation(GLenum mode)
		{
			GL.BlendEquation(mode);
		}

		bool AnisotropicFilterSupported = false;
		bool BlendMinMaxSupported = false;

	private:
		void addExtension(std::string name);
		void updateLegacyExtensionList();

		std::unordered_set<std::string> Extensions;
	};

}
}
