// SPDX-License-Identifier: Unlicense

#include "opengl.h"
#include "typedefs.h"

#if not defined(PLATFORM_OPENXR_ANDROID) && not defined(PLATFORM_EMSCRIPTEN) && not defined(PLATFORM_ANDROID_SDL)

#ifdef SHR3D_WINDOW_SDL
#include <SDL_video.h>
#define GL_GetProcAddress SDL_GL_GetProcAddress
#endif // SHR3D_WINDOW_SDL
#ifdef SHR3D_WINDOW_WIN32
#define GL_GetProcAddress wglGetProcAddress
#endif // SHR3D_WINDOW_WIN32
#ifdef SHR3D_WINDOW_X11
//#include <GL/gl.h>
#include <GL/glx.h>
//#include <X11/Xlib.h>
#define GL_GetProcAddress(procName) glXGetProcAddress(reinterpret_cast<const GLubyte*>(procName))
#endif // SHR3D_WINDOW_X11


#ifdef SHR3D_WINDOW_WIN32
typedef HGLRC WINAPI wglCreateContextAttribsARB_type(HDC hdc, HGLRC hShareContext, const int* attribList);
static wglCreateContextAttribsARB_type* wglCreateContextAttribsARBProc;
typedef BOOL WINAPI wglChoosePixelFormatARB_type(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats);
static wglChoosePixelFormatARB_type* wglChoosePixelFormatARBProc;
typedef BOOL WINAPI wglSwapIntervalEXT_type(int interval);
static wglSwapIntervalEXT_type* wglSwapIntervalEXTProc;
#endif // SHR3D_WINDOW_WIN32

static PFNGLCREATESHADERPROC glCreateShaderProc;
static PFNGLSHADERSOURCEPROC glShaderSourceProc;
static PFNGLCOMPILESHADERPROC glCompileShaderProc;
static PFNGLGETSHADERIVPROC glGetShaderivProc;
static PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLogProc;
static PFNGLDELETESHADERPROC glDeleteShaderProc;
static PFNGLATTACHSHADERPROC glAttachShaderProc;
static PFNGLCREATEPROGRAMPROC glCreateProgramProc;
static PFNGLLINKPROGRAMPROC glLinkProgramProc;
static PFNGLVALIDATEPROGRAMPROC glValidateProgramProc;
static PFNGLGETPROGRAMIVPROC glGetProgramivProc;
static PFNGLUSEPROGRAMPROC glUseProgramProc;
static PFNGLGENVERTEXARRAYSPROC glGenVertexArraysProc;
static PFNGLBINDVERTEXARRAYPROC glBindVertexArrayProc;
static PFNGLGENBUFFERSPROC glGenBuffersProc;
static PFNGLBINDBUFFERPROC glBindBufferProc;
static PFNGLDETACHSHADERPROC glDetachShaderProc;
static PFNGLDELETEPROGRAMPROC glDeleteProgramProc;
static PFNGLGETATTRIBLOCATIONPROC glGetAttribLocationProc;
static PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArrayProc;
static PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointerProc;
static PFNGLBUFFERDATAPROC glBufferDataProc;
static PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocationProc;
static PFNGLUNIFORM1FPROC glUniform1fProc;
static PFNGLUNIFORM1FVPROC glUniform1fvProc;
static PFNGLUNIFORM2FPROC glUniform2fProc;
static PFNGLUNIFORM3FPROC glUniform3fProc;
static PFNGLUNIFORM4FPROC glUniform4fProc;
static PFNGLUNIFORM4FVPROC glUniform4fvProc;
static PFNGLUNIFORM1IPROC glUniform1iProc;
static PFNGLUNIFORM1UIPROC glUniform1uiProc;
static PFNGLGENERATEMIPMAPPROC glGenerateMipmapProc;
static PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fvProc;
static PFNGLACTIVETEXTUREPROC glActiveTextureProc;
static PFNGLGENFRAMEBUFFERSPROC glGenFramebuffersProc;
static PFNGLBINDFRAMEBUFFERPROC glBindFramebufferProc;
static PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2DProc;
static PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatusProc;
static PFNGLGENRENDERBUFFERSPROC glGenRenderbuffersProc;
static PFNGLBINDRENDERBUFFERPROC glBindRenderbufferProc;
static PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorageProc;
static PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbufferProc;
static PFNGLDRAWBUFFERSPROC glDrawBuffersProc;
static PFNGLUNMAPBUFFERPROC glUnmapBufferProc;
static PFNGLBLENDEQUATIONPROC glBlendEquationProc;
static PFNGLDELETEBUFFERSPROC glDeleteBuffersProc;
static PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2DProc;
//static PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2DProc;
static PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstancedProc;
static PFNGLBUFFERSUBDATAPROC glBufferSubDataProc;
static PFNGLBINDSAMPLERPROC glBindSamplerProc;
static PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisorProc;
static PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstancedProc;
static PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArrayProc;
static PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertexProc;
static PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glDrawElementsInstancedBaseVertexProc;
#ifdef SHR3D_ENVIRONMENT_MILK
static PFNGLGENSAMPLERSPROC glGenSamplersProc;
static PFNGLSAMPLERPARAMETERIPROC glSamplerParameteriProc;
static PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fvProc;
static PFNGLVERTEXATTRIB4FPROC glVertexAttrib4fProc;
static PFNGLTEXIMAGE3DPROC glTexImage3DProc;
static PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLogProc;
#endif // SHR3D_ENVIRONMENT_MILK
#ifdef SHR3D_OPENGL_SPIR_V
typedef void(*PFNGLSHADERBINARYPROC)(GLsizei count, const GLuint* shaders, GLenum binaryFormat, const void* binary, GLsizei length);
static PFNGLSHADERBINARYPROC glShaderBinaryProc;
typedef void(*PFNGLSPECIALIZESHADERPROC)(GLuint shader, const GLchar* pEntryPoint, GLuint numSpecializationConstants, const GLuint* pConstantIndex, const GLuint* pConstantValue);
static PFNGLSPECIALIZESHADERPROC glSpecializeShaderProc;
#endif // SHR3D_OPENGL_SPIR_V

#ifdef SHR3D_WINDOW_WIN32
void OpenGl::initWin32()
{
  wglCreateContextAttribsARBProc = reinterpret_cast<wglCreateContextAttribsARB_type*>(GL_GetProcAddress("wglCreateContextAttribsARB"));
  wglChoosePixelFormatARBProc = reinterpret_cast<wglChoosePixelFormatARB_type*>(GL_GetProcAddress("wglChoosePixelFormatARB"));
  wglSwapIntervalEXTProc = reinterpret_cast<wglSwapIntervalEXT_type*>(GL_GetProcAddress("wglSwapIntervalEXT"));
}
#endif // SHR3D_WINDOW_WIN32

void OpenGl::init()
{
  glCreateShaderProc = reinterpret_cast<PFNGLCREATESHADERPROC>(GL_GetProcAddress("glCreateShader"));
  glShaderSourceProc = reinterpret_cast<PFNGLSHADERSOURCEPROC>(GL_GetProcAddress("glShaderSource"));
  glCompileShaderProc = reinterpret_cast<PFNGLCOMPILESHADERPROC>(GL_GetProcAddress("glCompileShader"));
  glGetShaderivProc = reinterpret_cast<PFNGLGETSHADERIVPROC>(GL_GetProcAddress("glGetShaderiv"));
  glGetShaderInfoLogProc = reinterpret_cast<PFNGLGETSHADERINFOLOGPROC>(GL_GetProcAddress("glGetShaderInfoLog"));
  glDeleteShaderProc = reinterpret_cast<PFNGLDELETESHADERPROC>(GL_GetProcAddress("glDeleteShader"));
  glAttachShaderProc = reinterpret_cast<PFNGLATTACHSHADERPROC>(GL_GetProcAddress("glAttachShader"));
  glCreateProgramProc = reinterpret_cast<PFNGLCREATEPROGRAMPROC>(GL_GetProcAddress("glCreateProgram"));
  glLinkProgramProc = reinterpret_cast<PFNGLLINKPROGRAMPROC>(GL_GetProcAddress("glLinkProgram"));
  glValidateProgramProc = reinterpret_cast<PFNGLVALIDATEPROGRAMPROC>(GL_GetProcAddress("glValidateProgram"));
  glGetProgramivProc = reinterpret_cast<PFNGLGETPROGRAMIVPROC>(GL_GetProcAddress("glGetProgramiv"));
  glUseProgramProc = reinterpret_cast<PFNGLUSEPROGRAMPROC>(GL_GetProcAddress("glUseProgram"));
  glGenVertexArraysProc = reinterpret_cast<PFNGLGENVERTEXARRAYSPROC>(GL_GetProcAddress("glGenVertexArrays"));
  glBindVertexArrayProc = reinterpret_cast<PFNGLBINDVERTEXARRAYPROC>(GL_GetProcAddress("glBindVertexArray"));
  glGenBuffersProc = reinterpret_cast<PFNGLGENBUFFERSPROC>(GL_GetProcAddress("glGenBuffers"));
  glBindBufferProc = reinterpret_cast<PFNGLBINDBUFFERPROC>(GL_GetProcAddress("glBindBuffer"));
  glDetachShaderProc = reinterpret_cast<PFNGLDETACHSHADERPROC>(GL_GetProcAddress("glDetachShader"));
  glDeleteProgramProc = reinterpret_cast<PFNGLDELETEPROGRAMPROC>(GL_GetProcAddress("glDeleteProgram"));
  glGetAttribLocationProc = reinterpret_cast<PFNGLGETATTRIBLOCATIONPROC>(GL_GetProcAddress("glGetAttribLocation"));
  glEnableVertexAttribArrayProc = reinterpret_cast<PFNGLENABLEVERTEXATTRIBARRAYPROC>(GL_GetProcAddress("glEnableVertexAttribArray"));
  glVertexAttribPointerProc = reinterpret_cast<PFNGLVERTEXATTRIBPOINTERPROC>(GL_GetProcAddress("glVertexAttribPointer"));
  glBufferDataProc = reinterpret_cast<PFNGLBUFFERDATAPROC>(GL_GetProcAddress("glBufferData"));
  glGetUniformLocationProc = reinterpret_cast<PFNGLGETUNIFORMLOCATIONPROC>(GL_GetProcAddress("glGetUniformLocation"));
  glUniform1fProc = reinterpret_cast<PFNGLUNIFORM1FPROC>(GL_GetProcAddress("glUniform1f"));
  glUniform1fvProc = reinterpret_cast<PFNGLUNIFORM1FVPROC>(GL_GetProcAddress("glUniform1fv"));
  glUniform2fProc = reinterpret_cast<PFNGLUNIFORM2FPROC>(GL_GetProcAddress("glUniform2f"));
  glUniform3fProc = reinterpret_cast<PFNGLUNIFORM3FPROC>(GL_GetProcAddress("glUniform3f"));
  glUniform4fProc = reinterpret_cast<PFNGLUNIFORM4FPROC>(GL_GetProcAddress("glUniform4f"));
  glUniform4fvProc = reinterpret_cast<PFNGLUNIFORM4FVPROC>(GL_GetProcAddress("glUniform4fv"));
  glUniform1iProc = reinterpret_cast<PFNGLUNIFORM1IPROC>(GL_GetProcAddress("glUniform1i"));
  glUniform1uiProc = reinterpret_cast<PFNGLUNIFORM1UIPROC>(GL_GetProcAddress("glUniform1ui"));
  glGenerateMipmapProc = reinterpret_cast<PFNGLGENERATEMIPMAPPROC>(GL_GetProcAddress("glGenerateMipmap"));
  glUniformMatrix4fvProc = reinterpret_cast<PFNGLUNIFORMMATRIX4FVPROC>(GL_GetProcAddress("glUniformMatrix4fv"));
  glActiveTextureProc = reinterpret_cast<PFNGLACTIVETEXTUREPROC>(GL_GetProcAddress("glActiveTexture"));
  glGenFramebuffersProc = reinterpret_cast<PFNGLGENFRAMEBUFFERSPROC>(GL_GetProcAddress("glGenFramebuffers"));
  glBindFramebufferProc = reinterpret_cast<PFNGLBINDFRAMEBUFFERPROC>(GL_GetProcAddress("glBindFramebuffer"));
  glFramebufferTexture2DProc = reinterpret_cast<PFNGLFRAMEBUFFERTEXTURE2DPROC>(GL_GetProcAddress("glFramebufferTexture2D"));
  glCheckFramebufferStatusProc = reinterpret_cast<PFNGLCHECKFRAMEBUFFERSTATUSPROC>(GL_GetProcAddress("glCheckFramebufferStatus"));
  glGenRenderbuffersProc = reinterpret_cast<PFNGLGENRENDERBUFFERSPROC>(GL_GetProcAddress("glGenRenderbuffers"));
  glBindRenderbufferProc = reinterpret_cast<PFNGLBINDRENDERBUFFERPROC>(GL_GetProcAddress("glBindRenderbuffer"));
  glRenderbufferStorageProc = reinterpret_cast<PFNGLRENDERBUFFERSTORAGEPROC>(GL_GetProcAddress("glRenderbufferStorage"));
  glFramebufferRenderbufferProc = reinterpret_cast<PFNGLFRAMEBUFFERRENDERBUFFERPROC>(GL_GetProcAddress("glFramebufferRenderbuffer"));
  glDrawBuffersProc = reinterpret_cast<PFNGLDRAWBUFFERSPROC>(GL_GetProcAddress("glDrawBuffers"));
  glUnmapBufferProc = reinterpret_cast<PFNGLUNMAPBUFFERPROC>(GL_GetProcAddress("glUnmapBuffer"));
  glBlendEquationProc = reinterpret_cast<PFNGLBLENDEQUATIONPROC>(GL_GetProcAddress("glBlendEquation"));
  glDeleteBuffersProc = reinterpret_cast<PFNGLDELETEBUFFERSPROC>(GL_GetProcAddress("glDeleteBuffers"));
  glCompressedTexImage2DProc = reinterpret_cast<PFNGLCOMPRESSEDTEXIMAGE2DPROC>(GL_GetProcAddress("glCompressedTexImage2D"));
  //glCompressedTexSubImage2DProc = reinterpret_cast<PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC>(GL_GetProcAddress("glCompressedTexSubImage2D"));
  glDrawArraysInstancedProc = reinterpret_cast<PFNGLDRAWARRAYSINSTANCEDPROC>(GL_GetProcAddress("glDrawArraysInstanced"));
  glBufferSubDataProc = reinterpret_cast<PFNGLBUFFERSUBDATAPROC>(GL_GetProcAddress("glBufferSubData"));
  glBindSamplerProc = reinterpret_cast<PFNGLBINDSAMPLERPROC>(GL_GetProcAddress("glBindSampler"));
  glVertexAttribDivisorProc = reinterpret_cast<PFNGLVERTEXATTRIBDIVISORPROC>(GL_GetProcAddress("glVertexAttribDivisor"));
  glDrawElementsInstancedProc = reinterpret_cast<PFNGLDRAWELEMENTSINSTANCEDPROC>(GL_GetProcAddress("glDrawElementsInstanced"));
  glDisableVertexAttribArrayProc = reinterpret_cast<PFNGLDISABLEVERTEXATTRIBARRAYPROC>(GL_GetProcAddress("glDisableVertexAttribArray"));
  glDrawElementsBaseVertexProc = reinterpret_cast<PFNGLDRAWELEMENTSBASEVERTEXPROC>(GL_GetProcAddress("glDrawElementsBaseVertex"));
  glDrawElementsInstancedBaseVertexProc = reinterpret_cast<PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC>(GL_GetProcAddress("glDrawElementsInstancedBaseVertex"));
#ifdef SHR3D_ENVIRONMENT_MILK
  glGenSamplersProc = reinterpret_cast<PFNGLGENSAMPLERSPROC>(GL_GetProcAddress("glGenSamplers"));
  glSamplerParameteriProc = reinterpret_cast<PFNGLSAMPLERPARAMETERIPROC>(GL_GetProcAddress("glSamplerParameteri"));
  glUniformMatrix3x4fvProc = reinterpret_cast<PFNGLUNIFORMMATRIX3X4FVPROC>(GL_GetProcAddress("glUniformMatrix3x4fv"));
  glVertexAttrib4fProc = reinterpret_cast<PFNGLVERTEXATTRIB4FPROC>(GL_GetProcAddress("glVertexAttrib4f"));
  glTexImage3DProc = reinterpret_cast<PFNGLTEXIMAGE3DPROC>(GL_GetProcAddress("glTexImage3D"));
  glGetProgramInfoLogProc = reinterpret_cast<PFNGLGETPROGRAMINFOLOGPROC>(GL_GetProcAddress("glGetProgramInfoLog"));
#endif // SHR3D_ENVIRONMENT_MILK
#ifdef SHR3D_OPENGL_SPIR_V
  glShaderBinaryProc = reinterpret_cast<PFNGLSHADERBINARYPROC>(GL_GetProcAddress("glShaderBinary"));
  glSpecializeShaderProc = reinterpret_cast<PFNGLSPECIALIZESHADERPROC>(GL_GetProcAddress("glSpecializeShader"));
#endif // SHR3D_OPENGL_SPIR_V
}

void glUseProgram(GLuint program)
{
  glUseProgramProc(program);
}

void glGenVertexArrays(GLsizei n, GLuint* arrays)
{
  glGenVertexArraysProc(n, arrays);
}

void glBindVertexArray(GLuint array)
{
  glBindVertexArrayProc(array);
}

void glGenBuffers(GLsizei n, GLuint* buffers)
{
  glGenBuffersProc(n, buffers);
}

void glBindBuffer(GLenum target, GLuint buffer)
{
  glBindBufferProc(target, buffer);
}

void glDetachShader(GLuint program, GLuint shader)
{
  glDetachShaderProc(program, shader);
}

void glDeleteProgram(GLuint program)
{
  glDeleteProgramProc(program);
}

void glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage)
{
  glBufferDataProc(target, size, data, usage);
}

GLint glGetUniformLocation(GLuint program, const GLchar* name)
{
  const GLint uniformLocation = glGetUniformLocationProc(program, name);
  //ASSERT(uniformLocation >= 0);

  return uniformLocation;
}

void glUniform1f(GLint location, GLfloat v0)
{
  glUniform1fProc(location, v0);
}

void glUniform1fv(GLint location, GLsizei count, const GLfloat* value)
{
  glUniform1fvProc(location, count, value);
}

void glUniform2f(GLint location, GLfloat v0, GLfloat v1)
{
  glUniform2fProc(location, v0, v1);
}

void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2)
{
  glUniform3fProc(location, v0, v1, v2);
}

void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
{
  glUniform4fProc(location, v0, v1, v2, v3);
}

void glUniform4fv(GLint location, GLsizei count, const GLfloat* value)
{
  glUniform4fvProc(location, count, value);
}

void glUniform1i(GLint location, GLint v0)
{
  glUniform1iProc(location, v0);
}

void glUniform1ui(GLint location, GLuint v0)
{
  glUniform1uiProc(location, v0);
}

GLint glGetAttribLocation(GLuint program, const GLchar* name)
{
  return glGetAttribLocationProc(program, name);
}

void glEnableVertexAttribArray(GLuint index)
{
  glEnableVertexAttribArrayProc(index);
}

void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer)
{
  glVertexAttribPointerProc(index, size, type, normalized, stride, pointer);
}

void glGenerateMipmap(GLenum target)
{
  glGenerateMipmapProc(target);
}

GLuint glCreateShader(GLenum shaderType)
{
  return glCreateShaderProc(shaderType);
}

void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length)
{
  glShaderSourceProc(shader, count, string, length);
}

void glCompileShader(GLuint shader)
{
  glCompileShaderProc(shader);
}

GLuint glCreateProgram()
{
  return glCreateProgramProc();
}

void glAttachShader(GLuint program, GLuint shader)
{
  glAttachShaderProc(program, shader);
}

void glLinkProgram(GLuint program)
{
  glLinkProgramProc(program);
}

void glGetProgramiv(GLuint program, GLenum pname, GLint* params)
{
  glGetProgramivProc(program, pname, params);
}

void glGetShaderiv(GLuint shader, GLenum pname, GLint* params)
{
  glGetShaderivProc(shader, pname, params);
}

void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei* length, GLchar* infoLog)
{
  glGetShaderInfoLogProc(shader, maxLength, length, infoLog);
}

void glDeleteShader(GLuint shader)
{
  glDeleteShaderProc(shader);
}

void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
{
  glUniformMatrix4fvProc(location, count, transpose, value);
}

void glActiveTexture(GLenum texture)
{
  glActiveTextureProc(texture);
}

void glGenFramebuffers(GLsizei n, GLuint* ids)
{
  glGenFramebuffersProc(n, ids);
}

void glBindFramebuffer(GLenum target, GLuint framebuffer)
{
  glBindFramebufferProc(target, framebuffer);
}

void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)
{
  glFramebufferTexture2DProc(target, attachment, textarget, texture, level);
}

GLenum glCheckFramebufferStatus(GLenum target)
{
  return glCheckFramebufferStatusProc(target);
}

void glGenRenderbuffers(GLsizei n, GLuint* renderbuffers)
{
  glGenRenderbuffersProc(n, renderbuffers);
}

void glBindRenderbuffer(GLenum target, GLuint renderbuffer)
{
  glBindRenderbufferProc(target, renderbuffer);
}

void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
{
  glRenderbufferStorageProc(target, internalformat, width, height);
}

void
glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
{
  glFramebufferRenderbufferProc(target, attachment, renderbuffertarget, renderbuffer);
}

void glDrawBuffers(GLsizei n, const GLenum* bufs)
{
  glDrawBuffersProc(n, bufs);
}

GLboolean glUnmapBuffer(GLenum target)
{
  return glUnmapBufferProc(target);
}

void glBlendEquation(GLenum mode)
{
  glBlendEquationProc(mode);
}

void glDeleteBuffers(GLsizei n, const GLuint* buffers)
{
  glDeleteBuffersProc(n, buffers);
}

void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data)
{
  glCompressedTexImage2DProc(target, level, internalformat, width, height, border, imageSize, data);
}

//void glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void* data)
//{
//    glCompressedTexSubImage2DProc(target, level, xoffset, yoffset, width, height, format, imageSize, data);
//}

void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instancecount)
{
  glDrawArraysInstancedProc(mode, first, count, instancecount);
}

void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data)
{
  glBufferSubDataProc(target, offset, size, data);
}

void glBindSampler(GLuint unit, GLuint sampler)
{
  glBindSamplerProc(unit, sampler);
}

void glVertexAttribDivisor(GLuint index, GLuint divisor)
{
  glVertexAttribDivisorProc(index, divisor);
}

void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instancecount)
{
  glDrawElementsInstancedProc(mode, count, type, indices, instancecount);
}

void glDisableVertexAttribArray(GLuint index)
{
  glDisableVertexAttribArrayProc(index);
}

void glDrawElementsBaseVertex(GLenum mode, GLsizei count, GLenum type, void* indices, GLint basevertex)
{
  glDrawElementsBaseVertexProc(mode, count, type, indices, basevertex);
}

void glDrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, void* indices, GLsizei instancecount, GLint basevertex)
{
  glDrawElementsInstancedBaseVertexProc(mode, count, type, indices, instancecount, basevertex);
}

#ifdef SHR3D_ENVIRONMENT_MILK
void glGenSamplers(GLsizei n, GLuint* samplers)
{
  glGenSamplersProc(n, samplers);
}

void glSamplerParameteri(GLuint sampler, GLenum pname, GLint param)
{
  glSamplerParameteriProc(sampler, pname, param);
}

void glUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
{
  glUniformMatrix3x4fvProc(location, count, transpose, value);
}

void glVertexAttrib4f(GLuint index, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
{
  glVertexAttrib4fProc(index, v0, v1, v2, v3);
}

void glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void* data)
{
  glTexImage3DProc(target, level, internalformat, width, height, depth, border, format, type, data);
}

void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei* length, GLchar* infoLog)
{
  glGetProgramInfoLogProc(program, maxLength, length, infoLog);
}

void glBindVertexArray_Milk_WaveForm_IgnoreGlError(GLuint array)
{
  glBindVertexArrayProc(array);
  ASSERT(glGetError() == GL_INVALID_VALUE);
}
#endif // SHR3D_ENVIRONMENT_MILK

#ifdef SHR3D_OPENGL_SPIR_V
void glShaderBinary(GLsizei count, const GLuint* shaders, GLenum binaryFormat, const void* binary, GLsizei length)
{
  glShaderBinaryProc(count, shaders, binaryFormat, binary, length);
}

void glSpecializeShader(GLuint shader, const GLchar* pEntryPoint, GLuint numSpecializationConstants, const GLuint* pConstantIndex, const GLuint* pConstantValue)
{
  glSpecializeShaderProc(shader, pEntryPoint, numSpecializationConstants, pConstantIndex, pConstantValue);
}
#endif // SHR3D_OPENGL_SPIR_V

#ifdef SHR3D_WINDOW_WIN32
HGLRC wglCreateContextAttribsARB(HDC hdc, HGLRC hShareContext, const int* attribList)
{
  return wglCreateContextAttribsARBProc(hdc, hShareContext, attribList);
}

BOOL wglChoosePixelFormatARB(HDC hdc, const int* piAttribIList, const FLOAT* pfAttribFList, UINT nMaxFormats, int* piFormats, UINT* nNumFormats)
{
  return wglChoosePixelFormatARBProc(hdc, piAttribIList, pfAttribFList, nMaxFormats, piFormats, nNumFormats);
}
BOOL wglSwapIntervalEXT(int interval)
{
  return wglSwapIntervalEXTProc(interval);
}
#endif // SHR3D_WINDOW_WIN32

#endif // !PLATFORM_OPENXR_ANDROID && !PLATFORM_EMSCRIPTEN && !SHR3D_WINDOW_ANDROID

#ifdef SHR3D_OPENGL_ERROR_CHECK
void glErrorCheck()
{
  GLenum errorCode;
  while ((errorCode = glGetError()) != GL_NO_ERROR)
  {
    switch (errorCode)
    {
    case GL_INVALID_ENUM:
      ASSERT(false);
      break;
    case GL_INVALID_VALUE:
      ASSERT(false);
      break;
    case GL_INVALID_OPERATION:
      ASSERT(false);
      break;
    case GL_INVALID_FRAMEBUFFER_OPERATION:
      ASSERT(false);
#if !defined(__ANDROID__) && !defined(PLATFORM_EMSCRIPTEN)
    case GL_STACK_OVERFLOW:
      ASSERT(false);
      break;
    case GL_STACK_UNDERFLOW:
      ASSERT(false);
      break;
    case GL_TABLE_TOO_LARGE:
      ASSERT(false);
      break;
#endif // !__ANDROID__ && !PLATFORM_EMSCRIPTEN
    case GL_OUT_OF_MEMORY:
      ASSERT(false);
      break;
    }
  }
}
#endif // SHR3D_OPENGL_ERROR_CHECK
