// SPDX-License-Identifier: Unlicense

#ifndef OPENGL_H
#define OPENGL_H

#include "configuration.h"

#define GL_MULTISAMPLE_EXT                0x809D

#ifdef __ANDROID__
#include <GLES3/gl32.h> // 3.2 because of ASTC texture support
#else // __ANDROID__

#ifdef PLATFORM_EMSCRIPTEN
#include <GLES3/gl3.h> // webgl 2
#else // PLATFORM_EMSCRIPTEN

#ifdef PLATFORM_WINDOWS
#include <Windows.h>
#undef min
#undef max
#endif // PLATFORM_WINDOWS

#include <GL/gl.h>

#include "glext.h"

namespace OpenGl
{
#ifndef SHR3D_WINDOW_SDL
  void initWin32();
#endif // SHR3D_WINDOW_SDL

  void init();
}

#ifdef SHR3D_WINDOW_WIN32
BOOL wglChoosePixelFormatARB(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
HGLRC wglCreateContextAttribsARB(HDC hdc, HGLRC hShareContext, const int* attribList);
BOOL wglSwapIntervalEXT(int interval);
#endif // SHR3D_WINDOW_WIN32

void glUseProgram(GLuint program);
void glGenVertexArrays(GLsizei n, GLuint* arrays);
void glBindVertexArray(GLuint array);
void glGenBuffers(GLsizei n, GLuint* buffers);
void glBindBuffer(GLenum target, GLuint buffer);
void glDetachShader(GLuint program, GLuint shader);
void glDeleteProgram(GLuint program);
void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage);
GLint glGetUniformLocation(GLuint program, const GLchar* name);
void glUniform1f(GLint location, GLfloat v0);
void glUniform1fv(GLint location, GLsizei count, const GLfloat* value);
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void glUniform4fv(GLint location, GLsizei count, const GLfloat* value);
void glUniform1i(GLint location, GLint v0);
void glUniform1ui(GLint location, GLuint v0);
GLint glGetAttribLocation(GLuint program, const GLchar* name);
void glEnableVertexAttribArray(GLuint index);
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
void glGenerateMipmap(GLenum target);
GLuint glCreateShader(GLenum shaderType);
void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length);
void glCompileShader(GLuint shader);
GLuint glCreateProgram();
void glAttachShader(GLuint program, GLuint shader);
void glLinkProgram(GLuint program);
void glGetProgramiv(GLuint program, GLenum pname, GLint* params);
void glGetShaderiv(GLuint shader, GLenum pname, GLint* params);
void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog);
void glDeleteShader(GLuint shader);
void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void glActiveTexture(GLenum texture);
void glGenFramebuffers(GLsizei n, GLuint* ids);
void glBindFramebuffer(GLenum target, GLuint framebuffer);
void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
GLenum glCheckFramebufferStatus(GLenum target);
void glGenRenderbuffers(GLsizei n, GLuint* renderbuffers);
void glBindRenderbuffer(GLenum target, GLuint renderbuffer);
void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
void glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
void glDrawBuffers(GLsizei n, const GLenum* bufs);
GLboolean glUnmapBuffer(GLenum target);
void glBlendEquation(GLenum mode);
void glDeleteBuffers(GLsizei n, const GLuint* buffers);
void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data);
//void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data);
void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount);
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data);
void glBindSampler(GLuint unit, GLuint sampler);
void glVertexAttribDivisor(GLuint index, GLuint divisor);
void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount);
void glDisableVertexAttribArray(GLuint index);
void glDrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, void* indices, GLint basevertex);
void glDrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, void* indices, GLsizei primcount, GLint basevertex);
#ifdef SHR3D_OPENGL_SPIR_V
void glShaderBinary(GLsizei count, const GLuint* shaders, GLenum binaryFormat, const void* binary, GLsizei length);
void glSpecializeShader(GLuint shader, const GLchar* pEntryPoint, GLuint numSpecializationConstants, const GLuint* pConstantIndex, const GLuint* pConstantValue);
#endif // SHR3D_OPENGL_SPIR_V
#ifdef SHR3D_ENVIRONMENT_MILK
void glGenSamplers(GLsizei n, GLuint* samplers);
void glSamplerParameteri(GLuint sampler, GLenum pname, GLint param);
void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void glVertexAttrib4f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
void glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* data);
void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei* length, GLchar* infoLog);
void glBindVertexArray_Milk_WaveForm_IgnoreGlError(GLuint array);
#endif // SHR3D_ENVIRONMENT_MILK

#endif // PLATFORM_EMSCRIPTEN

#endif // __ANDROID__


#ifdef SHR3D_OPENGL_ERROR_CHECK
void glErrorCheck();
#define GL(func) do { func; glErrorCheck(); } while(0)
#else
#define GL(func) func
#endif // SHR3D_OPENGL_ERROR_CHECK


//#ifndef SHR3D_OPENGL_DRAW_NON_INDEXED
//#ifndef SHR3D_OPENGL_DRAW_ZERO_BASED
#define glDrawElementsBaseVertex_compat(name) glDrawElements(GL_TRIANGLES, to_underlying_(Geometry::Indicies::name), GL_UNSIGNED_INT, (void*)(to_underlying_(Geometry::IndicesOffset::name) * sizeof(GLuint)));
#define glDrawElementsInstancedBaseVertex_compat(name, instancecount) glDrawElementsInstanced(GL_TRIANGLES, to_underlying_(Geometry::Indicies::name), GL_UNSIGNED_INT, (void*)(to_underlying_(Geometry::IndicesOffset::name) * sizeof(GLuint)), instancecount);
//#else // SHR3D_OPENGL_DRAW_ZERO_BASED
//#define glDrawElementsBaseVertex_compat(name) glDrawElementsBaseVertex(GL_TRIANGLES, to_underlying_(Geometry::Indicies::name), GL_UNSIGNED_INT, (void*)(to_underlying_(Geometry::IndicesOffset::name) * sizeof(GLuint)), to_underlying_(Geometry::BaseVertex::name));
//#define glDrawElementsInstancedBaseVertex_compat(name, instancecount) glDrawElementsInstancedBaseVertex(GL_TRIANGLES, to_underlying_(Geometry::Indicies::name), GL_UNSIGNED_INT, (void*)(to_underlying_(Geometry::IndicesOffset::name) * sizeof(GLuint)), instancecount, to_underlying_(Geometry::BaseVertex::name));
//#endif // SHR3D_OPENGL_DRAW_ZERO_BASED
//#else // SHR3D_OPENGL_DRAW_NON_INDEXED
//#define glDrawElementsBaseVertex_compat(name) glDrawArrays(GL_TRIANGLES, to_underlying_(Geometry::VerticesOffset::name), to_underlying_(Geometry::Vertices::name));
//#define glDrawElementsInstancedBaseVertex_compat(name, instancecount) glDrawArraysInstanced(GL_TRIANGLES, to_underlying_(Geometry::VerticesOffset::name), to_underlying_(Geometry::Vertices::name), instancecount);
//#endif // SHR3D_OPENGL_DRAW_NON_INDEXED


#endif // OPENGL_H
