#include "gl_loader.h"

#ifdef _WIN32

PFNGLATTACHSHADERPROC        fn_glAttachShader        = nullptr;
PFNGLBINDBUFFERPROC          fn_glBindBuffer          = nullptr;
PFNGLBINDFRAMEBUFFERPROC     fn_glBindFramebuffer     = nullptr;
PFNGLBINDVERTEXARRAYPROC     fn_glBindVertexArray     = nullptr;
PFNGLCOMPILESHADERPROC       fn_glCompileShader       = nullptr;
PFNGLCREATEPROGRAMPROC       fn_glCreateProgram       = nullptr;
PFNGLCREATESHADERPROC        fn_glCreateShader        = nullptr;
PFNGLDELETEBUFFERSPROC       fn_glDeleteBuffers       = nullptr;
PFNGLDELETEFRAMEBUFFERSPROC  fn_glDeleteFramebuffers  = nullptr;
PFNGLDELETEPROGRAMPROC       fn_glDeleteProgram       = nullptr;
PFNGLDELETESHADERPROC        fn_glDeleteShader        = nullptr;
PFNGLDELETEVERTEXARRAYSPROC  fn_glDeleteVertexArrays  = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DPROC fn_glFramebufferTexture2D = nullptr;
PFNGLGENBUFFERSPROC          fn_glGenBuffers          = nullptr;
PFNGLGENFRAMEBUFFERSPROC     fn_glGenFramebuffers     = nullptr;
PFNGLGENVERTEXARRAYSPROC     fn_glGenVertexArrays     = nullptr;
PFNGLGETPROGRAMINFOLOGPROC   fn_glGetProgramInfoLog   = nullptr;
PFNGLGETPROGRAMIVPROC        fn_glGetProgramiv        = nullptr;
PFNGLGETSHADERINFOLOGPROC    fn_glGetShaderInfoLog    = nullptr;
PFNGLGETSHADERIVPROC         fn_glGetShaderiv         = nullptr;
PFNGLGETUNIFORMLOCATIONPROC  fn_glGetUniformLocation  = nullptr;
PFNGLLINKPROGRAMPROC         fn_glLinkProgram         = nullptr;
PFNGLSHADERSOURCEPROC        fn_glShaderSource        = nullptr;
PFNGLUNIFORM1FPROC           fn_glUniform1f           = nullptr;
PFNGLUNIFORM1IPROC           fn_glUniform1i           = nullptr;
PFNGLUNIFORM2FPROC           fn_glUniform2f           = nullptr;
PFNGLUNIFORM3FPROC           fn_glUniform3f           = nullptr;
PFNGLUNIFORM4FPROC           fn_glUniform4f           = nullptr;
PFNGLUNIFORM4FVPROC          fn_glUniform4fv          = nullptr;
PFNGLUNIFORMMATRIX4FVPROC    fn_glUniformMatrix4fv    = nullptr;
PFNGLUSEPROGRAMPROC          fn_glUseProgram          = nullptr;
PFNGLACTIVETEXTUREPROC       fn_glActiveTexture       = nullptr;
PFNGLGENERATEMIPMAPPROC      fn_glGenerateMipmap      = nullptr;
PFNGLBUFFERDATAPROC          fn_glBufferData          = nullptr;
PFNGLDRAWARRAYSINSTANCEDPROC fn_glDrawArraysInstanced = nullptr;
PFNGLENABLEVERTEXATTRIBARRAYPROC fn_glEnableVertexAttribArray = nullptr;
PFNGLVERTEXATTRIBDIVISORPROC fn_glVertexAttribDivisor = nullptr;
PFNGLVERTEXATTRIBPOINTERPROC fn_glVertexAttribPointer = nullptr;
PFNGLBINDRENDERBUFFERPROC    fn_glBindRenderbuffer    = nullptr;
PFNGLDELETERENDERBUFFERSPROC fn_glDeleteRenderbuffers = nullptr;
PFNGLFRAMEBUFFERRENDERBUFFERPROC fn_glFramebufferRenderbuffer = nullptr;
PFNGLGENRENDERBUFFERSPROC    fn_glGenRenderbuffers    = nullptr;
PFNGLRENDERBUFFERSTORAGEPROC fn_glRenderbufferStorage = nullptr;
PFNGLFRAMEBUFFERTEXTUREPROC  fn_glFramebufferTexture  = nullptr;

namespace fn::gfx {

bool gl_loader_init() {
    #define LOAD(name) \
        fn_##name = (decltype(fn_##name))wglGetProcAddress(#name); \
        if (!fn_##name) return false

    LOAD(glAttachShader);
    LOAD(glBindBuffer);
    LOAD(glBindFramebuffer);
    LOAD(glBindVertexArray);
    LOAD(glCompileShader);
    LOAD(glCreateProgram);
    LOAD(glCreateShader);
    LOAD(glDeleteBuffers);
    LOAD(glDeleteFramebuffers);
    LOAD(glDeleteProgram);
    LOAD(glDeleteShader);
    LOAD(glDeleteVertexArrays);
    LOAD(glFramebufferTexture2D);
    LOAD(glGenBuffers);
    LOAD(glGenFramebuffers);
    LOAD(glGenVertexArrays);
    LOAD(glGetProgramInfoLog);
    LOAD(glGetProgramiv);
    LOAD(glGetShaderInfoLog);
    LOAD(glGetShaderiv);
    LOAD(glGetUniformLocation);
    LOAD(glLinkProgram);
    LOAD(glShaderSource);
    LOAD(glUniform1f);
    LOAD(glUniform1i);
    LOAD(glUniform2f);
    LOAD(glUniform3f);
    LOAD(glUniform4f);
    LOAD(glUniform4fv);
    LOAD(glUniformMatrix4fv);
    LOAD(glUseProgram);
    LOAD(glActiveTexture);
    LOAD(glGenerateMipmap);
    LOAD(glBufferData);
    LOAD(glDrawArraysInstanced);
    LOAD(glEnableVertexAttribArray);
    LOAD(glVertexAttribDivisor);
    LOAD(glVertexAttribPointer);
    LOAD(glBindRenderbuffer);
    LOAD(glDeleteRenderbuffers);
    LOAD(glFramebufferRenderbuffer);
    LOAD(glGenRenderbuffers);
    LOAD(glRenderbufferStorage);
    LOAD(glFramebufferTexture);

    #undef LOAD
    return true;
}

} // namespace fn::gfx

#else

namespace fn::gfx {
bool gl_loader_init() { return true; }
} // namespace fn::gfx

#endif
