/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2006)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2006)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "visu_OSInterface.h"

#include <stdio.h>
#include <stdlib.h>

#include <X11/Xlib.h>
#include <GL/glx.h>

/* Include from glxproto.h */
/* #define GLXBadContext		0 */
/* #define GLXBadContextState	1 */
/* #define GLXBadDrawable		2 */
/* #define GLXBadPixmap		3 */
/* #define GLXBadContextTag	4 */
/* #define GLXBadCurrentWindow	5 */
/* #define GLXBadRenderRequest	6 */
/* #define GLXBadLargeRequest	7 */
/* #define GLXUnsupportedPrivateRequest	8 */
/* #define GLXBadFBConfig		9 */
/* #define GLXBadPbuffer		10 */
/* #define GLXBadCurrentDrawable	11 */
/* #define GLXBadWindow		12 */

#include <visu_tools.h>
#include "visu_X11.h"

static GLXContext ctxDirect;
static GLXContext ctxPixmap;

static GLXDrawable storedDrawable;
static GLXContext storedContext;

gboolean drawableIsPixmap;

struct DumpImage_struct
{
  GLXPixmap glxPixmap;
  Pixmap pixmap;
  int width, height;
};

int screenId;

int setScreen(int screen)
{
  DBG_fprintf(stderr, "Visu GLX : set screen id %d (previous was %d).\n", screen, screenId);
  screenId = screen;
  return 1;
}

int initOpenGLContext()
{
  int stereo;

  int list[] = {
    GLX_RGBA,
    GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
    GLX_DEPTH_SIZE, 1,
    GLX_DOUBLEBUFFER,
    GLX_STEREO,
    None
  };

  DBG_fprintf(stderr, "Visu GLX : init OpenGl context.\n");

  if (!glXQueryExtension(dpy, 0, 0))
    {
      g_warning("No GLX extension.\nYour X server"
		" does not support OpenGL extension. Please contact your"
		" system administrator to ask him to add the 'glx'"
		" extension to your X server.\n");
      return 0;
    }

  if (!screenId)
    screenId = DefaultScreen(dpy);
  stereo = 1;
  if ( (vinfo = glXChooseVisual(dpy, screenId, list)) == NULL )
    {
      stereo = 0;
      list[10] = None; 
      if ( (vinfo = glXChooseVisual(dpy, screenId, list)) == NULL )
	{
	  g_warning("Cannot find a visual.\n"
		    "Have you enough right access on the X server?");
	  return 0;
	}
    }
  if ( (ctxDirect = glXCreateContext(dpy, vinfo, 0, GL_TRUE)) == 0 )
    {
      g_warning("Cannot create direct GLX context.\n");
      return 0;
    }
  
  storedDrawable = None;
  storedContext = NULL;
  
  drawableIsPixmap = FALSE;

  DBG_fprintf(stderr, "Visu GLX : OK.\n");

  return 1;
}

gboolean OpenGLIsPixmap()
{
  return drawableIsPixmap;
}

int attachOpenGlContext(gboolean isPixmap, gpointer windowDescriptor)
{
  int res;
  GLenum errorId;
 
  XFlush(dpy); 
  res = 0;
  if (isPixmap)
    {
      if (windowDescriptor)
	res = glXMakeCurrent(dpy, *(GLXDrawable*)windowDescriptor, ctxPixmap);
      else
	{
	  fprintf(stderr, "INTERNAL ERROR! 'attachOpenGlContext' has been "
		  "called with the flag 'isPixmap' set to TRUE but no parameter"
		  " 'windowDescriptor' has been defined.\n");
	  exit(1);
	}
    }
  else
    {
      if (windowDescriptor)
	{
	  setWindowHandler(windowDescriptor);
	  res = glXMakeCurrent(dpy, (GLXDrawable)window, ctxDirect);
	  if (!res)
	    {
	      /* Print the GLX error. */
	      do
		{
		  errorId = glGetError();
		  switch (errorId)
		    {
		    case GL_NO_ERROR:
		      g_warning("glXMakeCurrent, no error.");
		      break;
		    case BadMatch:
		      g_warning("glXMakeCurrent, BadMatch error.");
		      break;
		    case BadAccess:
		      g_warning("glXMakeCurrent, BadAccess error.");
		      break;
/* 		    case GLXBadDrawable: */
/* 		      g_warning("glXMakeCurrent, BadDrawable error."); */
/* 		      break; */
/* 		    case GLXBadContext: */
/* 		      g_warning("glXMakeCurrent, BadContext error."); */
/* 		      break; */
/* 		    case GLXBadContextState: */
/* 		      g_warning("glXMakeCurrent, BadContextState error."); */
/* 		      break; */
/* 		    case GLXBadCurrentWindow: */
/* 		      g_warning("glXMakeCurrent, BadCurrentWindow error."); */
/* 		      break; */
		    case BadAlloc:
		      g_warning("glXMakeCurrent, BadAlloc error.");
		      break;
		    default:
		      g_warning("glXMakeCurrent, other error.");
		      fprintf(stderr, " | error code %d.\n", errorId);
		    }
		}
	      while (errorId != GL_NO_ERROR);
	    }
	}
      else
	{
	  fprintf(stderr, "INTERNAL ERROR! 'attachOpenGlContext' has been "
		  "called with 'windowDescriptor' paramater set to NULL but"
		  " no default handler has been defined.\n");
	  exit(1);
	}
    }

  drawableIsPixmap = isPixmap;

  if (!res)
    {
      g_warning("Cannot attach GLX context to X window\n");
      return 0;
    }
  DBG_fprintf(stderr, "Visu GLX : Set the current context to pixmap(1)"
	      " or to window(0) : %d.\n", isPixmap);
  return 1;
}

DumpImage* dumpImageNew(int width, int height)
{
  DumpImage *image;
  int d_depth;

  DBG_fprintf(stderr, "Visu GLX : creating a off-screen buffer (%dx%d).\n", width, height);
  image = malloc(sizeof(DumpImage));
  if (!image)
    {
      allocationProblems();
      exit(1);
    }
  d_depth = DefaultDepth(dpy, DefaultScreen(dpy));
  image->width = width;
  image->height = height;
  image->pixmap = XCreatePixmap(dpy, RootWindow(dpy, vinfo->screen), width, height, d_depth);
  if (!image->pixmap)
    {
      g_warning("Cannot allocate a Pixmap for the indirect rendering:\n"
		"Window dump cancelled\n");
      return (DumpImage*)0;
    }
  image->glxPixmap = glXCreateGLXPixmap(dpy, vinfo, image->pixmap);
  if (!image->glxPixmap)
    {
      g_warning("Cannot allocate a GLXPixmap for the indirect rendering:\n"
		"Window dump cancelled\n");
      return (DumpImage*)0;
    }
  if ( (ctxPixmap = glXCreateContext(dpy, vinfo, 0, GL_FALSE)) == 0 )
    {
      g_warning("Cannot create indirect GLX context.\n");
      return 0;
    }

  DBG_fprintf(stderr, "Visu GLX : Creating a Pixmap and a GLX"
	      " pixmap (%dx%d).\n", width, height);
  return image;
}
void dumpImageFree(DumpImage *image)
{
  if (image)
    {
      if (image->pixmap)
	XFreePixmap(dpy, image->pixmap);
      if (image->glxPixmap)
	glXDestroyGLXPixmap(dpy, image->glxPixmap);
      glXDestroyContext(dpy, ctxPixmap);
      ctxPixmap = (GLXContext)0;
      free(image);
    }
}
gpointer dumpImageGet_buffer(DumpImage *image)
{
  if (image)
    return (gpointer)&image->glxPixmap;
  else
    return (gpointer)0;
}

void callSwapGLBuffer()
{
  if (!drawableIsPixmap)
    glXSwapBuffers(dpy, window);
}

int initOSinterface()
{
  window = 0;
  screenId = 0;
  return 1;
}

int setDisplay(gpointer display)
{
  DBG_fprintf(stderr, "Visu GLX : set display id %p (previous was %p).\n",
	      display, (gpointer)dpy);
  dpy = (Display*)display;
  return 1;
}
 
int initDisplay()
{
  if ( (dpy = XOpenDisplay(0)) == 0 )
    {
      g_warning("Cannot connect to %s.\n"
		"Is there an X server running and do you have"
		" access on it?\n", XDisplayName(0));
      return 0;
    }
  return 1;
}

/* Return the display used by OpenGl. It's useful
   if extensions need to ask X for differents things. */
Display* getDisplay()
{
  return dpy;
}

void storeOpenGLContext()
{
  storedDrawable = glXGetCurrentDrawable();
  storedContext = glXGetCurrentContext();
}
void restoreOpenGLContext()
{
  int res;

  res = glXMakeCurrent(dpy, storedDrawable, storedContext);
  if (storedDrawable == window)
    drawableIsPixmap = FALSE;
  if (!res)
    g_warning("Cannot restore GLX context to previous drawable.\n");
}

int setWindowHandler(gpointer windowHandler)
{
  DBG_fprintf(stderr, "Visu GLX : set window handler to %d"
	      " (previous was %d).\n", *(int*)windowHandler, (int)window);
  window = *(Window*)windowHandler;
  return 1;
}

GLuint initOpenGLFontList()
{
  XFontStruct *fontInfo;
  unsigned int first, last;
  char name[] = 
    "-adobe-helvetica-medium-r-normal--18-180-75-75-p-98-iso8859-1";
  GLuint BASE;
       
  DBG_fprintf(stderr, "Visu GLX : initialise fonts.\n");
  if ((fontInfo = XLoadQueryFont(dpy, name)) == NULL)
    {
      g_warning("Specified font not available in gl_font_init\n"
		"Trying to use fixed font\n");
      if ((fontInfo = XLoadQueryFont(dpy, "fixed")) == NULL)
	{
	  g_warning("Fixed font not available.\n");
	  exit(1);
	}
    }

  first = fontInfo->min_char_or_byte2;
  last = fontInfo->max_char_or_byte2;

  /* this creates display lists 1 to 256 (not checked) */
  if ( (BASE = glGenLists(last+1)) == 0 )
    {
      g_warning("Cannot build font display lists.\n");
      exit(1);
    }
   
  glXUseXFont(fontInfo->fid, first, last-first+1, BASE+first);

  return BASE;
}
