/*
 * Copyright 2014 The Emscripten Authors.  All rights reserved.
 * Emscripten is available under two separate licenses, the MIT license and the
 * University of Illinois/NCSA Open Source License.  Both these licenses can be
 * found in the LICENSE file.
 */

#if GL_ENABLE_GET_PROC_ADDRESS

// GL proc address retrieval

#include <string.h>
#include <stdlib.h>
#include <emscripten.h>
#include <emscripten/html5_webgl.h>

#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>

#ifdef LEGACY_GL_EMULATION

#include <webgl/webgl1_ext.h>
#include "webgl_internal_funcs.h"

#define RETURN_GL_EMU_FN(functionName) if (!strcmp(name, #functionName)) return emscripten_##functionName;

void* emscripten_legacy_gl_emulation_GetProcAddress(const char *name) {
  // main list, autogenerated from the earlier part of the file using
  /*
for line in open('a').readlines():
  if "emscripten" not in line: continue
  line = line.replace(' * ', ' ').replace('const ', '').replace('(', ' ')
  func = line.split(' ')[3].strip().replace('(', '')
  short = func.replace('EXT', '').replace('ARB', '').replace('OES', '').replace('ANGLE', '').replace('emscripten_', '')
  print '  if (!strcmp(name, "%s")) return %s;' % (short, func);
  */

  // The following list contains only exactly those functions that library_glemu.js currently implements.
  // Others will return a null pointer.

  // misc renamings
  if (!strcmp(name, "glCreateProgramObject")) return emscripten_glCreateProgram;
  if (!strcmp(name, "glUseProgramObject")) return emscripten_glUseProgram;
  if (!strcmp(name, "glCreateShaderObject")) return emscripten_glCreateShader;
  if (!strcmp(name, "glAttachObject")) return emscripten_glAttachShader;
  if (!strcmp(name, "glDetachObject")) return emscripten_glDetachShader;

  RETURN_GL_EMU_FN(glDeleteObject);
  RETURN_GL_EMU_FN(glGetObjectParameteriv);
  RETURN_GL_EMU_FN(glGetInfoLog);
  RETURN_GL_EMU_FN(glBindProgram);
  RETURN_GL_EMU_FN(glGetPointerv);
  RETURN_GL_EMU_FN(glBegin);
  RETURN_GL_EMU_FN(glEnd);
  //RETURN_GL_EMU_FN(glVertex2d);
  RETURN_GL_EMU_FN(glVertex2f);
  RETURN_GL_EMU_FN(glVertex2i);
  //RETURN_GL_EMU_FN(glVertex2s);
  //RETURN_GL_EMU_FN(glVertex3d);
  RETURN_GL_EMU_FN(glVertex3f);
  RETURN_GL_EMU_FN(glVertex3i);
  //RETURN_GL_EMU_FN(glVertex3s);
  //RETURN_GL_EMU_FN(glVertex4d);
  RETURN_GL_EMU_FN(glVertex4f);
  RETURN_GL_EMU_FN(glVertex4i);
  //RETURN_GL_EMU_FN(glVertex4s);
  //RETURN_GL_EMU_FN(glVertex2dv);
  RETURN_GL_EMU_FN(glVertex2fv);
  //RETURN_GL_EMU_FN(glVertex2iv);
  //RETURN_GL_EMU_FN(glVertex2sv);
  //RETURN_GL_EMU_FN(glVertex3dv);
  RETURN_GL_EMU_FN(glVertex3fv);
  //RETURN_GL_EMU_FN(glVertex3iv);
  //RETURN_GL_EMU_FN(glVertex3sv);
  //RETURN_GL_EMU_FN(glVertex4dv);
  RETURN_GL_EMU_FN(glVertex4fv);
  //RETURN_GL_EMU_FN(glVertex4iv);
  //RETURN_GL_EMU_FN(glVertex4sv);
  //RETURN_GL_EMU_FN(glColor3b);
  RETURN_GL_EMU_FN(glColor3d);
  RETURN_GL_EMU_FN(glColor3f);
  //RETURN_GL_EMU_FN(glColor3i);
  //RETURN_GL_EMU_FN(glColor3s);
  RETURN_GL_EMU_FN(glColor3ub);
  RETURN_GL_EMU_FN(glColor3ui);
  RETURN_GL_EMU_FN(glColor3us);
  //RETURN_GL_EMU_FN(glColor4b);
  RETURN_GL_EMU_FN(glColor4d);
  RETURN_GL_EMU_FN(glColor4f);
  //RETURN_GL_EMU_FN(glColor4i);
  //RETURN_GL_EMU_FN(glColor4s);
  RETURN_GL_EMU_FN(glColor4ub);
  RETURN_GL_EMU_FN(glColor4ui);
  RETURN_GL_EMU_FN(glColor4us);
  //RETURN_GL_EMU_FN(glColor3bv);
  //RETURN_GL_EMU_FN(glColor3dv);
  RETURN_GL_EMU_FN(glColor3fv);
  //RETURN_GL_EMU_FN(glColor3iv);
  //RETURN_GL_EMU_FN(glColor3sv);
  RETURN_GL_EMU_FN(glColor3ubv);
  RETURN_GL_EMU_FN(glColor3uiv);
  RETURN_GL_EMU_FN(glColor3usv);
  //RETURN_GL_EMU_FN(glColor4bv);
  //RETURN_GL_EMU_FN(glColor4dv);
  RETURN_GL_EMU_FN(glColor4fv);
  //RETURN_GL_EMU_FN(glColor4iv);
  //RETURN_GL_EMU_FN(glColor4sv);
  RETURN_GL_EMU_FN(glColor4ubv);
  //RETURN_GL_EMU_FN(glColor4uiv);
  //RETURN_GL_EMU_FN(glColor4usv);
  //RETURN_GL_EMU_FN(glFogCoordf);
  //RETURN_GL_EMU_FN(glFogCoordfv);
  //RETURN_GL_EMU_FN(glFogCoordd);
  //RETURN_GL_EMU_FN(glFogCoorddv);
  //RETURN_GL_EMU_FN(glFogCoordPointer);
  RETURN_GL_EMU_FN(glPolygonMode);
  RETURN_GL_EMU_FN(glAlphaFunc);
  //RETURN_GL_EMU_FN(glNormal3b);
  //RETURN_GL_EMU_FN(glNormal3d);
  RETURN_GL_EMU_FN(glNormal3f);
  //RETURN_GL_EMU_FN(glNormal3i);
  //RETURN_GL_EMU_FN(glNormal3s);
  //RETURN_GL_EMU_FN(glNormal3bv);
  //RETURN_GL_EMU_FN(glNormal3dv);
  //RETURN_GL_EMU_FN(glNormal3fv);
  //RETURN_GL_EMU_FN(glNormal3iv);
  //RETURN_GL_EMU_FN(glNormal3sv);
  RETURN_GL_EMU_FN(glDrawRangeElements);
  RETURN_GL_EMU_FN(glEnableClientState);
  RETURN_GL_EMU_FN(glDisableClientState);
  RETURN_GL_EMU_FN(glVertexPointer);
  RETURN_GL_EMU_FN(glNormalPointer);
  RETURN_GL_EMU_FN(glColorPointer);
  RETURN_GL_EMU_FN(glTexCoordPointer);
  RETURN_GL_EMU_FN(glClientActiveTexture);
  RETURN_GL_EMU_FN(glMatrixMode);
  RETURN_GL_EMU_FN(glPushMatrix);
  RETURN_GL_EMU_FN(glPopMatrix);
  RETURN_GL_EMU_FN(glLoadIdentity);
  RETURN_GL_EMU_FN(glLoadMatrixd);
  RETURN_GL_EMU_FN(glLoadMatrixf);
  RETURN_GL_EMU_FN(glLoadTransposeMatrixf);
  RETURN_GL_EMU_FN(glLoadTransposeMatrixd);
  RETURN_GL_EMU_FN(glMultTransposeMatrixf);
  RETURN_GL_EMU_FN(glMultTransposeMatrixd);
  RETURN_GL_EMU_FN(glMultMatrixd);
  RETURN_GL_EMU_FN(glMultMatrixf);
  RETURN_GL_EMU_FN(glOrtho);
  RETURN_GL_EMU_FN(glFrustum);
  RETURN_GL_EMU_FN(glRotated);
  RETURN_GL_EMU_FN(glRotatef);
  RETURN_GL_EMU_FN(glScaled);
  RETURN_GL_EMU_FN(glScalef);
  RETURN_GL_EMU_FN(glTranslated);
  RETURN_GL_EMU_FN(glTranslatef);
  RETURN_GL_EMU_FN(glClipPlane);
  RETURN_GL_EMU_FN(glDrawBuffer);
  RETURN_GL_EMU_FN(glReadBuffer);
  //RETURN_GL_EMU_FN(glLightf);
  //RETURN_GL_EMU_FN(glLighti);
  RETURN_GL_EMU_FN(glLightfv);
  //RETURN_GL_EMU_FN(glLightiv);
  RETURN_GL_EMU_FN(glLightModelf);
  //RETURN_GL_EMU_FN(glLightModeli);
  RETURN_GL_EMU_FN(glLightModelfv);
  //RETURN_GL_EMU_FN(glLightModeliv);
  //RETURN_GL_EMU_FN(glMaterialf);
  //RETURN_GL_EMU_FN(glMateriali);
  RETURN_GL_EMU_FN(glMaterialfv);
  //RETURN_GL_EMU_FN(glMaterialiv);
  //RETURN_GL_EMU_FN(glTexGend);
  //RETURN_GL_EMU_FN(glTexGenf);
  RETURN_GL_EMU_FN(glTexGeni);
  //RETURN_GL_EMU_FN(glTexGendv);
  RETURN_GL_EMU_FN(glTexGenfv);
  //RETURN_GL_EMU_FN(glTexGeniv);
  RETURN_GL_EMU_FN(glGetTexEnvfv);
  RETURN_GL_EMU_FN(glGetTexEnviv);
  RETURN_GL_EMU_FN(glTexImage1D);
  //RETURN_GL_EMU_FN(glTexCoord1d);
  //RETURN_GL_EMU_FN(glTexCoord1f);
  //RETURN_GL_EMU_FN(glTexCoord1i);
  //RETURN_GL_EMU_FN(glTexCoord1s);
  //RETURN_GL_EMU_FN(glTexCoord2d);
  RETURN_GL_EMU_FN(glTexCoord2f);
  RETURN_GL_EMU_FN(glTexCoord2i);
  //RETURN_GL_EMU_FN(glTexCoord2s);
  //RETURN_GL_EMU_FN(glTexCoord3d);
  RETURN_GL_EMU_FN(glTexCoord3f);
  //RETURN_GL_EMU_FN(glTexCoord3i);
  //RETURN_GL_EMU_FN(glTexCoord3s);
  //RETURN_GL_EMU_FN(glTexCoord4d);
  RETURN_GL_EMU_FN(glTexCoord4f);
  //RETURN_GL_EMU_FN(glTexCoord4i);
  //RETURN_GL_EMU_FN(glTexCoord4s);
  //RETURN_GL_EMU_FN(glTexCoord1dv);
  //RETURN_GL_EMU_FN(glTexCoord1fv);
  //RETURN_GL_EMU_FN(glTexCoord1iv);
  //RETURN_GL_EMU_FN(glTexCoord1sv);
  //RETURN_GL_EMU_FN(glTexCoord2dv);
  RETURN_GL_EMU_FN(glTexCoord2fv);
  //RETURN_GL_EMU_FN(glTexCoord2iv);
  //RETURN_GL_EMU_FN(glTexCoord2sv);
  //RETURN_GL_EMU_FN(glTexCoord3dv);
  //RETURN_GL_EMU_FN(glTexCoord3fv);
  //RETURN_GL_EMU_FN(glTexCoord3iv);
  //RETURN_GL_EMU_FN(glTexCoord3sv);
  //RETURN_GL_EMU_FN(glTexCoord4dv);
  //RETURN_GL_EMU_FN(glTexCoord4fv);
  //RETURN_GL_EMU_FN(glTexCoord4iv);
  //RETURN_GL_EMU_FN(glTexCoord4sv);
  //RETURN_GL_EMU_FN(glGetTexLevelParameterfv);
  RETURN_GL_EMU_FN(glGetTexLevelParameteriv);
  RETURN_GL_EMU_FN(glShadeModel);

  // GL emulation library "sloppy" lookup:
  // WebGL 1 extensions are offered without their EXT suffixes.
  if (!strcmp(name, "glBindVertexArray")) return emscripten_glBindVertexArrayOES;
  if (!strcmp(name, "glDeleteVertexArrays")) return emscripten_glDeleteVertexArraysOES;
  if (!strcmp(name, "glGenVertexArrays")) return emscripten_glGenVertexArraysOES;
  if (!strcmp(name, "glIsVertexArray")) return emscripten_glIsVertexArrayOES;
  if (!strcmp(name, "glDrawBuffers")) return emscripten_glDrawBuffersWEBGL;
  if (!strcmp(name, "glDrawArraysInstanced")) return emscripten_glDrawArraysInstancedANGLE;
  if (!strcmp(name, "glDrawElementsInstanced")) return emscripten_glDrawElementsInstancedANGLE;
  if (!strcmp(name, "glVertexAttribDivisor")) return emscripten_glVertexAttribDivisorANGLE;
  if (!strcmp(name, "glGenQueries")) return emscripten_glGenQueriesEXT;
  if (!strcmp(name, "glDeleteQueries")) return emscripten_glDeleteQueriesEXT;
  if (!strcmp(name, "glIsQuery")) return emscripten_glIsQueryEXT;
  if (!strcmp(name, "glBeginQuery")) return emscripten_glBeginQueryEXT;
  if (!strcmp(name, "glEndQuery")) return emscripten_glEndQueryEXT;
  if (!strcmp(name, "glQueryCounter")) return emscripten_glQueryCounterEXT;
  if (!strcmp(name, "glGetQueryiv")) return emscripten_glGetQueryivEXT;
  if (!strcmp(name, "glGetQueryObjectiv")) return emscripten_glGetQueryObjectivEXT;
  if (!strcmp(name, "glGetQueryObjectuiv")) return emscripten_glGetQueryObjectuivEXT;
  if (!strcmp(name, "glGetQueryObjecti64v")) return emscripten_glGetQueryObjecti64vEXT;
  if (!strcmp(name, "glGetQueryObjectui64v")) return emscripten_glGetQueryObjectui64vEXT;

  return 0;
}
#endif

void *_webgl1_match_ext_proc_address_without_suffix(const char *name);
void *_webgl2_match_ext_proc_address_without_suffix(const char *name);

// "Sloppy" desktop OpenGL/mobile GLES emulating
// behavior: different functionality is available under
// different vendor suffixes. In emscripten_GetProcAddress()
// function, all these suffixes will be ignored when performing
// the function pointer lookup.
void* emscripten_GetProcAddress(const char *name_) {
  char *name = malloc(strlen(name_)+1);
  strcpy(name, name_);
  // remove EXT|ARB|OES|ANGLE suffixes
  char *end = strstr(name, "EXT");
  if (end) *end = 0;
  end = strstr(name, "ARB");
  if (end) *end = 0;
  end = strstr(name, "OES");
  if (end) *end = 0;
  end = strstr(name, "ANGLE");
  if (end) *end = 0;
  end = strstr(name, "WEBGL");
  if (end) *end = 0;

  void *ptr = emscripten_webgl1_get_proc_address(name);

  if (!ptr) ptr = _webgl1_match_ext_proc_address_without_suffix(name);

#if LEGACY_GL_EMULATION
  if (!ptr) ptr = emscripten_legacy_gl_emulation_GetProcAddress(name);
#endif
#if MAX_WEBGL_VERSION >= 2
  if (!ptr) ptr = emscripten_webgl2_get_proc_address(name);
  if (!ptr) ptr = _webgl2_match_ext_proc_address_without_suffix(name);
#endif

  free(name);
  return ptr;
}

extern void *emscripten_webgl_get_proc_address(const char *name)
{
  void *ptr = emscripten_webgl1_get_proc_address(name);
#if MAX_WEBGL_VERSION >= 2
  if (!ptr) ptr = emscripten_webgl2_get_proc_address(name);
#endif
  return ptr;
}

#endif
