//-----------------------------------------------------------------------------
// Torque Game Engine 
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------

#include "platform/types.h"
#if defined(TORQUE_OS_MAC_OSX)
// AAAAAAAAARGGGH.
// OSX Frameworks version of AGL.h includes CoreServices, which hits the
// OpenTransport-has-a-new-but-platform.h-#defs-new-to-something-nonstandard
// issue, so we need to pre-include OT here...
#include <OpenTransport.h>
#endif

#include "PlatformMacCarb/platformMacCarb.h"
#include "PlatformMacCarb/platformGL.h"
#include "dgl/dgl.h"
#include "console/console.h"

#include <AGL/agl.h>

GLState gGLState;

bool hwIsaRadeon = false; // for detecting Radeon-brand (R, R8500, R7500, R7200, etc...) boards.
bool hwIsaR200 = false; // for detecting R200 (R8500, R8800, etc...) or later boards.

bool gOpenGLDisablePT          = false;
bool gOpenGLDisableCVA         = false;
bool gOpenGLDisableTEC         = false;
bool gOpenGLDisableARBMT       = false;
bool gOpenGLDisableFC          = true; //false;
bool gOpenGLDisableTCompress   = false;
bool gOpenGLNoEnvColor         = false;
float gOpenGLGammaCorrection   = 0.5;
bool gOpenGLNoDrawArraysAlpha  = false;

// !!!!!TBD -- What to do about missing functions??  are they in 1.3???
#define HANDLE_MISSING_EXTS		1
#if HANDLE_MISSING_EXTS // until we decide how to actually implement these...

/*
GLboolean mglAvailVB() {   return(false); }
GLint mglAllocVB(GLsizei size, GLint format, GLboolean preserve) {   return(0); }
void* mglLockVB(GLint handle, GLsizei size) {   return(NULL); }
void mglUnlockVB(GLint handle) {}
void mglSetVB(GLint handle) {}
void mglOffsetVB(GLint handle, GLuint offset) {}
void mglFillVB(GLint handle, GLint first, GLsizei count) {}
void mglFreeVB(GLint handle) {}
void mglFogCP(GLenum type, GLsizei stride, const GLvoid *pointer) {}
void mglColorTable(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table) {}
*/
// maybe we don't need the extern C?
#if GL_EXTERN_C
extern "C"
{
#endif
GLboolean glAvailableVertexBufferEXT() {   return(false); }
GLint glAllocateVertexBufferEXT(GLsizei size, GLint format, GLboolean preserve) {   return(0); }
void* glLockVertexBufferEXT(GLint handle, GLsizei size) {   return(NULL); }
void glUnlockVertexBufferEXT(GLint handle) {}
void glSetVertexBufferEXT(GLint handle) {}
void glOffsetVertexBufferEXT(GLint handle, GLuint offset) {}
void glFillVertexBufferEXT(GLint handle, GLint first, GLsizei count) {}
void glFreeVertexBufferEXT(GLint handle) {}

// if the colortable stuff isn't declared, or we're building in PB but against < GL1.3, stub the colortable fn.
#if !defined(GL_EXT_fog_coord) || !defined(__APPLE__) || !defined(GL_VERSION_1_3)
void glFogCoordPointerEXT(GLenum type, GLsizei stride, const GLvoid *pointer) {}
#endif

// if the colortable stuff isn't declared, or we're building in PB but against < GL1.3, stub the colortable fn.
#if !defined(GL_EXT_paletted_texture) || !defined(__APPLE__) || !defined(GL_VERSION_1_3)
void glColorTableEXT(GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const GLvoid *table) {}
#endif
#if GL_EXTERN_C
}
#endif
#endif // HANDLE_MISSING_EXTS


// Load extensions.  !!!TBD rename the darn function already.
bool GL_EXT_Init( )
{
   OSStatus err = 0;

   // we only really need two of the strings.
   const char* pExtString = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
   const char* pRendString = reinterpret_cast<const char*>(glGetString(GL_RENDERER));

   // OpenGL Exists ========================================
   if (glBegin == (void *)kUnresolvedCFragSymbolAddress)
   { // !!!!!!! TBD
      Con::printf("OpenGL Init: Failed to find OpenGL system library.");
      return(false);
   }
   
   // pre-clear the structure!!!
   dMemset(&gGLState, 0, sizeof(gGLState));

   // early hardware detection.  // !!!TBD update for new boards.
   hwIsaRadeon = false;
   hwIsaR200 = false;
   if (pRendString && dStrstr(pRendString, (const char*)"ATI ") != NULL)
   { // we know we're on an ATI renderer at least.  now, which one??
      // first, check for 8500 variations
      if (dStrstr(pRendString, (const char*)"ATI R-") != NULL // that way, we'll catch R-300...
      ||  dStrstr(pRendString, (const char*)"ATI R200") != NULL
      ||  dStrstr(pRendString, (const char*)"ATI Radeon 8") != NULL) // catch 8500, 8800, etc.
      {
         hwIsaR200 = true;
         hwIsaRadeon = true; // if is an R200, it's >= Radeon.
      }
      else // wasn't an R200+, let's just look for Radeon
      if (dStrstr(pRendString, (const char*)"ATI Radeon"))
      {
         hwIsaRadeon = true;
      }
   }
   
   // EXT_paletted_texture ========================================
//   glColorTableEXT = NULL;
   if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_paletted_texture") != NULL)
      gGLState.suppPalettedTexture = true;
   else
      gGLState.suppPalettedTexture = false;
   
   // EXT_compiled_vertex_array ========================================
/*
   glLockArraysEXT   = NULL;
   glUnlockArraysEXT = NULL;
*/
   gGLState.suppLockedArrays = false;
   if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_compiled_vertex_array") != NULL)
      gGLState.suppLockedArrays = true;
//actually, OS9 crashes too.  Specifically in Shadow::render(), so I patched it there for now.
//   gOpenGLDisableCVA = true;
// HMMMM. !!!!TBD -- looks like it crashes in terrain editor as well.

   // ARB_multitexture ========================================
/*
      glActiveTextureARB       = NULL;
      glClientActiveTextureARB = NULL;
      glMultiTexCoord2fARB     = NULL;
      glMultiTexCoord2fvARB    = NULL;
*/
   if (pExtString && dStrstr(pExtString, (const char*)"GL_ARB_multitexture") != NULL)
      gGLState.suppARBMultitexture = true;
   else
      gGLState.suppARBMultitexture = false;
   
   // EXT_blend_color
   if(pExtString && dStrstr(pExtString, (const char*)"GL_EXT_blend_color") != NULL)
      gGLState.suppEXTblendcolor = true;
   else
      gGLState.suppEXTblendcolor = false;

   // EXT_blend_minmax
   if(pExtString && dStrstr(pExtString, (const char*)"GL_EXT_blend_minmax") != NULL)
      gGLState.suppEXTblendminmax = true;
   else
      gGLState.suppEXTblendminmax = false;

   // NV_vertex_array_range ========================================
/*
      glVertexArrayRangeNV      = dllVertexArrayRangeNV      = NULL;
      glFlushVertexArrayRangeNV = dllFlushVertexArrayRangeNV = NULL;
*/
/*
   wglAllocateMemoryNV       = NULL;
   wglFreeMemoryNV           = NULL;
*/
   if (pExtString && dStrstr(pExtString, (const char*)"GL_NV_vertex_array_range") != NULL)
      gGLState.suppVertexArrayRange = true;
   else
      gGLState.suppVertexArrayRange = false;
   

   // EXT_fog_coord ========================================
/*
      glFogCoordfEXT       = NULL;
      glFogCoordPointerEXT = NULL;
*/
   if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_fog_coord") != NULL)
      gGLState.suppFogCoord = true;
   else
      gGLState.suppFogCoord = false;
   
   // ARB_texture_compression ========================================
/*
      glCompressedTexImage3DARB    = NULL;
      glCompressedTexImage2DARB    = NULL;
      glCompressedTexImage1DARB    = NULL;
      glCompressedTexSubImage3DARB = NULL;
      glCompressedTexSubImage2DARB = NULL;
      glCompressedTexSubImage1DARB = NULL;
      glGetCompressedTexImageARB   = NULL;
*/
   if (pExtString && dStrstr(pExtString, (const char*)"GL_ARB_texture_compression") != NULL)
      gGLState.suppTextureCompression = true;
   else
      gGLState.suppTextureCompression = false;
   

   // 3DFX_texture_compression_FXT1 ========================================
   if (pExtString && dStrstr(pExtString, (const char*)"GL_3DFX_texture_compression_FXT1") != NULL)
      gGLState.suppFXT1 = true;
   else
      gGLState.suppFXT1 = false;


   // EXT_texture_compression_S3TC ========================================
   if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_texture_compression_s3tc") != NULL)
      gGLState.suppS3TC = true;
   else
      gGLState.suppS3TC = false;


   // WGL_3DFX_gamma_control ========================================
/*
      qwglGetDeviceGammaRamp3DFX = NULL;
      qwglSetDeviceGammaRamp3DFX = NULL;
*/
   if (pExtString && dStrstr(pExtString, (const char*)"WGL_3DFX_gamma_control" ) != NULL)
   { 
//      qwglGetDeviceGammaRamp3DFX = (qwglGetDeviceGammaRamp3DFX_t) qwglGetProcAddress( "wglGetDeviceGammaRamp3DFX" ); 
//     qwglSetDeviceGammaRamp3DFX = (qwglSetDeviceGammaRamp3DFX_t) qwglGetProcAddress( "wglSetDeviceGammaRamp3DFX" );
   }
   else
   {
   }


   // EXT_vertex_buffer ========================================
/*
      glAvailableVertexBufferEXT   = NULL;
      glAllocateVertexBufferEXT   = NULL;
      glLockVertexBufferEXT      = NULL;
      glUnlockVertexBufferEXT      = NULL;
      glSetVertexBufferEXT         = NULL;
      glOffsetVertexBufferEXT      = NULL;
      glFillVertexBufferEXT      = NULL;
      glFreeVertexBufferEXT      = NULL;
*/
   if (pExtString && dStrstr(pExtString, (const char*)"GL_EXT_vertex_buffer") != NULL)
   {
      gGLState.suppVertexBuffer = true;
      AssertWarn(gGLState.suppVertexBuffer == false, "We're assuming no vertex bufffer support on Mac for now!");
   }
   else
      gGLState.suppVertexBuffer = false;

   // Binary states, i.e., no supporting functions  ========================================
   // NOTE:
   // Some of these have multiple representations, via EXT and|or ARB and|or NV and|or SGIS ... etc.
   // Check all relative versions.
   
   gGLState.suppPackedPixels      = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_packed_pixels") != NULL) : false;
   gGLState.suppPackedPixels     |= pExtString? (dStrstr(pExtString, (const char*)"GL_APPLE_packed_pixel") != NULL) : false;

   gGLState.suppTextureEnvCombine = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_env_combine") != NULL) : false;
   gGLState.suppTextureEnvCombine|= pExtString? (dStrstr(pExtString, (const char*)"GL_ARB_texture_env_combine") != NULL) : false;

   gGLState.suppEdgeClamp         = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_edge_clamp") != NULL) : false;
   // whoops.  there's another things to check for.
   gGLState.suppEdgeClamp        |= pExtString? (dStrstr(pExtString, (const char*)"GL_SGIS_texture_edge_clamp") != NULL) : false;
   gGLState.suppEdgeClamp        |= pExtString? (dStrstr(pExtString, (const char*)"GL_ARB_texture_border_clamp") != NULL) : false;

   gGLState.suppTexEnvAdd         = pExtString? (dStrstr(pExtString, (const char*)"GL_ARB_texture_env_add") != NULL) : false;
   gGLState.suppTexEnvAdd        |= pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_env_add") != NULL) : false;

   // Anisotropic filtering ========================================
   gGLState.suppTexAnisotropic    = pExtString? (dStrstr(pExtString, (const char*)"GL_EXT_texture_filter_anisotropic") != NULL) : false;
   if (gGLState.suppTexAnisotropic)
      glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gGLState.maxAnisotropy);


   // Texture combine units  ========================================
   if (gGLState.suppARBMultitexture)
      glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &gGLState.maxTextureUnits);
   else
      gGLState.maxTextureUnits = 1;


   // Swap interval ========================================
//   if (pExtString && dStrstr(pExtString, (const char*)"WGL_EXT_swap_control") != NULL)
//      gGLState.suppSwapInterval = true; //( qwglSwapIntervalEXT != NULL );
//   else
// Mac inherently supports a swap interval AGL-set-integer extension.
      gGLState.suppSwapInterval = true;


   // FSAA support, currently Radeon++/ATI only.
   gGLState.maxFSAASamples = 0;
   if (hwIsaRadeon)
   {
      // default to off.
      gGLState.maxFSAASamples = 1;
   }

   // console out all the extensions...
   Con::printf("OpenGL Init: Enabled Extensions");
   if (gGLState.suppARBMultitexture)    Con::printf("  ARB_multitexture (Max Texture Units: %d)", gGLState.maxTextureUnits);
   if (gGLState.suppEXTblendcolor)      Con::printf("  EXT_blend_color");
   if (gGLState.suppEXTblendminmax)     Con::printf("  EXT_blend_minmax");
   if (gGLState.suppPalettedTexture)    Con::printf("  EXT_paletted_texture");
   if (gGLState.suppLockedArrays)       Con::printf("  EXT_compiled_vertex_array");
   if (gGLState.suppVertexArrayRange)   Con::printf("  NV_vertex_array_range");
   if (gGLState.suppTextureEnvCombine)  Con::printf("  EXT_texture_env_combine");
   if (gGLState.suppPackedPixels)       Con::printf("  EXT_packed_pixels");
   if (gGLState.suppFogCoord)           Con::printf("  EXT_fog_coord");
   if (gGLState.suppTextureCompression) Con::printf("  ARB_texture_compression");
   if (gGLState.suppS3TC)               Con::printf("  EXT_texture_compression_s3tc");
   if (gGLState.suppFXT1)               Con::printf("  3DFX_texture_compression_FXT1");
   if (gGLState.suppTexEnvAdd)          Con::printf("  (ARB|EXT)_texture_env_add");
   if (gGLState.suppTexAnisotropic)     Con::printf("  EXT_texture_filter_anisotropic (Max anisotropy: %f)", gGLState.maxAnisotropy);
   if (gGLState.suppSwapInterval)       Con::printf("  WGL_EXT_swap_control");
   if (gGLState.maxFSAASamples)         Con::printf("  ATI_FSAA");

   Con::warnf("OpenGL Init: Disabled Extensions");
   if (!gGLState.suppARBMultitexture)    Con::warnf("  ARB_multitexture");
   if (!gGLState.suppEXTblendcolor)      Con::warnf("  EXT_blend_color");
   if (!gGLState.suppEXTblendminmax)     Con::warnf("  EXT_blend_minmax");
   if (!gGLState.suppPalettedTexture)    Con::warnf("  EXT_paletted_texture");
   if (!gGLState.suppLockedArrays)       Con::warnf("  EXT_compiled_vertex_array");
   if (!gGLState.suppVertexArrayRange)   Con::warnf("  NV_vertex_array_range");
   if (!gGLState.suppTextureEnvCombine)  Con::warnf("  EXT_texture_env_combine");
   if (!gGLState.suppPackedPixels)       Con::warnf("  EXT_packed_pixels");
   if (!gGLState.suppFogCoord)           Con::warnf("  EXT_fog_coord");
   if (!gGLState.suppTextureCompression) Con::warnf("  ARB_texture_compression");
   if (!gGLState.suppS3TC)               Con::warnf("  EXT_texture_compression_s3tc");
   if (!gGLState.suppFXT1)               Con::warnf("  3DFX_texture_compression_FXT1");
   if (!gGLState.suppTexEnvAdd)          Con::warnf("  (ARB|EXT)_texture_env_add");
   if (!gGLState.suppTexAnisotropic)     Con::warnf("  EXT_texture_filter_anisotropic");
   if (!gGLState.suppSwapInterval)       Con::warnf("  WGL_EXT_swap_control");
   if (!gGLState.maxFSAASamples)         Con::warnf("  ATI_FSAA");
   Con::printf("");

   // Set some console variables:
   Con::setBoolVariable( "$FogCoordSupported", gGLState.suppFogCoord );
   Con::setBoolVariable( "$TextureCompressionSupported", gGLState.suppTextureCompression );
   Con::setBoolVariable( "$AnisotropySupported", gGLState.suppTexAnisotropic );
   Con::setBoolVariable( "$PalettedTextureSupported", gGLState.suppPalettedTexture );
   Con::setBoolVariable( "$SwapIntervalSupported", gGLState.suppSwapInterval );

   if (!gGLState.suppPalettedTexture && Con::getBoolVariable("$pref::OpenGL::forcePalettedTexture",false))
   {
      Con::setBoolVariable("$pref::OpenGL::forcePalettedTexture", false);
      Con::setBoolVariable("$pref::OpenGL::force16BitTexture", true);
   }

   if (gGLState.maxFSAASamples>1)
   {
      gFSAASamples = Con::getIntVariable("$pref::OpenGL::numFSAASamples", gGLState.maxFSAASamples<<1);
      dglSetFSAASamples(gFSAASamples);
   }

   return true;
}

// this is currently only for Radeon++ ATI boards
// pass in 1 for normal/no-AA, 2 for 2x-AA, 4 for 4x-AA.
void dglSetFSAASamples(GLint aasamp)
{
   if (gGLState.maxFSAASamples<2) return;
   if (!hwIsaRadeon) return; // sanity check.

   // for the moment, don't assume values passed in are capped min/max.
   if (aasamp<1) aasamp = 1;
   else
   if (aasamp==3) aasamp = 2;
   else
   if (aasamp>4) aasamp = 4;

   // !!!!TBD method for OSX invocation.
   aglSetInteger((AGLContext)platState.ctx, AGLSETINT_ATI_FSAA_SAMPLES, (const long*)&aasamp);
   
   Con::printf(">>Number of FSAA samples is now [%d].", aasamp);
   Con::setIntVariable("$pref::OpenGL::numFSAASamples", aasamp);
   if (Con::getIntVariable("$pref::TradeshowDemo", 0))
      Con::executef(2, "setFSAABadge", aasamp>1?"true":"false");
}
