/**
 * gltool.c
 *
 * Copyright (C) 2001  Sven Goethel
 *
 * GNU Library General Public License 
 * as published by the Free Software Foundation
 *
 * http://www.gnu.org/copyleft/lgpl.html
 * General dynamical loading OpenGL (GL/GLU) support for:
 *
 *
 * <OS - System>          <#define>  commentary
 * -----------------------------------------------
 * GNU/Linux, Unices/X11  _X11_      (loads glx also)
 * Macinstosh OS9         _MAC_OS9_
 * Macinstosh OSX         _MAC_OSX_
 * Win32                  _WIN32_
 *
 */
#include "gltool.h"
#include "gl-disp-var.hc"
#include "glu-disp-var.hc"

#ifdef _X11_
	#include <string.h>
	#include "glxtool.h"
#endif

#ifdef _WIN32_
	#include "wgltool.h"
#endif

static int _glLibsLoaded = 0;

#ifdef _WIN32_
  static HMODULE hDLL_OPENGL32 = 0;
  static HMODULE hDLL_OPENGLU32 = 0;
#endif

#ifdef _X11_
  static void *libHandleGL=0;
  static void *libHandleGLU=0;
#endif

#ifdef _MAC_OS9_
  Ptr glLibMainAddr = 0;
  CFragConnectionID glLibConnectId = 0;
#endif

#ifndef NOGLCHECKS
void LIBAPIENTRY print_gl_error (const char *msg, const char *file, int line, GLenum errorcode)
{
  if (errorcode != GL_NO_ERROR)
  {
    const char *errstr = (const char *) disp__gluErrorString (errorcode);
    if (errstr != 0)
      fprintf (stderr, "\n\n****\n**%s %s:%d>0x%X %s\n****\n", msg, file, line, errorcode, errstr);
    else
      fprintf (stderr, "\n\n****\n**%s %s:%d>0x%X <unknown>\n****\n", msg, file, line, errorcode);
  }
}

#ifdef _WIN32_
void LIBAPIENTRY check_wgl_error (HWND wnd, const char *file, int line)
{
  LPVOID lpMsgBuf;

  FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		 0, GetLastError (), MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),	// Default language
		 (LPTSTR) & lpMsgBuf, 0, 0);	// Display the string.

  fprintf (stderr, "\n\n****\n** %s:%d>%s\n****\n", file, line, lpMsgBuf);

  // Free the buffer.
  LocalFree (lpMsgBuf);
}
#endif
#endif

#ifdef GLDEBUG
static int gl_begin_ctr;

void LIBAPIENTRY __sglBegin(const char * file, int line, GLenum mode)
{
#ifndef NOGLCHECKS
  	print_gl_error("GL-PreBEGIN-CHECK", file, line, disp__glGetError());
#endif
	if(gl_begin_ctr!=0)
	{
		fprintf(stderr, "\n\n****\n** GL-BEGIN-ERROR %s:%d> glBegin was called %d times (reset now)\n****\n", 
			file, line, gl_begin_ctr);
		gl_begin_ctr=0;
	} else
		gl_begin_ctr++;

	disp__glBegin(mode);
}

void LIBAPIENTRY __sglEnd(const char * file, int line)
{
	if(gl_begin_ctr!=1)
	{
		fprintf(stderr, "\n\n****\n** GL-END-ERROR %s:%d> glBegin was called %d times (reset now)\n****\n", 
			file, line, gl_begin_ctr);
		gl_begin_ctr=1;
	} else
		gl_begin_ctr--;

	disp__glEnd();
#ifndef NOGLCHECKS
  	print_gl_error("GL-PostEND-CHECK", file, line, disp__glGetError());
#endif
}

/** 
 * call this function only if you 
 * are not within a glBegin/glEnd block,
 * or you think your are not ;-)
 */
void LIBAPIENTRY checkGlBeginEndBalance(const char *file, int line)
{
	if(gl_begin_ctr!=0)
	{
		fprintf(stderr, "\n****\n** GL-BeginEnd-ERROR %s:%d> glBegin was called %d times\n****\n", 
			file, line, gl_begin_ctr);
	}
#ifndef NOGLCHECKS
  	print_gl_error("GL-BeginEnd-CHECK", file, line, disp__glGetError());
#endif
}

void LIBAPIENTRY showGlBeginEndBalance(const char *file, int line)
{
	fprintf(stderr, "\n****\n** GL-BeginEnd %s:%d> glBegin was called %d times\n****\n", 
		file, line, gl_begin_ctr);
}
#endif

int LIBAPIENTRY unloadGLLibrary ()
{
#ifdef _WIN32_
      if(hDLL_OPENGL32!=NULL)
      {
		FreeLibrary(hDLL_OPENGL32);
		hDLL_OPENGL32=NULL;
      }
      if(hDLL_OPENGLU32!=NULL)
      {
		FreeLibrary(hDLL_OPENGLU32);
		hDLL_OPENGLU32=NULL;
      }
#endif

#ifdef _X11_
      if(libHandleGL!=NULL)
      {
	      dlclose (libHandleGL);
	      libHandleGL = NULL;
      }
      if(libHandleGLU!=NULL)
      {
	      dlclose (libHandleGLU);
	      libHandleGLU = NULL;
      }
#endif

#ifdef _MAC_OS9_
      if (glLibConnectId!=NULL)
      {
	(void) CloseConnection(&glLibConnectId);
	glLibConnectId=0;
      }
      glLibMainAddr = 0;
#endif

      _glLibsLoaded = 0;
      return 1;

}

int LIBAPIENTRY loadGLLibrary (const char * libGLName, const char * libGLUName)
{
  const char *envGLName  = NULL;
  const char *envGLUName = NULL;
#ifdef _MAC_OS9_
  Str255 errName;
  OSErr returnError=fragNoErr;
#endif

  if(_glLibsLoaded) return 1;

  envGLName  = getenv("GLTOOL_USE_GLLIB");
  envGLUName = getenv("GLTOOL_USE_GLULIB");

  if(envGLName!=NULL)
  {
  	libGLName = envGLName;
	fprintf(stderr, "GLTOOL: using env's GLTOOL_USE_GLLIB = %s\n", libGLName);
  }

  if(envGLUName!=NULL)
  {
  	libGLUName = envGLUName;
	fprintf(stderr, "GLTOOL: using env's GLTOOL_USE_GLULIB = %s\n", libGLUName);
  }

#ifdef _WIN32_
  if(hDLL_OPENGL32!=NULL) return 1;

  hDLL_OPENGL32 = LoadLibrary (libGLName);
  hDLL_OPENGLU32 = LoadLibrary (libGLUName);

  if (hDLL_OPENGL32 == NULL)
  {
      fprintf (stderr, "GLERROR: cannot access OpenGL library %s\n", libGLName);
      fflush (NULL);
      return 0;
  }

  if (hDLL_OPENGLU32 == NULL)
  {
      fprintf (stderr, "GLERROR: cannot access GLU library %s\n", libGLUName);
      fflush (NULL);
      return 0;
  }

#endif

#ifdef _X11_
#ifdef SUN_FORTE_DLOPEN_LIBCRUN
  {
     void *libcrun;

     libcrun = dlopen (SUN_FORTE_DLOPEN_LIBCRUN, RTLD_LAZY | RTLD_GLOBAL);
     if (libcrun == NULL)
     {
        fprintf (stderr, "GLERROR: cannot access library %s\n", SUN_FORTE_DLOPEN_LIBCRUN);
        fprintf (stderr, "GLERROR: dlerror() returns [%s]\n", dlerror());
        fflush (NULL);
        return 0;
     }
  }
#endif

  libHandleGL = dlopen (libGLName, RTLD_LAZY | RTLD_GLOBAL);
  if (libHandleGL == NULL)
  {
      fprintf (stderr, "GLERROR: cannot access OpenGL library %s\n", libGLName);
      fprintf (stderr, "GLERROR: dlerror() returns [%s]\n", dlerror());
      fflush (stderr);
      return 0;
  }

  libHandleGLU = dlopen (libGLUName, RTLD_LAZY | RTLD_GLOBAL);
  if (libHandleGLU == NULL)
  {
      fprintf (stderr, "GLERROR: cannot access GLU library %s\n", libGLUName);
      fprintf (stderr, "GLERROR: dlerror() returns [%s]\n", dlerror());
      fflush (stderr);
      return 0;
  }

#endif

#ifdef _MAC_OS9_
        returnError =
                GetSharedLibrary("\pOpenGLLibrary",
                               kPowerPCCFragArch,
                               kReferenceCFrag,
                               &glLibConnectId,
                               &glLibMainAddr,
                   errName);
 
        if (returnError != fragNoErr)
        {
                fprintf (stderr, "GetSharedLibrary Err(%d): Ahhh!  Didn't find LIBRARY !\n",
                        returnError);
		return 0;
        }
#endif

#ifdef _MAC_OSX_
  fprintf (stderr, "GLINFO: loadGLLibrary - no special code implemented !\n");
#else
  fprintf (stderr, "GLINFO: loaded OpenGL library %s!\n", libGLName);
  fprintf (stderr, "GLINFO: loaded GLU    library %s!\n", libGLUName);
#endif
  fflush (stderr);
  
  _glLibsLoaded = 1;

  return 1;
}

void * LIBAPIENTRY getGLProcAddressHelper 
	(const char * libGLName, const char * libGLUName,
         const char *func, int *method, int debug, int verbose)
{
  void * funcPtr = NULL;
  int lmethod;

#ifdef _WIN32_
  static int __firstAccess = 1;

  if(!loadGLLibrary (libGLName, libGLUName))
  	return NULL;

  if (disp__wglGetProcAddress == NULL && __firstAccess)
  {
	  disp__wglGetProcAddress = ( PROC  (CALLBACK *)(LPCSTR) )
      	GetProcAddress (hDLL_OPENGL32, "wglGetProcAddress");

      if (disp__wglGetProcAddress != NULL /* && verbose */)
      {
			fprintf (stderr, "GLINFO: found wglGetProcAddress in %s\n", libGLName);
			fflush (NULL);
      }

      if (disp__wglGetProcAddress == NULL)
      {
	fprintf (stderr, "GLINFO: can't find wglGetProcAddress in %s\n", libGLName);
      }
  }
  __firstAccess = 0;

  if (disp__wglGetProcAddress != NULL)
	  funcPtr = disp__wglGetProcAddress (func);

  if (funcPtr == NULL)
  {
    lmethod = 2;

    funcPtr = GetProcAddress (hDLL_OPENGL32, func);
  }
  else
    lmethod = 1;

  if (funcPtr == NULL)
  {
    lmethod = 3;

    funcPtr = GetProcAddress (hDLL_OPENGLU32, func);
  }

#endif

#ifdef _X11_
  /*
   * void (*glXGetProcAddressARB(const GLubyte *procName))
   */
  if(!loadGLLibrary (libGLName, libGLUName))
  	return NULL;

  if (strncmp(func, "glu", 3))
  {
    lmethod = 2;
    funcPtr = dlsym (libHandleGL, func);
  }
  else
  {
    lmethod = 3;
    funcPtr = dlsym (libHandleGLU, func);
  }
#endif

#ifdef _MAC_OS9_
        Str255 errName;
        Str255 funcName;
        CFragSymbolClass glLibSymClass = 0;
        OSErr returnError=fragNoErr;
        #ifdef GLDEBUG
		static int firstTime = 1;
        #endif
 
        if(!loadGLLibrary (libGLName, libGLUName))
  		return NULL;

	c2pstrcpy ( funcName, func );

	#ifdef GLDEBUG
	 if(firstTime)
	 {
		PrintSymbolNamesByConnection (glLibConnectId);
		firstTime=0;
	 }
	 funcPtr = (void *)
		SeekSymbolNamesByConnection (glLibConnectId, funcName);
	#endif

	if(funcPtr==NULL)
	{
	  returnError =
	    FindSymbol (glLibConnectId, funcName,
			&funcPtr, & glLibSymClass );
	    lmethod=2;
	}
	#ifdef GLDEBUG
	 else lmethod=3;
	#endif

	if (returnError != fragNoErr)
	{
	  fprintf (stderr, "GetSharedLibrary Err(%d): Ahhh!  Didn't find SYMBOL: %s !\n",
		returnError, func);
	}
#endif

#ifdef _MAC_OSX_
    char underscoreName[256];
    strcpy(underscoreName, "_");
    strcat(underscoreName, func);
 
    if ( NSIsSymbolNameDefined(underscoreName) ) {
        NSSymbol sym = NSLookupAndBindSymbol(underscoreName);
        funcPtr = (void *)NSAddressOfSymbol(sym);
    }
 
#endif

  if (funcPtr == NULL)
  {
    if (debug || verbose)
    {
      fprintf (stderr, "GLINFO: %s (%d): not implemented !\n", func, lmethod);
      fflush (NULL);
    }
  }
  else if (verbose)
  {
    fprintf (stderr, "GLINFO: %s (%d): loaded !\n", func, lmethod);
    fflush (NULL);
  }
  if (method != NULL)
    *method = lmethod;
  return funcPtr;
}


void LIBAPIENTRY fetch_GL_FUNCS (const char * libGLName, 
			         const char * libGLUName, int force)
{
  static int _firstRun = 1;

  if(force)
  {
	unloadGLLibrary();
        _firstRun = 1;
  }

  if(!_firstRun)
  	return;

  if(!loadGLLibrary (libGLName, libGLUName))
  	return;

  #define GET_GL_PROCADDRESS(a) getGLProcAddressHelper (libGLName, libGLUName, (SYMBOL_PREFIX a), NULL, 1, 0);

  #include "gl-disp-fetch.hc"
  #include "glu-disp-fetch.hc"

  _firstRun=0;

#ifdef _X11_
  fetch_GLX_FUNCS (libGLName, libGLUName, force);
#endif

#ifdef _WIN32_
  fetch_WGL_FUNCS (libGLName, libGLUName, force);
#endif

}

#ifdef _MAC_OS9_

#ifdef GLDEBUG

static void PrintSymbolNamesByConnection (CFragConnectionID myConnID)
{
       long           myIndex;
       long           myCount;       /*number of exported symbols in fragment*/
       OSErr          myErr;
       Str255         myName;        /*symbol name*/
       Ptr            myAddr;        /*symbol address*/
       CFragSymbolClass       myClass;       /*symbol class*/
       static char buffer[256];

       myErr = CountSymbols(myConnID, &myCount);
       if (!myErr)
          for (myIndex = 1; myIndex <= myCount; myIndex++)
             {
                myErr = GetIndSymbol(myConnID, myIndex, myName, 
                                        &myAddr, &myClass);
                if (!myErr)
		{
			p2cstrcpy (buffer, myName);
								 
            fprintf(stderr, "%d/%d: class %d - name %s\n", 
		   		myIndex, myCount, myClass, buffer);
		}
             }
}

static Ptr SeekSymbolNamesByConnection (CFragConnectionID myConnID, Str255 name)
{
       long           myIndex;
       long           myCount;       /*number of exported symbols in fragment*/
       OSErr          myErr;
       Str255         myName;        /*symbol name*/
       Ptr            myAddr;        /*symbol address*/
       CFragSymbolClass       myClass;       /*symbol class*/

       myErr = CountSymbols(myConnID, &myCount);
       if (!myErr)
          for (myIndex = 1; myIndex <= myCount; myIndex++)
             {
                myErr = GetIndSymbol(myConnID, myIndex, myName, 
                                        &myAddr, &myClass);
                if (!myErr && EqualString (myName, name, true, true) == true )
			return myAddr;
             }
}

#endif /* ifndef GLDEBUG */

#endif /* ifdef _MAC_OS9_ */

