/*   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 "interactive.h"

#include <GL/gl.h>
#include <GL/glu.h> 

#include "view.h"
#include "objectList.h"
#include <visu_object.h>
#include <visu_tools.h>
#include <visu_extension.h>
#include <renderingBackend/visu_actionInterface.h>
#include <visu_configFile.h>
#include <renderingBackend/visu_windowInterface.h>
#include <visu_rendering.h>

#define FLAG_PARAMETER_OBSERVE_METHOD "opengl_observe_method"
#define DESC_PARAMETER_OBSERVE_METHOD "Choose the observe method ; integer (0: constrained mode, 1: walker mode)"
int openGL_preferedObserveMethod;

struct observeData
{
  float old_theta;
  float old_phi;
  float old_omega;
  float old_xs;
  float old_ys;
  float old_gross;
  float old_d_red;
  float xi, yi;
};
struct observeData *observeValues;

/* When an atom is moved (see move()), it is associated
   to a specific extension list to avoid to recreate
   the complete allElement list. */
#define  moveAtomName        _("Moved a node")
#define  moveAtomDescription _("Draw the node that is displaced.")
OpenGLExtension *moveAtomExtension;
int moveAtomExtension_list;

/* When an atom is marked (see mark()), it is added to a specific extension
   responsible of the drawing of all marks. */
#define markAtomName        _("Mark nodes")
#define markAtomDescription _("Draw some decorations around selected nodes.")
OpenGLExtension *markAtomExtension;
int markAtomExtension_id;
GList *markAtomExtension_list;

GenericRenderingWindow windowForInteractiveMode;
callBackOnNodeSelectedFunc callbackOnNodeSelected;
gpointer locationOnNodeSelected;
GList* pickObserveCallbackId;
CallbackFunctions callbackPickObserve;

/* Local methods. */
static gboolean exportParameters(GString *data, int *nbLinesWritten);
gboolean readOpenGLObserveMethod(gchar **lines, int nbLines,
				 int position, GString *errorMessage);
static void rebuildListOfMarks(VisuData *dataObj);
static gboolean observe(SimplifiedEvents *ev, VisuData *data);
static gboolean pick(SimplifiedEvents *ev, VisuData *data);
static gboolean move(SimplifiedEvents *ev, VisuData *data);
static gboolean mark(SimplifiedEvents *ev, VisuData *data);
static gboolean pickAndObserve(SimplifiedEvents *ev, VisuData *data);
static void drawSelectMark(VisuData *data, VisuNode *node, float size);
VisuNode* getSelectElement(VisuData *data, int x, int y);

/* Callbacks. */
static void visuDataChanged(GObject *obj, VisuData *dataObj, gpointer bool);
static void rebuildMarkNodesOnChanges(VisuData *dataObj, gpointer data);
static void rebuildMarkNodesOnElementChanges(VisuData *dataObj, VisuElement *ele, gpointer data);



void openGLInteractive_init()
{
  VisuConfigFileEntry *resourceEntry;

  DBG_fprintf(stderr, "Interactive : initialisation.\n");

  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_PARAMETER,
					  FLAG_PARAMETER_OBSERVE_METHOD,
					  DESC_PARAMETER_OBSERVE_METHOD,
					  1, readOpenGLObserveMethod);
  visuConfigFileAdd_exportFunction(VISU_CONFIGFILE_PARAMETER,
				   exportParameters);

  /* Set local variables to default. */
  openGL_preferedObserveMethod = OPENGL_OBSERVE_CONSTRAINED;
  moveAtomExtension = (OpenGLExtension*)0;
  markAtomExtension = (OpenGLExtension*)0;
  observeValues = g_malloc(sizeof(struct observeData));
}

void openGLInteractiveSet_preferedObserveMethod(int method)
{
  if (method != OPENGL_OBSERVE_CONSTRAINED && method != OPENGL_OBSERVE_WALKER)
    {
      fprintf(stderr, "WARNING! 'openGLSet_preferedObserveMethod' has been"
	      " called with a wrong method argument.\n");
      return;
    }
  openGL_preferedObserveMethod = method;
}
int openGLInteractiveGet_preferedObserveMethod()
{
  return openGL_preferedObserveMethod;
}

static gboolean pickAndObserve(SimplifiedEvents *ev, VisuData *data)
{
  /* If button 1 or 2, use observe mode */
  if (ev->button != 3)
    return observe(ev, data);
  /* If button 3, use pick mode with button 3 as button 1 */
  else
    {
      if (ev->shiftMod)
	{
	  ev->shiftMod = 0;
	  ev->button = 2;
	}
      else
	ev->button = 1;
      
      return pick(ev, data);
    }
}

static gboolean observe(SimplifiedEvents *ev, VisuData *data)
{
  int reDrawNeeded;
  int sign_theta;
  int dx, dy;
  OpenGLView *view;
  float angles[3];

  g_return_val_if_fail(ev, TRUE);
  if (ev->button == 3 && ev->buttonType == BUTTON_TYPE_PRESS)
    return TRUE;
  /* If the realese event is triggered, we exit on ly if it's
     not a scroll event (button 4 and 5). */
  if (ev->buttonType == BUTTON_TYPE_RELEASE)
    if (ev->button != 4 && ev->button != 5 )
      return FALSE;

  g_return_val_if_fail(data, TRUE);
  view = visuDataGet_openGLView(data);

/*   fprintf(stderr, "%d %d, %d\n", ev->x, ev->y, ev->button); */
/*   fprintf(stderr, "%d %d, %d\n", ev->shiftMod, ev->controlMod, ev->motion); */
  reDrawNeeded = 0;
  if (ev->button && !ev->motion)
    {
      observeValues->xi = ev->x;
      observeValues->yi = ev->y;
      /* Support de la roulette en zoom et perspective. */
      if (ev->button == 4 && !ev->shiftMod)
	reDrawNeeded = visuDataSet_zoomOfView(data, view->camera->gross * 1.1);
      else if (ev->button == 4 && ev->shiftMod)
	reDrawNeeded = visuDataSet_perspectiveOfView(data, view->camera->d_red / 1.1);
      else if (ev->button == 5 && !ev->shiftMod)
	reDrawNeeded = visuDataSet_zoomOfView(data, view->camera->gross / 1.1);
      else if (ev->button == 5 && ev->shiftMod)
	reDrawNeeded = visuDataSet_perspectiveOfView(data, view->camera->d_red * 1.1);
    }
  else if (ev->motion)
    {
      dx =   ev->x - observeValues->xi;
      dy = -(ev->y - observeValues->yi);
      if(ev->button == 1 && !ev->shiftMod && !ev->controlMod)
	{
	  if (openGL_preferedObserveMethod == OPENGL_OBSERVE_CONSTRAINED)
	    {
	      if(view->camera->theta > 0.0)
		sign_theta = 1;
	      else
		sign_theta = -1;
	      openGLViewRotate_box(view, dy * 180.0f / view->window->height,
				   -dx * 180.0f / view->window->width * sign_theta, angles);
	      reDrawNeeded = visuDataSet_angleOfView(data, angles[0], angles[1],
						     0., MASK_THETA | MASK_PHI);
	    }
	  else if (openGL_preferedObserveMethod == OPENGL_OBSERVE_WALKER)
	    {
	      openGLViewRotate_camera(view, dy * 180.0f / view->window->height,
				      -dx * 180.0f / view->window->width, angles);
	      reDrawNeeded = visuDataSet_angleOfView(data, angles[0], angles[1], angles[2],
						     MASK_THETA | MASK_PHI | MASK_OMEGA);
	    }
	}
      else if(ev->button == 1 && ev->shiftMod && !ev->controlMod)
	reDrawNeeded =
	  visuDataSet_positionOfView(data, view->camera->xs + (float)dx / view->window->width /
				     view->camera->gr / view->camera->gross,
				     view->camera->ys + (float)dy / view->window->height /
				     view->camera->gr / view->camera->gross,
				     MASK_XS | MASK_YS);
      else if(ev->button == 1 && ev->controlMod && !ev->shiftMod)
	{
	  if (abs(dx) > abs(dy))
	    reDrawNeeded = visuDataSet_angleOfView(data, 0., 0.,
						   view->camera->omega + dx *
						   180.0f / view->window->width, MASK_OMEGA);
	  else
	    reDrawNeeded = visuDataSet_angleOfView(data, 0., 0.,
						   view->camera->omega + dy *
						   180.0f / view->window->height, MASK_OMEGA);
	}
      else if(ev->button == 2 && !ev->shiftMod)
	reDrawNeeded = visuDataSet_zoomOfView(data, view->camera->gross *
					      (1. + (float)dy *
					       3.0f / view->window->height));
      else if(ev->button == 2 && ev->shiftMod)
	reDrawNeeded = visuDataSet_perspectiveOfView(data, view->camera->d_red *
						     (1. - (float)dy *
						      5.0f / view->window->height));
      observeValues->xi = ev->x;
      observeValues->yi = ev->y;
    }
  else if (ev->letter == 'r')
    {
      reDrawNeeded = visuDataSet_angleOfView(data, observeValues->old_theta,
					     observeValues->old_phi,
					     observeValues->old_omega,
					     MASK_THETA | MASK_PHI | MASK_OMEGA) ||
	reDrawNeeded;
      reDrawNeeded = visuDataSet_positionOfView(data, observeValues->old_xs,
						observeValues->old_ys,
						MASK_XS | MASK_YS) ||
	reDrawNeeded;
      reDrawNeeded = visuDataSet_zoomOfView(data, observeValues->old_gross) ||
	reDrawNeeded;
      reDrawNeeded = visuDataSet_perspectiveOfView(data, observeValues->old_d_red) ||
	reDrawNeeded;
    }
  if (reDrawNeeded)
    g_signal_emit(visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		  0 , NULL);
  return FALSE;
}

void openGLInteractiveInit_session(GenericRenderingWindow window,
				   callBackOnNodeSelectedFunc handler, gpointer data,
				   GDestroyNotify stopPickObserve)
{
  OpenGLView *view;

  g_return_if_fail(window);

  DBG_fprintf(stderr, "Interactive : start new interactive session.\n");

  windowForInteractiveMode = window;
  view = visuDataGet_openGLView(visuRenderingWindowGet_visuData(window));

  observeValues->old_theta = view->camera->theta;
  observeValues->old_phi   = view->camera->phi;
  observeValues->old_omega = view->camera->omega;
  observeValues->old_xs    = view->camera->xs;
  observeValues->old_ys    = view->camera->ys;
  observeValues->old_gross = view->camera->gross;
  observeValues->old_d_red = view->camera->d_red;

/*   stopStandardEventListener(window); */

  callbackOnNodeSelected = handler;
  locationOnNodeSelected = data;

  pickObserveCallbackId = (GList*)0;

  callbackPickObserve.data = visuRenderingWindowGet_visuData(window);
  callbackPickObserve.action = observe;
  callbackPickObserve.stop = stopPickObserve;
}

void openGLInteractiveBegin_observe(void)
{
  callbackPickObserve.action = observe;
  /* Note : the list is useless if gtk_renderingWindowWidget is used. */
  pickObserveCallbackId = setObserveEventListener(windowForInteractiveMode,
						  &callbackPickObserve);
}

void openGLInteractiveBegin_pickAndObserve(void)
{
  callbackPickObserve.action = pickAndObserve;
  /* Note : the list is useless if gtk_renderingWindowWidget is used. */
  pickObserveCallbackId = setObserveEventListener(windowForInteractiveMode,
						  &callbackPickObserve);
}

void openGLInteractiveEnd_session(void)
{
  DBG_fprintf(stderr, "Interactive : end previous interactive session.\n");

  removeEventListener(windowForInteractiveMode, pickObserveCallbackId);
  pickObserveCallbackId = (GList*)0;

/*   restoreStandardEventListener(windowForInteractiveMode); */

  windowForInteractiveMode = (GenericRenderingWindow)0;
}

/******************************************************************************/

static gboolean pick(SimplifiedEvents *ev, VisuData *data)
{
  VisuPick nodeInfo;

  g_return_val_if_fail(ev, TRUE);
  if (ev->button == 3 && ev->buttonType == BUTTON_TYPE_PRESS)
    return TRUE;
  if (ev->buttonType == BUTTON_TYPE_RELEASE)
    return FALSE;

  g_return_val_if_fail(data, TRUE);

  nodeInfo.button = ev->button;
  nodeInfo.x = ev->x;
  nodeInfo.y = ev->y;
  nodeInfo.shiftMod = ev->shiftMod;
  nodeInfo.data = data;
  nodeInfo.node = getSelectElement(nodeInfo.data, ev->x, ev->y);
  if (DEBUG)
    {
      if (nodeInfo.node)
	{
	  DBG_fprintf(stderr, "Picked node %d, coord %f %f %f\n",
		      nodeInfo.node->number, nodeInfo.node->x,
		      nodeInfo.node->y, nodeInfo.node->z);
	}
      else
	{
	  DBG_fprintf(stderr, "You picked nothing.\n");
	}
    }
  if (callbackOnNodeSelected)
    callbackOnNodeSelected(&nodeInfo, locationOnNodeSelected);
  else
    g_warning("No method has been specified to detail what"
	      " to do when a node is selected.\n");
  return FALSE;
}

void openGLInteractiveBegin_pick()
{
  callbackPickObserve.action = pick;
  /* Note : the list is useless if gtk_renderingWindowWidget is used. */
  pickObserveCallbackId = setPickEventListener(windowForInteractiveMode,
					       &callbackPickObserve);
}

static gboolean move(SimplifiedEvents *ev, VisuData *data)
{
  VisuPick *nodeInfo;
  int dx, dy, i;
  float xAxis[3], yAxis[3], zAxis[3], ratio;
  OpenGLView *view;

  g_return_val_if_fail(ev, TRUE);
  if (ev->button == 3)
    {
      if (ev->buttonType == BUTTON_TYPE_PRESS)
	return TRUE;
      else
	return FALSE;
    }
  if (ev->button != 1)
    return FALSE;

  g_return_val_if_fail(data, TRUE);

  /* When a node is selected, store the nodeInfo structure
     as a property for the given data (or use one already
     registered). Then, it can be retrieve later when dragged.
     The freeing of this properties will be assured when the
     object is freed. */
  nodeInfo = (VisuPick*)visuDataGet_property(data, "opengl_nodeInfo");
  if (!nodeInfo)
    {
      nodeInfo = g_malloc(sizeof(VisuPick));
      visuDataSet_property(data, "opengl_nodeInfo", (gpointer)nodeInfo);
    }

  if (ev->motion == 1)
    {
      DBG_fprintf(stderr, "Drag node %p.\n", (gpointer)nodeInfo->node);
      dx =   ev->x - observeValues->xi;
      dy = -(ev->y - observeValues->yi);
      DBG_fprintf(stderr, " | dx x dy : %d x %d\n", dx, dy);

      /* Update stored position for drag info. */
      observeValues->xi = ev->x;
      observeValues->yi = ev->y;

      if (nodeInfo->node)
	{
	  /* Get the camera orientation. */
	  view = visuDataGet_openGLView(data);
	  for (i = 0; i < 3; i++)
	    {
	      xAxis[i] = 0.;
	      yAxis[i] = 0.;
	      zAxis[i] = 0.;
	    }
	  if (!ev->shiftMod && !ev->controlMod)
	    OpenGLViewGet_screenAxes(view, xAxis, yAxis);
	  else
	    {
	      if (ev->shiftMod)
		{
		  xAxis[0] = 1.;
		  yAxis[1] = 1.;
		}
	      else if (ev->controlMod)
		zAxis[2] = 1.;
	    }
	  ratio = OpenGLViewGet_fileUnitPerPixel(view);
	  nodeInfo->node->x += ratio * (dx * xAxis[0] + dy * yAxis[0] + dy * zAxis[0]);
	  nodeInfo->node->y += ratio * (dx * xAxis[1] + dy * yAxis[1] + dy * zAxis[1]);
	  nodeInfo->node->z += ratio * (dx * xAxis[2] + dy * yAxis[2] + dy * zAxis[2]);
	  glNewList(moveAtomExtension_list, GL_COMPILE);
	  visuData_createNode(data, nodeInfo->node);
	  glEndList();
	  visuDataEmit_nodePositionChanged(data);
	  
	  /* Force redraw */
	  g_signal_emit(visu, VISU_GET_CLASS (visu)->OpenGLForceReDraw_signal_id,
			0 , NULL);
	}
    }
  else if (ev->button == 1 && ev->buttonType == BUTTON_TYPE_PRESS)
    {
      nodeInfo->button = ev->button;
      nodeInfo->x = ev->x;
      nodeInfo->y = ev->y;
      nodeInfo->shiftMod = ev->shiftMod;
      nodeInfo->data = data;
      nodeInfo->node = getSelectElement(nodeInfo->data, ev->x, ev->y);

      /* Store the position to find the drag values. */
      observeValues->xi = ev->x;
      observeValues->yi = ev->y;

      if (nodeInfo->node)
	{
	  /* Hide the selected element in the allElement list. */
	  visuNodeSet_visibility(nodeInfo->node, FALSE);
	  visuData_createNodes(data, data->fromIntToVisuElement[nodeInfo->node->posElement]);

	  /* Show again the selected element and add it in
	     the specific moveAtom list. */
	  visuNodeSet_visibility(nodeInfo->node, TRUE);
	  glNewList(moveAtomExtension_list, GL_COMPILE);
	  visuData_createNode(data, nodeInfo->node);
	  glEndList();
	  moveAtomExtension->used = 1;

	  /* Force redraw */
	  g_signal_emit(visu, VISU_GET_CLASS (visu)->OpenGLForceReDraw_signal_id,
			0 , NULL);
	}

      DBG_fprintf(stderr, "Pick node %p.\n", (gpointer)nodeInfo->node);
    }
  else if (ev->button == 1 && ev->buttonType == BUTTON_TYPE_RELEASE)
    {
      if (nodeInfo->node)
	{
	  /* Hide the selected element in the allElement list. */
	  visuNodeSet_visibility(nodeInfo->node, TRUE);
	  visuData_createNodes(data, data->fromIntToVisuElement[nodeInfo->node->posElement]);

	  /* Stop the move extension. */
	  moveAtomExtension->used = 0;

	  /* Force redraw */
	  g_signal_emit(visu, VISU_GET_CLASS (visu)->OpenGLForceReDraw_signal_id,
			0 , NULL);
	}
      DBG_fprintf(stderr, "Restore standard allElement after move.\n");
    }
  if (callbackOnNodeSelected)
    callbackOnNodeSelected(nodeInfo, locationOnNodeSelected);
  else
    g_warning("No method has been specified to detail what"
	      " to do when a node is selected.\n");
  return FALSE;
}

void openGLInteractiveBegin_move()
{
  /* If this is the first time we begin a move action,
     we initialise the extension list. */
  if (!moveAtomExtension)
    {
      moveAtomExtension_list = openGLObjectList_new(1);
      moveAtomExtension = OpenGLExtension_new(moveAtomName, moveAtomDescription,
					      moveAtomExtension_list, NULL);
      OpenGLExtensionSet_priority(moveAtomExtension, OPENGL_EXTENSION_PRIORITY_FIRST + 1);
      OpenGLExtensionRegister(moveAtomExtension);
    }

/*   moveAtomExtension->used = 1; */
  callbackPickObserve.action = move;
  /* Note : the list is useless if gtk_renderingWindowWidget is used. */
  pickObserveCallbackId = setPickEventListener(windowForInteractiveMode,
					       &callbackPickObserve);
}

void openGLInteractiveBegin_mark(VisuData *data)
{
  /* If this is the first time we begin a move action,
     we initialise the extension list. */
  if (!markAtomExtension)
    {
      markAtomExtension_id = openGLObjectList_new(1);
      markAtomExtension = OpenGLExtension_new(markAtomName, markAtomDescription,
					      markAtomExtension_id, rebuildListOfMarks);
      OpenGLExtensionRegister(markAtomExtension);
      markAtomExtension->used = 1;

      markAtomExtension_list = (GList*)0;

      g_signal_connect(G_OBJECT(visu), "dataReadyForRendering",
		       G_CALLBACK(visuDataChanged), (gpointer)0);

      visuDataChanged(G_OBJECT(visu), data, (gpointer)0);
    }

  callbackPickObserve.action = mark;
  /* Note : the list is useless if gtk_renderingWindowWidget is used. */
  pickObserveCallbackId = setPickEventListener(windowForInteractiveMode,
					       &callbackPickObserve);
}

static void visuDataChanged(GObject *obj, VisuData *dataObj, gpointer bool)
{
  DBG_fprintf(stderr, "Interactive : caught 'dataReadyForRendering' signal,"
	      " emptying the mark list.\n");
  g_list_free(markAtomExtension_list);
  markAtomExtension_list = (GList*)0;
  if (dataObj)
    {
      DBG_fprintf(stderr, "Interactive : caught 'dataReadyForRendering' signal,"
		  " connecting signals for marked nodes.\n");

      g_signal_connect(G_OBJECT(dataObj), "ElementRenderedChanged",
		       G_CALLBACK(rebuildMarkNodesOnElementChanges), (gpointer)0);
      g_signal_connect(G_OBJECT(dataObj), "NodeRenderedChanged",
		       G_CALLBACK(rebuildMarkNodesOnChanges), (gpointer)0);
      g_signal_connect(G_OBJECT(dataObj), "NodePositionChanged",
		       G_CALLBACK(rebuildMarkNodesOnChanges), (gpointer)0);
    }
  rebuildListOfMarks(dataObj);
}

static void rebuildMarkNodesOnChanges(VisuData *dataObj, gpointer data)
{
  rebuildListOfMarks(dataObj);
}
static void rebuildMarkNodesOnElementChanges(VisuData *dataObj, VisuElement *ele, gpointer data)
{
  rebuildListOfMarks(dataObj);
}

static void rebuildListOfMarks(VisuData *dataObj)
{
  GList *tmpLst;
  int i;
  float *elementSize;
  VisuNode *node;
  VisuElement *ele;
  RenderingMethod *method;

  g_return_if_fail(dataObj);

  DBG_fprintf(stderr, "Interactive : rebuilding object list for marked nodes.\n");
  /* Create a table having the size of each element.
     This size is required to draw the mark. */
  elementSize = g_malloc(sizeof(float) * dataObj->ntype);
  method = getRenderingMethodInUse();
  for (i = 0; i < dataObj->ntype; i++)
    {
      ele = dataObj->fromIntToVisuElement[i];
      if (ele->rendered)
	elementSize[i] = renderingMethodGet_sizeOfElement(method, ele);
      else
	elementSize[i] = 0.;
    }
  tmpLst = markAtomExtension_list;
  glNewList(markAtomExtension_id, GL_COMPILE);
  while(tmpLst)
    {
      node = (VisuNode*)tmpLst->data;
      if (elementSize[node->posElement] > 0. && node->rendered)
	drawSelectMark(dataObj, node, elementSize[node->posElement]);
      tmpLst = g_list_next(tmpLst);
    }
  glEndList();
  g_free(elementSize);
}

static gboolean mark(SimplifiedEvents *ev, VisuData *data)
{
  VisuPick nodeInfo;
  GList *tmpLst;

  g_return_val_if_fail(ev, TRUE);
  if (ev->button == 3 && ev->buttonType == BUTTON_TYPE_PRESS)
    return TRUE;
  if (ev->buttonType == BUTTON_TYPE_RELEASE)
    return FALSE;

  g_return_val_if_fail(data, TRUE);

  nodeInfo.button = ev->button;
  nodeInfo.x = ev->x;
  nodeInfo.y = ev->y;
  nodeInfo.shiftMod = ev->shiftMod;
  nodeInfo.data = data;
  nodeInfo.node = getSelectElement(nodeInfo.data, ev->x, ev->y);
  if (!nodeInfo.node)
    return FALSE;

  tmpLst = g_list_find(markAtomExtension_list, (gconstpointer)nodeInfo.node);
  if (tmpLst)
    /* Node is already in the list, then it is removed. */
    markAtomExtension_list = g_list_remove_link(markAtomExtension_list, tmpLst);
  else
    /* Node is first selected, then added. */
    markAtomExtension_list = g_list_append(markAtomExtension_list, (gpointer)nodeInfo.node);

  /* Rebuild the list. */
  rebuildListOfMarks(data);

  /* Redraw. */
  g_signal_emit(visu, VISU_GET_CLASS (visu)->OpenGLForceReDraw_signal_id,
		0 , NULL);

  if (callbackOnNodeSelected)
    callbackOnNodeSelected(&nodeInfo, locationOnNodeSelected);
  else
    g_warning("No method has been specified to detail what"
	      " to do when a node is selected.\n");
  return FALSE;
}

static void drawSelectMark(VisuData *data, VisuNode *node, float eleSize)
{
  float position[3];
  OpenGLView *view;
  #define ratio 0.5
  VisuElement *ele;
  float size;

  g_return_if_fail(IS_VISU_DATA_TYPE(data) && node && eleSize > 0.);
  
  visuDataGet_nodePosition(data, node, position);
  view = visuDataGet_openGLView(data);
  ele = data->fromIntToVisuElement[node->posElement];
  size = eleSize * 0.9;

  glDisable(GL_LIGHTING);
  glDisable(GL_DITHER);
  glLineWidth(5);
  glColor3f(1. - ele->rgb[0], 1. - ele->rgb[1], 1. -ele->rgb[2]);
  glPushMatrix();
  glTranslated(position[0] - view->box->dxxs2 - size,
	       position[1] - view->box->dyys2 - size,
	       position[2] - view->box->dzzs2 - size);
  glBegin(GL_LINES);
  /* Vertex (0 ; 0 ; 0) */
  glVertex3d(0., 0., 0.);
  glVertex3d(ratio * size, 0., 0.);
  glVertex3d(0., 0., 0.);
  glVertex3d(0., ratio * size, 0.);
  glVertex3d(0., 0., 0.);
  glVertex3d(0., 0., ratio * size);
  /* Vertex (1 ; 0 ; 0) */
  glVertex3d(2. * size, 0., 0.);
  glVertex3d((2. - ratio) * size, 0., 0.);
  glVertex3d(2. * size, 0., 0.);
  glVertex3d(2. * size, ratio * size, 0.);
  glVertex3d(2. * size, 0., 0.);
  glVertex3d(2. * size, 0., ratio * size);
  /* Vertex (0 ; 1 ; 0) */
  glVertex3d(0., 2. * size, 0.);
  glVertex3d(ratio * size, 2. * size, 0.);
  glVertex3d(0., 2. * size, 0.);
  glVertex3d(0., (2. - ratio) * size, 0.);
  glVertex3d(0., 2. * size, 0.);
  glVertex3d(0., 2. * size, ratio * size);
  /* Vertex (0 ; 0 ; 1) */
  glVertex3d(0., 0., 2. * size);
  glVertex3d(ratio * size, 0., 2. * size);
  glVertex3d(0., 0., 2. * size);
  glVertex3d(0., ratio * size, 2. * size);
  glVertex3d(0., 0., 2. * size);
  glVertex3d(0., 0., (2. - ratio) * size);

  /* Vertex (1 ; 1 ; 0) */
  glVertex3d(2. * size, 2. * size, 0.);
  glVertex3d((2. - ratio) * size, 2. * size, 0.);
  glVertex3d(2. * size, 2. * size, 0.);
  glVertex3d(2. * size, (2. - ratio) * size, 0.);
  glVertex3d(2. * size, 2. * size, 0.);
  glVertex3d(2. * size, 2. * size, ratio * size);
  /* Vertex (0 ; 1 ; 1) */
  glVertex3d(0., 2. * size, 2. * size);
  glVertex3d(ratio * size, 2. * size, 2. * size);
  glVertex3d(0., 2. * size, 2. * size);
  glVertex3d(0., (2. - ratio) * size, 2. * size);
  glVertex3d(0., 2. * size, 2. * size);
  glVertex3d(0., 2. * size, (2. - ratio) * size);
  /* Vertex (1 ; 0 ; 1) */
  glVertex3d(2. * size, 0., 2. * size);
  glVertex3d((2. - ratio) * size, 0., 2. * size);
  glVertex3d(2. * size, 0., 2. * size);
  glVertex3d(2. * size, ratio * size, 2. * size);
  glVertex3d(2. * size, 0., 2. * size);
  glVertex3d(2. * size, 0., (2. - ratio) * size);
  /* Vertex (1 ; 1 ; 1) */
  glVertex3d(2. * size, 2. * size, 2. * size);
  glVertex3d((2. - ratio) * size, 2. * size, 2. * size);
  glVertex3d(2. * size, 2. * size, 2. * size);
  glVertex3d(2. * size, (2. - ratio) * size, 2. * size);
  glVertex3d(2. * size, 2. * size, 2. * size);
  glVertex3d(2. * size, 2. * size, (2. - ratio) * size);
  glEnd();
  glPopMatrix();
  glLineWidth(1.);
  glEnable(GL_LIGHTING);
  glEnable(GL_DITHER);
}
gboolean openGLInteractiveRemove_allMarks(VisuData *data)
{
  if (!markAtomExtension_list)
    return FALSE;

  g_list_free(markAtomExtension_list);
  markAtomExtension_list = (GList*)0;

  /* Rebuild the list. */
  rebuildListOfMarks(data);

  return TRUE;
}

VisuNode* getSelectElement(VisuData *dataObj, int x, int y)
{
#define bufsize 512
   GLuint select_buf[bufsize];
   GLint viewport[4] = {0, 0, 0, 0};
#define wpck 2.0
#define hpck 2.0
   int hits, names, ptr, i;
   unsigned int z1;
   unsigned int z1_sauve = UINT_MAX;
   int number;
   int found;
   VisuNode* res;
   OpenGLView *view;
 
   g_return_val_if_fail(IS_VISU_DATA_TYPE(dataObj), (VisuNode*)0);

   res = (VisuNode*)0;

   view = visuDataGet_openGLView(dataObj);

   glSelectBuffer(bufsize, select_buf);
   hits = glRenderMode(GL_SELECT);
   glInitNames();
   glPushName(-1);
      
   viewport[2] = view->window->width;
   viewport[3] = view->window->height;
   glNewList(10, GL_COMPILE);
      gluPickMatrix(1.0*x, 1.0*(view->window->height-y), wpck, hpck, viewport);
   glEndList();

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glCallList(10);
   glFrustum(view->window->left, view->window->right, view->window->bottom,
	     view->window->top, view->window->near, view->window->far);
   glMatrixMode(GL_MODELVIEW); 

   glCallList(visuDataGet_objectList(dataObj));
   glFlush();

   hits = glRenderMode(GL_RENDER);
   DBG_fprintf(stderr, "%d elements are on the z buffer.\n", hits);
   ptr = 0;

   /* return the buffer to normal */
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glFrustum(view->window->left, view->window->right, view->window->bottom,
	     view->window->top, view->window->near, view->window->far);
   glMatrixMode(GL_MODELVIEW); 

   found = 0;
   number = -1;
   for(i=0; i<hits; i++) {
      names = select_buf[ptr];
      if (names != 1)
	{
	  g_warning("OpenGL picking is not working???\n");
	  return (VisuNode*)0;
	}
      ptr = ptr + 1;
      z1 = select_buf[ptr];
      ptr = ptr + 2;
      if (z1 < z1_sauve) {
         z1_sauve = z1;
	 number = (int)select_buf[ptr];
	 found = 1;
      }
      ptr = ptr + 1;
   }
   if (found && number >= 0)
     res = visuDataGet_nodeFromNumber(dataObj, number);
   return res;
}

gboolean readOpenGLObserveMethod(gchar **lines, int nbLines,
				 int position, GString *errorMessage)
{
  int res;
  int val;

  res = sscanf(lines[0],"%d", &val);
  if (res != 1 || (val != OPENGL_OBSERVE_CONSTRAINED && val != OPENGL_OBSERVE_WALKER))
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 1 integer value (v > 0) must appear"
					       " after the %s markup.\n"),
			       position, FLAG_PARAMETER_OBSERVE_METHOD);
      openGLInteractiveSet_preferedObserveMethod(OPENGL_OBSERVE_CONSTRAINED);
      return FALSE;
    }
  else
    openGLInteractiveSet_preferedObserveMethod(val);

  return TRUE;
}
static gboolean exportParameters(GString *data, int *nbLinesWritten)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_OBSERVE_METHOD);
  g_string_append_printf(data, "%s: %d\n\n", FLAG_PARAMETER_OBSERVE_METHOD,
	  openGL_preferedObserveMethod);
  *nbLinesWritten = 3;
  return TRUE;
}
