#include <inttypes.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <GL/glx.h>
/* Linux headers don't work properly */
#define __USE_GNU
#include <dlfcn.h>
#undef __USE_GNU

/* HP-UX doesn't define RTLD_DEFAULT. */
#if defined(_HPUX) && !defined(RTLD_DEFAULT)
#define RTLD_DEFAULT NULL
#endif

#include <string.h>

/* We expect glXGetProcAddressARB to be defined */
extern void (*glXGetProcAddressARB(const GLubyte *procname))();

/**
 * Java->C glue code:
 *   Java package: jogamp.opengl.x11.glx.GLX
 *    Java method: int glXGetFBConfigAttributes(long dpy, long config, IntBuffer attributes, IntBuffer values)
 */
JNIEXPORT jint JNICALL 
Java_jogamp_opengl_x11_glx_GLX_dispatch_1glXGetFBConfigAttributes(JNIEnv *env, jclass _unused, jlong dpy, jlong config, jint attributeCount, jobject attributes, jint attributes_byte_offset, jobject values, jint values_byte_offset, jlong procAddress) {
  typedef int (APIENTRY*_local_PFNGLXGETFBCONFIGATTRIBPROC)(Display *  dpy, GLXFBConfig config, int attribute, int *  value);
  _local_PFNGLXGETFBCONFIGATTRIBPROC ptr_glXGetFBConfigAttrib = (_local_PFNGLXGETFBCONFIGATTRIBPROC) (intptr_t) procAddress;
  assert(ptr_glXGetFBConfigAttrib != NULL);

  int err = 0;
  if ( attributeCount > 0 && NULL != attributes ) {
    int i;
    int * attributes_ptr = (int *) (((char*) (*env)->GetDirectBufferAddress(env, attributes)) + attributes_byte_offset);
    int * values_ptr = (int *) (((char*) (*env)->GetDirectBufferAddress(env, values)) + values_byte_offset);
    for(i=0; 0 == err && i<attributeCount; i++) {
        err = (* ptr_glXGetFBConfigAttrib) ((Display *) (intptr_t) dpy, (GLXFBConfig) (intptr_t) config, attributes_ptr[i], &values_ptr[i]);
    }
    if( 0 != err ) {
        values_ptr[0] = i;
    }
  }
  return (jint)err;
}

/*   Java->C glue code:
 *   Java package: jogamp.opengl.x11.glx.GLX
 *    Java method: XVisualInfo glXGetVisualFromFBConfig(long dpy, long config)
 *     C function: XVisualInfo *  glXGetVisualFromFBConfig(Display *  dpy, GLXFBConfig config);
 */
JNIEXPORT jobject JNICALL 
Java_jogamp_opengl_x11_glx_GLX_dispatch_1glXGetVisualFromFBConfig(JNIEnv *env, jclass _unused, jlong dpy, jlong config, jlong procAddress) {
  typedef XVisualInfo* (APIENTRY*_local_PFNGLXGETVISUALFROMFBCONFIG)(Display *  dpy, GLXFBConfig config);
  _local_PFNGLXGETVISUALFROMFBCONFIG ptr_glXGetVisualFromFBConfig;
  XVisualInfo *  _res;
  jobject jbyteCopy;
  ptr_glXGetVisualFromFBConfig = (_local_PFNGLXGETVISUALFROMFBCONFIG) (intptr_t) procAddress;
  assert(ptr_glXGetVisualFromFBConfig != NULL);
  _res = (* ptr_glXGetVisualFromFBConfig) ((Display *) (intptr_t) dpy, (GLXFBConfig) (intptr_t) config);
  if (_res == NULL) return NULL;

  jbyteCopy   = JVMUtil_NewDirectByteBufferCopy(env, _res, sizeof(XVisualInfo));
  XFree(_res);

  return jbyteCopy;
}

/*   Java->C glue code:
 *   Java package: jogamp.opengl.x11.glx.GLX
 *    Java method: com.jogamp.common.nio.PointerBuffer dispatch_glXChooseFBConfig(long dpy, int screen, java.nio.IntBuffer attribList, java.nio.IntBuffer nitems)
 *     C function: GLXFBConfig *  glXChooseFBConfig(Display *  dpy, int screen, const int *  attribList, int *  nitems);
 */
JNIEXPORT jobject JNICALL 
Java_jogamp_opengl_x11_glx_GLX_dispatch_1glXChooseFBConfig(JNIEnv *env, jclass _unused, jlong dpy, jint screen, jobject attribList, jint attribList_byte_offset, jobject nitems, jint nitems_byte_offset, jlong procAddress) {
  typedef GLXFBConfig *  (APIENTRY*_local_PFNGLXCHOOSEFBCONFIGPROC)(Display *  dpy, int screen, const int *  attribList, int *  nitems);
  _local_PFNGLXCHOOSEFBCONFIGPROC ptr_glXChooseFBConfig;
  int * _attribList_ptr = NULL;
  int * _nitems_ptr = NULL;
  GLXFBConfig *  _res;
  int count, i;
  jobject jbyteCopy;
    if ( NULL != attribList ) {
        _attribList_ptr = (int *) (((char*) (*env)->GetDirectBufferAddress(env, attribList)) + attribList_byte_offset);
    }
    if ( NULL != nitems ) {
        _nitems_ptr = (int *) (((char*) (*env)->GetDirectBufferAddress(env, nitems)) + nitems_byte_offset);
    }
  ptr_glXChooseFBConfig = (_local_PFNGLXCHOOSEFBCONFIGPROC) (intptr_t) procAddress;
  assert(ptr_glXChooseFBConfig != NULL);
  _res = (* ptr_glXChooseFBConfig) ((Display *) (intptr_t) dpy, (int) screen, (int *) _attribList_ptr, (int *) _nitems_ptr);
  count = _nitems_ptr[0];
  if (NULL == _res) return NULL;

  /**
   * Bug 961: Validate returned 'GLXFBConfig *', i.e. remove NULL pointer. 
   * Note: sizeof(GLXFBConfig) == sizeof(void*), a.k.a a pointer
   */
  // fprintf(stderr, "glXChooseFBConfig.0: Count %d\n", count);
  i=0;
  while( i < count ) {
    if( NULL == _res[i] ) {
       if( 0 < count-i-1 ) {
         memmove(_res+i, _res+i+1, (count-i-1)*sizeof(GLXFBConfig)); 
       }
       count--;
    } else {
       i++;
    }
  }
  // fprintf(stderr, "glXChooseFBConfig.X: Count %d\n", count);

  jbyteCopy   = JVMUtil_NewDirectByteBufferCopy(env, _res, count * sizeof(GLXFBConfig));
  XFree(_res);

  return jbyteCopy;
}

/*   Java->C glue code:
 *   Java package: jogamp.opengl.x11.glx.GLX
 *    Java method: com.jogamp.common.nio.PointerBuffer dispatch_glXGetFBConfigs(long dpy, int screen, java.nio.IntBuffer nelements)
 *     C function: GLXFBConfig *  glXGetFBConfigs(Display *  dpy, int screen, int *  nelements);
 */
JNIEXPORT jobject JNICALL 
Java_jogamp_opengl_x11_glx_GLX_dispatch_1glXGetFBConfigs(JNIEnv *env, jclass _unused, jlong dpy, jint screen, jobject nelements, jint nelements_byte_offset, jlong procAddress) {
  typedef GLXFBConfig *  (APIENTRY*_local_PFNGLXGETFBCONFIGSPROC)(Display *  dpy, int screen, int *  nelements);
  _local_PFNGLXGETFBCONFIGSPROC ptr_glXGetFBConfigs;
  int * _nelements_ptr = NULL;
  GLXFBConfig *  _res;
  int count, i;
  jobject jbyteCopy;
    if ( NULL != nelements ) {
        _nelements_ptr = (int *) (((char*) (*env)->GetDirectBufferAddress(env, nelements)) + nelements_byte_offset);
    }
  ptr_glXGetFBConfigs = (_local_PFNGLXGETFBCONFIGSPROC) (intptr_t) procAddress;
  assert(ptr_glXGetFBConfigs != NULL);
  _res = (* ptr_glXGetFBConfigs) ((Display *) (intptr_t) dpy, (int) screen, (int *) _nelements_ptr);
  count = _nelements_ptr[0];
  if (NULL == _res) return NULL;

  /**
   * Bug 961: Validate returned 'GLXFBConfig *', i.e. remove NULL pointer. 
   * Note: sizeof(GLXFBConfig) == sizeof(void*), a.k.a a pointer
   */
  i=0;
  while( i < count ) {
    if( NULL == _res[i] ) {
       if( 0 < count-i-1 ) {
         memmove(_res+i, _res+i+1, (count-i-1)*sizeof(GLXFBConfig));
       }
       count--;
    } else {
       i++;
    }
  }

  jbyteCopy   = JVMUtil_NewDirectByteBufferCopy(env, _res, count * sizeof(GLXFBConfig));
  XFree(_res);

  return jbyteCopy;
}


/*   Java->C glue code:
 *   Java package: jogamp.opengl.x11.glx.GLX
 *    Java method: XVisualInfo dispatch_glXChooseVisual(long dpy, int screen, java.nio.IntBuffer attribList)
 *     C function: XVisualInfo *  glXChooseVisual(Display *  dpy, int screen, int *  attribList);
 */
JNIEXPORT jobject JNICALL 
Java_jogamp_opengl_x11_glx_GLX_dispatch_1glXChooseVisual(JNIEnv *env, jclass _unused, jlong dpy, jint screen, jobject attribList, jint attribList_byte_offset, jlong procAddress) {
  typedef XVisualInfo *  (APIENTRY*_local_PFNGLXCHOOSEVISUALPROC)(Display *  dpy, int screen, int *  attribList);
  _local_PFNGLXCHOOSEVISUALPROC ptr_glXChooseVisual;
  int * _attribList_ptr = NULL;
  XVisualInfo *  _res;
  jobject jbyteCopy;
    if ( NULL != attribList ) {
        _attribList_ptr = (int *) (((char*) (*env)->GetDirectBufferAddress(env, attribList)) + attribList_byte_offset);
    }
  ptr_glXChooseVisual = (_local_PFNGLXCHOOSEVISUALPROC) (intptr_t) procAddress;
  assert(ptr_glXChooseVisual != NULL);
  _res = (* ptr_glXChooseVisual) ((Display *) (intptr_t) dpy, (int) screen, (int *) _attribList_ptr);
  if (NULL == _res) return NULL;

  jbyteCopy   = JVMUtil_NewDirectByteBufferCopy(env, _res, sizeof(XVisualInfo));
  XFree(_res);

  return jbyteCopy;
}

