/*
  Copyright (C) 2002 by Anders Stenberg

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*

 Please be careful when changing this file. It's the result of roughly
 78 hours of work, where approximately 14 liters of coffee where consumed,
 the keyboard been replaced 4 times due to worn-out cut'n'paste combo keys
 and two ambulance calls because of near-fatal RSI syndroms. Every line has
 been thought of carefully and has been highly optimized by 3 members of a
 Zen buddhist religion in a Tibetian monastry. Thus modifications, as they
 touch the core OpenGL support, could severely affect performance. Beware!
 Okay, maybe this was a bit exagerated... this file is autogenerated.
 
 */

/**\file
 * GL extensions manager
 */

#ifndef __CS_GLEXTENSIONMANAGER_H__
#define __CS_GLEXTENSIONMANAGER_H__

/**********************************************************************
 * Begin system-specific stuff.
 */
#if defined(__BEOS__)
#include <stdlib.h>     /* to get some BeOS-isms */
#endif

#if !defined(OPENSTEP) && (defined(NeXT) || defined(NeXT_PDO))
#define OPENSTEP
#endif

#if defined(_WIN32) && !defined(__WIN32__) && !defined(__CYGWIN__)
#define __WIN32__
#endif

#if !defined(GLAPI)
#  if !defined(OPENSTEP) && (defined(__WIN32__) && !defined(__CYGWIN__))
#    if defined(_MSC_VER) && defined(BUILD_GL32) /* tag specify we're building mesa as a DLL */
#      define GLAPI __declspec(dllexport)
#    elif defined(_MSC_VER) && defined(_DLL) /* tag specifying we're building for DLL runtime support */
#      define GLAPI __declspec(dllimport)
#    else /* for use with static link lib build of Win32 edition only */
#      define GLAPI extern
#    endif /* _STATIC_MESA support */
#    define GLAPIENTRY __stdcall
#  else
/* non-Windows compilation */
/* In most cases, it seems safest to avoid defining these at all. Please report
 *  if this causes trouble.
 * #define GLAPI extern
 * #define GLAPI
 * #define GLAPIENTRY
 */
#  endif /* WIN32 / CYGWIN bracket */
#endif

//#if defined(_WIN32) && !defined(_WINGDI_) && !defined(__CYGWIN__) && !defined(_GNU_H_WINDOWS32_DEFINES) && !defined(OPENSTEP)
//#include <gl/mesa_wgl.h>
//#endif

#if defined(macintosh) && PRAGMA_IMPORT_SUPPORTED
#pragma import on
#endif

#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__)
#define WIN32_LEAN_AND_MEAN 1
#include <windows.h>
#endif

#ifndef csAPIENTRY
#ifndef APIENTRY
#define csAPIENTRY
#else
#define csAPIENTRY APIENTRY
#endif
#endif

// In CS, we ignore Cygwin's graphical offerings and use the Windows versions.
#if defined(__CYGWIN__) && !defined(_WIN32)
#define _WIN32
#endif

// Assume that GLX is used on Unix platforms (except OS/X)
#if defined(CS_PLATFORM_UNIX) && !defined(CS_PLATFORM_MACOSX)
#define CS_OPENGL_GLX
/* NOTE: CS_GLEXTMANAGER_USE_GLX must be defined to get the Init*
   functions for GLX extensions. (This was done to avoid pulling X
   headers into every component using the GL extension manager.) */
#endif

/*
 * End system-specific stuff.
 **********************************************************************/

#ifdef DOXYGEN_RUN
typedef void GLvoid;
typedef int GLint;
typedef uint GLuint;
typedef int GLsizei;
typedef uint GLenum;
typedef float GLfloat;
typedef double GLdouble;
typedef unsigned char GLubyte;
#  define _WIN32
#else
#  if defined(CS_OPENGL_PATH)
#    include CS_HEADER_GLOBAL(CS_OPENGL_PATH,gl.h)
#  else
#    include <GL/gl.h>
#  endif
#endif

#include "cssysdef.h"

/*
  Appear in the ARB_shader_objects ext spec.
 */
#ifndef CS_HAVE_GLCHARARB_T
typedef char GLcharARB;
#endif
#ifndef CS_HAVE_GLHANDLEARB_T
typedef unsigned int GLhandleARB;
#endif
/* Identifiers after promotion to core */
#ifndef CS_HAVE_GLCHAR_T
typedef GLcharARB GLchar;
#endif
/* Newer GL type identifiers */
#ifndef CS_HAVE_GLINTPTR_T
typedef intptr_t GLintptr;
#endif
#ifndef CS_HAVE_GLSIZEIPTR_T
typedef uintptr_t GLsizeiptr;
#endif
#ifndef CS_HAVE_GLINT64_T
typedef int64 GLint64;
#endif
#ifndef CS_HAVE_GLUINT64_T
typedef uint64 GLuint64;
#endif

#include "iutil/cmdline.h"
#include "iutil/objreg.h"
#include "iutil/verbositymanager.h"
#include "ivaria/reporter.h"
#include "ivideo/graph2d.h"
#include "csutil/cfgacc.h"
#include "csutil/stringquote.h"
#include "csplugincommon/iopengl/openglinterface.h"

%Definitions%

// end of definitions

#ifdef CS_DEBUG
#  define REPORT_MISSING_ENTRIES true
#else
#  define REPORT_MISSING_ENTRIES false
#endif

#define EXTMGR_FUNC_INIT(nameNC, nameUC)				\
      funcTest =							\
	((nameNC = (cs##nameUC) gl->GetProcAddress (#nameNC)) != 0);	\
      if (!funcTest && config->GetBool (				\
	"Video.OpenGL.ReportMissingEntries",				\
	REPORT_MISSING_ENTRIES))					\
      {									\
	Report (msgExtRetrieveFail, #nameNC); 				\
      }									\
      allclear &= funcTest;

#define EXTMGR_REPORT_INIT_RESULT(exttype, nameNC)			\
      CS_##nameNC = allclear;						\
      if (CS_##nameNC) 					                \
      {									\
        CS_##nameNC &= config->GetBool (cfgkey, defaultUse);		\
        if (CS_##nameNC)						\
        {								\
          Report (msgExtFoundAndUsed, exttype, CS::Quote::Single (ext));\
        }								\
        else								\
        {								\
          Report (msgExtFoundAndNotUsed, exttype,			\
		  CS::Quote::Single (ext));				\
        }								\
      }									\
      else								\
      {									\
        Report (msgExtInitFail, exttype, CS::Quote::Single (ext));	\
      }

/// Struct containing all GL extension functions.
struct csGLExtensionFunctions
{
public:
%Functions%
// end of functions
};

/// Struct containing all GL extension test flags.
struct csGLExtensionFlags
{
public:
%ExtFlagsDetected%
protected:
%ExtFlagsTested%
};

/**
 * GL extensions manager. Checks for the presence of specific GL extensions
 * and fetches all function pointers if present.
 */
struct csGLExtensionManager : public csGLExtensionFunctions,
			      public csGLExtensionFlags
{
private:
  iObjectRegistry* object_reg;
  csConfigAccess config;
  iOpenGLInterface* gl;
  bool doVerbose;
  bool defaultUse;
  
  const char* extstrGL;
  const char* msgExtRetrieveFail;
  const char* msgExtFoundAndUsed;
  const char* msgExtFoundAndNotUsed;
  const char* msgExtInitFail;
  const char* msgExtNotFound;
  const char* msgDependencyNotFound;

#ifdef __WIN32__
  const char* extstrWGL;
  void SetupWGLextStr (HDC hDC)
  {
    if (extstrWGL != 0) return;
  
    if (!tested_CS_WGL_ARB_extensions_string) InitWGL_ARB_extensions_string (hDC);
    if (CS_WGL_ARB_extensions_string)
    {
      extstrWGL = wglGetExtensionsStringARB (hDC);
    }
    else
    {
      extstrWGL = extstrGL;
    }
  }
#endif

#ifdef CS_OPENGL_GLX
  const char* extstrGLX;
#ifdef CS_GLEXTMANAGER_USE_GLX
  void SetupGLXextStr (Display* glxDisplay, int glxScreen)
  {
    if (extstrGLX != 0) return;
  
    extstrGLX = glXQueryExtensionsString (glxDisplay, glxScreen);
  }
#endif
#endif

  void Report (const char* msg, ...)
  {
    if (!doVerbose)
      return;
    
    va_list arg;
    va_start (arg, msg);
    csReportV (object_reg, CS_REPORTER_SEVERITY_NOTIFY,
      "crystalspace.canvas.opengl.extmgr", msg, arg);
    va_end (arg);
  }

  /**
   * Check if the extension \a extToCheck is present in the GL/WGL/GLX
   * extensions list \a extensions. Takes care that no substrings are picked
   * up.
   */
  bool CheckExtension (const char* extensions, const char* extToCheck)
  {
    size_t extLen = strlen (extToCheck);
    do
    {
      const char* extStrPos = strstr (extensions, extToCheck);
      if (extStrPos == 0) return false;
      
      /* If no space is before and after the ext string it's a substring
       * of another ext */
      char endch;
      if (((extStrPos > extensions) && (*(extStrPos-1) != ' '))
        || (((endch = *(extStrPos + extLen)) != 0) && (endch != ' ')))
      {
        extensions = extStrPos + extLen;
        continue;
      }
      break;
    }
    while (true);
    return true;
  }
public:
  void Initialize (iObjectRegistry* object_reg, iGraphics2D* g2d)
  {
    csGLExtensionManager::object_reg = object_reg;
    gl = csRef<iOpenGLInterface> (scfQueryInterface<iOpenGLInterface> (g2d));
    // Low priority so canvas/renderer cfgs may override the settings
    config.AddConfig (object_reg, "/config/glext.cfg", true,
      iConfigManager::ConfigPriorityPlugin - 1);

    csRef<iVerbosityManager> verbosemgr (csQueryRegistry<iVerbosityManager> (
      object_reg));
    doVerbose = verbosemgr->Enabled ("renderer");
  }
  
  void Open () 
  { 
    extstrGL = (const char*)glGetString (GL_EXTENSIONS);
    defaultUse = config->GetBool ("Video.OpenGL.UseExtension.ALL", true);
    if (!defaultUse)
      Report ("ALL extensions are disabled by default");
  }
  
  void Close () { }
public:
  void Reset ()
  {
    extstrGL = 0;
#ifdef __WIN32__
    extstrWGL = 0;
#endif
#ifdef CS_OPENGL_GLX
    extstrGLX = 0;
#endif

    memset ((csGLExtensionFunctions*)this, 0, 
      sizeof (csGLExtensionFunctions));
    memset ((csGLExtensionFlags*)this, 0, sizeof (csGLExtensionFlags));
  }
  
  csGLExtensionManager () : object_reg (0), gl (0), doVerbose (false),
    defaultUse (true)
  {
    msgExtRetrieveFail = "Failed to retrieve %%s";
    msgExtFoundAndUsed = "%%s Extension %%s found and used.";
    msgExtFoundAndNotUsed = "%%s Extension %%s found, but not used.";
    msgExtInitFail = "%%s Extension %%s failed to initialize.";
    msgExtNotFound = "%%s Extension %%s not found.";
    msgDependencyNotFound = "%%s Extension %%s depends on %%s which did "
      "not initialize.";
    
    Reset ();
  }
  
%InitExtensions%
};

#undef REPORT_MISSING_ENTRIES

#undef EXTMGR_FUNC_INIT
#undef EXTMGR_REPORT_INIT_RESULT

#endif // __CS_GLEXTENSIONMANAGER_H__

