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

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

#include "visu_tools.h"
#include "visu_data.h"
#include "visu_object.h"
#include "visu_extension.h"
#include "openGLFunctions/text.h"
#include "openGLFunctions/objectList.h"
#include "renderingBackend/visu_windowInterface.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <glib.h>

OpenGLExtension *extensionMarks;
int openGlListMarksId;

#define PICK_MESURE_MARK_BIG_SQUARE_SIZE 8
#define PICK_MESURE_MARK_SMALL_SQUARE_SIZE 4
guchar pickMesureMark_bigSquare[PICK_MESURE_MARK_BIG_SQUARE_SIZE *
				PICK_MESURE_MARK_BIG_SQUARE_SIZE * 4];
guchar pickMesureMark_smallSquare[PICK_MESURE_MARK_SMALL_SQUARE_SIZE *
				  PICK_MESURE_MARK_SMALL_SQUARE_SIZE * 4];

typedef enum
  {
    PICK_MESURE_MARK_BIG_SQUARE,
    PICK_MESURE_MARK_SMALL_SQUARE,
    PICK_MESURE_END_MARK_DOT,
    PICK_MESURE_MARK_DISTANCE,
    PICK_MESURE_END_MARK_LINK
  } PickMesureMarkType;

struct MarkInfo_struct
{
  /* Mark type. */
  PickMesureMarkType type;
  /* Pointer to one VisuNode. */
  VisuNode *node1;
  /* Coordinates used to address the VisuNode when the
     pointer node1 has changed, for example when a new VisuData
     is loaded with the same geometry and we want to apply this mark. */
  int eleNode1, posNode1;

  /* Idem for a second node. */
  VisuNode *node2;
  int eleNode2, posNode2;

  /* Text not used. */
  gchar *text;
};
/**
 * PickMesure_struct:
 * @xRef1: x coordinates of reference 1 (if set) in window coordinates ;
 * @yRef1: y coordinates of reference 1 (if set) in window coordinates ;
 * @ref1: a #VisuNode corresponding to reference 1 (if set) ;
 * @xRef2: x coordinates of reference 2 (if set) in window coordinates ;
 * @yRef2: y coordinates of reference 2 (if set) in window coordinates ;
 * @ref2: a #VisuNode corresponding to reference 2 (if set) ;
 * @selected: a #VisuNode corresponding to the normal selected node (if set) ;
 * @info: a string containing the formated pick informations ;
 * @error: a string containing an error message (if any) ;
 * @newValuesAvailable: a flag to set if the pick action has canged anything ;
 * @formatFlag: if set, the errors and info GString are used and strings
 *              output for the pick action are created.
 *
 * This structure reflects the state of a current pick session used for
 * distance and angles measurements.
 */
struct PickMesure_struct
{
  VisuData *data;

  int xRef1, yRef1;
  VisuNode *ref1;
  int eleRef1, posRef1;
  int xRef2, yRef2;
  VisuNode *ref2;
  int eleRef2, posRef2;

  VisuNode *selected;
  int eleSelected, posSelected;

  gboolean formatFlag;
  GString *info;
  GString *errors;

  gboolean newValuesAvailable;

  GList *storedMarks;
  gboolean storeDistance;

  gulong onFileChangeSignalId;
  gulong onPositionChangeSignalId;
  gulong onNodeRenderedChangeSignalId;
  gulong onElementRenderedChangeSignalId;
};

/* Local drawing methods. */
void updateMarkList(VisuData *data, GList *list);
/* Dots drawing methods. */
void drawMarkDot(VisuData *data, VisuNode *node, PickMesureMarkType type);
void putMarkDot(VisuData *data, VisuNode *node, PickMesureMarkType type);
void addMarkDotToList(PickMesure *mesureData, VisuNode *node, PickMesureMarkType type);
void removeMarkDotFromList(PickMesure *mesureData, VisuNode *node);
/* Distances drawing method. */
void drawMarkDistance(VisuData *data, VisuNode *nodeRef, VisuNode *node, PickMesureMarkType type);
void putMarkDistance(VisuData *data, VisuNode *nodeRef, VisuNode *node, PickMesureMarkType type);
void addMarkDistanceToList(PickMesure *mesureData, VisuNode *nodeRef,
			   VisuNode *node, PickMesureMarkType type);
/* gboolean pickMesureRemove_allDistanceMarks(PickMesure *mesureData, VisuData *data); */

/* Local callbacks. */
void rebuildMarkListOnFileChange(GObject *visu, VisuData *dataObj, gpointer data);
void updateListOnNodeChange(VisuData *dataObj, gpointer data);
void updateListOnElementChange(VisuData *dataObj, VisuElement *ele, gpointer data);


void initPick_module()
{
  int i;

  /* Get an id for the OpenGL list used to draw marks. */
  openGlListMarksId = openGLObjectList_new(1);
  /* Create the marks. */
  for (i = 0; i < PICK_MESURE_MARK_BIG_SQUARE_SIZE *
	 PICK_MESURE_MARK_BIG_SQUARE_SIZE * 4 ; i++)
    pickMesureMark_bigSquare[i] = 0xff;
  for (i = 0; i < PICK_MESURE_MARK_SMALL_SQUARE_SIZE *
	 PICK_MESURE_MARK_SMALL_SQUARE_SIZE * 4; i++)
    pickMesureMark_smallSquare[i] = 0xff;
}


PickMesure* initPickMesure(VisuData *data, gboolean formatFlag, gboolean storeDistance)
{
  char *description = _("Draw some marks on element in video inverse.");
  PickMesure *mesureData;

  g_return_val_if_fail(data, (PickMesure*)0);

  mesureData = g_malloc(sizeof(PickMesure));
  mesureData->data = data;
  g_object_ref(data);
  mesureData->ref1 = (VisuNode*)0;
  mesureData->ref2 = (VisuNode*)0;
  mesureData->selected = (VisuNode*)0;
  mesureData->info = (GString*)0;
  mesureData->errors = (GString*)0;
  mesureData->newValuesAvailable = FALSE;
  mesureData->formatFlag = formatFlag;
  mesureData->storedMarks = (GList*)0;
  mesureData->storeDistance = storeDistance;

  /* Initialize an extension to draw the marks. */
  extensionMarks = OpenGLExtension_new("Marks", description,
				       openGlListMarksId, NULL);
  OpenGLExtensionRegister(extensionMarks);
  OpenGLExtensionSet_priority(extensionMarks, OPENGL_EXTENSION_PRIORITY_LAST);
  OpenGLExtensionSet_saveOpenGLState(extensionMarks, TRUE);
  extensionMarks->used = 1;

  if (storeDistance)
    openGLText_initFontList();

  /* Connect a signal on file change to remove everything. */
  mesureData->onFileChangeSignalId =
    g_signal_connect(G_OBJECT(visu), "dataReadyForRendering",
		     G_CALLBACK(rebuildMarkListOnFileChange), (gpointer)mesureData);
  mesureData->onPositionChangeSignalId =
    g_signal_connect(G_OBJECT(data), "NodePositionChanged",
		     G_CALLBACK(updateListOnNodeChange), (gpointer)mesureData);
  mesureData->onNodeRenderedChangeSignalId =
    g_signal_connect(G_OBJECT(data), "NodeRenderedChanged",
		     G_CALLBACK(updateListOnNodeChange), (gpointer)mesureData);
  mesureData->onElementRenderedChangeSignalId =
    g_signal_connect(G_OBJECT(data), "ElementRenderedChanged",
		     G_CALLBACK(updateListOnElementChange), (gpointer)mesureData);
  
  DBG_fprintf(stderr, "Visu PickMesure : create a new object (formating : %d), %p.\n",
	      (int)formatFlag, (gpointer)mesureData);

  return mesureData;
}
gboolean pickMesureRemove_allMarks(PickMesure *mesureData)
{
  gboolean redraw;

  g_return_val_if_fail(mesureData, FALSE);

  if (mesureData->ref1)
    removeMarkDotFromList(mesureData, mesureData->ref1);
  if (mesureData->ref2)
    removeMarkDotFromList(mesureData, mesureData->ref2);
  mesureData->ref1 = (VisuNode*)0;
  mesureData->ref2 = (VisuNode*)0;
  mesureData->selected = (VisuNode*)0;
  redraw = pickMesureRemove_allDistanceMarks(mesureData);
  
  return redraw;
}
void endPickMesure(PickMesure *mesureData)
{
  GList *list;
  gboolean redraw;

  g_return_if_fail(mesureData);

  DBG_fprintf(stderr, "Visu PickMesure : Freeing pick mesure data from %p (VisuData %p).\n",
	      (gpointer)mesureData, (gpointer)mesureData->data);

  redraw = pickMesureRemove_allMarks(mesureData);
  if (mesureData->info)
    g_string_free(mesureData->info, TRUE);
  if (mesureData->errors)
    g_string_free(mesureData->errors, TRUE);
  list = mesureData->storedMarks;
  while (list)
    {
      g_free(list->data);
      list = g_list_next(list);
    }
  g_list_free(mesureData->storedMarks);
  g_signal_handler_disconnect(G_OBJECT(visu), mesureData->onFileChangeSignalId);
  g_signal_handler_disconnect(G_OBJECT(mesureData->data), mesureData->onPositionChangeSignalId);
  g_signal_handler_disconnect(G_OBJECT(mesureData->data), mesureData->onNodeRenderedChangeSignalId);
  g_signal_handler_disconnect(G_OBJECT(mesureData->data), mesureData->onElementRenderedChangeSignalId);
  g_object_unref(G_OBJECT(mesureData->data));
  g_free(mesureData);

  /* Remove and free the extension used to draw the marks. */
  OpenGLExtensionRemove(extensionMarks);
  OpenGLExtension_free(extensionMarks);

  /* Empty OpenGL list. */
  glDeleteLists(openGlListMarksId, 1);

  if (redraw)
    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		   0 /* details */,
		   NULL);
}

void putMarkDot(VisuData *data, VisuNode *node, PickMesureMarkType type)
{
  VisuElement *ele;

  /* If the node is masked, then we don't draw the dot. */
  if (!node->rendered)
    return;
  /* If one of the element is masked, then we don't draw the distance. */
  ele = data->fromIntToVisuElement[node->posElement];
  if (!ele->rendered)
    return;

  glDrawBuffer(GL_FRONT);

  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_LIGHTING);
  glDisable(GL_FOG);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
  drawMarkDot(data, node, type);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glPopAttrib();

  glDrawBuffer(GL_BACK);
}
void drawMarkDot(VisuData *data, VisuNode *node, PickMesureMarkType type)
{
  float xyz[3];
  OpenGLView *view;
  VisuElement *ele;

  /* If the node is masked, then we don't draw the dot. */
  if (!node->rendered)
    return;
  /* If one of the element is masked, then we don't draw the distance. */
  ele = data->fromIntToVisuElement[node->posElement];
  if (!ele->rendered)
    return;

  visuDataGet_nodePosition(data, node, xyz);
  view = visuDataGet_openGLView(data);
  glRasterPos3f(xyz[0] - view->box->dxxs2,
		xyz[1] - view->box->dyys2,
		xyz[2] - view->box->dzzs2);
  switch (type)
    {
    case PICK_MESURE_MARK_BIG_SQUARE:
      glDrawPixels(PICK_MESURE_MARK_BIG_SQUARE_SIZE, PICK_MESURE_MARK_BIG_SQUARE_SIZE,
		   GL_RGBA, GL_UNSIGNED_BYTE, pickMesureMark_bigSquare);
      break;
    case PICK_MESURE_MARK_SMALL_SQUARE:
      glDrawPixels(PICK_MESURE_MARK_SMALL_SQUARE_SIZE, PICK_MESURE_MARK_SMALL_SQUARE_SIZE,
		   GL_RGBA, GL_UNSIGNED_BYTE, pickMesureMark_smallSquare);
      break;
    default:
      break;
    }
}
void putMarkDistance(VisuData *data, VisuNode *nodeRef, VisuNode *node, PickMesureMarkType type)
{
  VisuElement *ele;

  /* If one of the node is masked, then we don't draw the distance. */
  if (!nodeRef->rendered || !node->rendered)
    return;
  /* If one of the element is masked, then we don't draw the distance. */
  ele = data->fromIntToVisuElement[nodeRef->posElement];
  if (!ele->rendered)
    return;
  ele = data->fromIntToVisuElement[node->posElement];
  if (!ele->rendered)
    return;

  DBG_fprintf(stderr, "Visu pickMesure : draw directly a distance on front buffer.\n");
  glDrawBuffer(GL_FRONT);

  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_LIGHTING);
  glDisable(GL_FOG);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
  drawMarkDistance(data, nodeRef, node, type);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glPopAttrib();

  glDrawBuffer(GL_BACK);
}
void drawMarkDistance(VisuData *data, VisuNode *nodeRef, VisuNode *node, PickMesureMarkType type)
{
  float xyzRef[3], xyz[3], dist;
  int i;
  char distStr[8];
  OpenGLView *view;
  VisuElement *ele;

  /* If one of the node is masked, then we don't draw the distance. */
  if (!nodeRef->rendered || !node->rendered)
    return;

  /* If one of the element is masked, then we don't draw the distance. */
  ele = data->fromIntToVisuElement[nodeRef->posElement];
  if (!ele->rendered)
    return;
  ele = data->fromIntToVisuElement[node->posElement];
  if (!ele->rendered)
    return;

  visuDataGet_nodePosition(data, nodeRef, xyzRef);
  visuDataGet_nodePosition(data, node, xyz);
  view = visuDataGet_openGLView(data);

  dist = 0.;
  for (i = 0; i < 3; i++)
    dist += (xyzRef[i] - xyz[i]) * (xyzRef[i] - xyz[i]);
  sprintf(distStr, "%7.3f", sqrt(dist));
  distStr[7] = '\0';
  DBG_fprintf(stderr, "Visu pickMesure : draw a link with distance %s.\n", distStr);

  glPushMatrix();
  glTranslated(-view->box->dxxs2, -view->box->dyys2, -view->box->dzzs2);
  glLineWidth(1);
  glColor4f(1., 1., 1., 0.);
  glBegin(GL_LINES);
  glVertex3fv(xyzRef);
  glVertex3fv(xyz);
  glEnd();
  glPointSize(8.);
  glBegin(GL_POINTS);
  glVertex3fv(xyzRef);
  glVertex3fv(xyz);
  glEnd();
  glRasterPos3f((xyzRef[0] + xyz[0])/2., (xyzRef[1] + xyz[1])/2, (xyzRef[2] + xyz[2])/2.); 
  openGLText_drawChars(distStr); 
/*   fprintf(stderr, "----> '%s'\n", distStr); */
  glPopMatrix();
  
/*   drawMarkDot(data, node, PICK_MESURE_MARK_SMALL_SQUARE); */
}

/* This method recreate the opengl list associated to marks with
   all the marks given as argument. */
void updateMarkList(VisuData *data, GList *list)
{
  struct MarkInfo_struct *mark;

  DBG_fprintf(stderr, "Visu pickMesure : update the list %p"
	      " of mark distances.\n", (gpointer)list);
  glDeleteLists(openGlListMarksId, 1);
  if (!list)
    return;
  glNewList(openGlListMarksId, GL_COMPILE);
/*   glDisable(GL_DEPTH_TEST); */
  glDisable(GL_LIGHTING);
  glDisable(GL_FOG);
/*   glDisable(GL_DITHER); */
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
  while(list)
    {
      mark = (struct MarkInfo_struct*)list->data;
      DBG_fprintf(stderr, " | draw mark of type %d.\n", mark->type);
      if (mark->type < PICK_MESURE_END_MARK_DOT)
	drawMarkDot(data, mark->node1, mark->type);
      else if (mark->type > PICK_MESURE_END_MARK_DOT &&
	       mark->type < PICK_MESURE_END_MARK_LINK)
	drawMarkDistance(data, mark->node1, mark->node2, mark->type);
      list = g_list_next(list);
    }
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glEndList();
}
/* Method that add a dot mark to the list of drawn marks.
   The given new mark is drawn and the opengl list is updated for
   future use. */
void addMarkDotToList(PickMesure *mesureData, VisuNode *node, PickMesureMarkType type)
{
  struct MarkInfo_struct *mark;

  g_return_if_fail(type < PICK_MESURE_END_MARK_DOT && node && mesureData);

  mark = g_malloc(sizeof(struct MarkInfo_struct));
  mark->type = type;
  mark->node1 = node;
  mark->eleNode1 = node->posElement;
  mark->posNode1 = node->posNode;
  mark->node2 = (VisuNode*)0;
  mesureData->storedMarks = g_list_append(mesureData->storedMarks, (gpointer)mark);
  putMarkDot(mesureData->data, node, type);
  glFlush();
  updateMarkList(mesureData->data, mesureData->storedMarks);
}
/* Method that remove a dot mark to the list of drawn marks.
   The removed mark is selected by its VisuNode. */
void removeMarkDotFromList(PickMesure *mesureData, VisuNode *node)
{
  GList *list;
  struct MarkInfo_struct *mark;

  g_return_if_fail(node && mesureData);

  mark = (struct MarkInfo_struct*)0;
  list = mesureData->storedMarks;
  while (list)
    {
      if (((struct MarkInfo_struct*)list->data)->node1 == node)
	{
	  mark = (struct MarkInfo_struct*)list->data;
	  list = (GList*)0;
	}
      else
	list = g_list_next(list);
    }
  g_return_if_fail(mark);
  mesureData->storedMarks = g_list_remove(mesureData->storedMarks, mark);
  putMarkDot(mesureData->data, node, mark->type);
  glFlush();
  g_free(mark);
  updateMarkList(mesureData->data, mesureData->storedMarks);
}
/* Method that add a distance mark to the list of drawn marks.
   The given new mark is drawn and the opengl list is updated for
   future use. */
void addMarkDistanceToList(PickMesure *mesureData, VisuNode *nodeRef,
			   VisuNode *node, PickMesureMarkType type)
{
  struct MarkInfo_struct *mark;

  g_return_if_fail(type > PICK_MESURE_END_MARK_DOT && type < PICK_MESURE_END_MARK_LINK &&
		   nodeRef && node && mesureData);

  if (!mesureData->storeDistance)
    return;

  mark = g_malloc(sizeof(struct MarkInfo_struct));
  mark->type = type;
  mark->node1 = nodeRef;
  mark->eleNode1 = nodeRef->posElement;
  mark->posNode1 = nodeRef->posNode;
  mark->node2 = node;
  mark->eleNode2 = node->posElement;
  mark->posNode2 = node->posNode;
  mesureData->storedMarks = g_list_append(mesureData->storedMarks, (gpointer)mark);
  putMarkDistance(mesureData->data, nodeRef, node, type);
  glFlush();
  updateMarkList(mesureData->data, mesureData->storedMarks);
}
/* Method that removes all distance marks from the list of drawn marks. */
gboolean pickMesureRemove_allDistanceMarks(PickMesure *mesureData)
{
  GList *list, *rmList;
  struct MarkInfo_struct *mark;

  g_return_val_if_fail(mesureData, FALSE);

  mark = (struct MarkInfo_struct*)0;
  list = mesureData->storedMarks;
  rmList = (GList*)0;
  while (list)
    {
      if (((struct MarkInfo_struct*)list->data)->type > PICK_MESURE_END_MARK_DOT &&
	  ((struct MarkInfo_struct*)list->data)->type < PICK_MESURE_END_MARK_LINK)
	rmList = g_list_append(rmList, list->data);
      list = g_list_next(list);
    }
  if (!rmList)
    return FALSE;

  list = rmList;
  while (list)
    {
      mark = (struct MarkInfo_struct*)list->data;
      mesureData->storedMarks = g_list_remove(mesureData->storedMarks, mark);
      g_free(mark);
      list = g_list_next(list);
    }  
  g_list_free(rmList);
  updateMarkList(mesureData->data, mesureData->storedMarks);
  return TRUE;
}


void marksAndMesures(const VisuPick *info, gpointer data)
{
  double dx, dy, dz, dr, dx1, dy1, dz1, dr1,  dx2, dy2, dz2, dr2;
  double ang;
  float posRef1[3], posRef2[3], posSelect[3];
  PickMesure *mesureData;

  g_return_if_fail(info && data);

  mesureData = data;
  g_return_if_fail(mesureData->data == info->data);

  mesureData->newValuesAvailable = TRUE;
  if (mesureData->formatFlag)
    {
      if (!mesureData->info)
	mesureData->info = g_string_new("");
      else
	mesureData->info = g_string_erase(mesureData->info, 0, -1);
      if (!mesureData->errors)
	mesureData->errors = g_string_new("");
      else
	mesureData->errors = g_string_erase(mesureData->errors, 0, -1);
    }

  mesureData->selected = (VisuNode*)0;
  if (info->button == 1)
    {
      if (info->node)
	{
	  mesureData->selected = info->node;
	  mesureData->eleSelected = info->node->posElement;
	  mesureData->posSelected = info->node->posNode;
	  if (mesureData->ref1)
	    addMarkDistanceToList(mesureData, mesureData->ref1, info->node,
				  PICK_MESURE_MARK_DISTANCE);
	}
      else
	{
	  if (mesureData->formatFlag)
	    mesureData->errors = g_string_append(mesureData->errors, _("You picked nothing.\n"));
	  mesureData->selected = (VisuNode*)0;
	}
    }
  else if (info->button == 2)
    {
      if (!(info->shiftMod))
	{
	  if (info->node)
	    {
	      if (mesureData->ref2 == info->node)
		{
		  if (mesureData->formatFlag)
		    mesureData->errors =
		      g_string_append(mesureData->errors,
				      _("1st reference node should be distinct\n"
					"from 2nd one.\n"));
		}
	      else
		{
		  if (mesureData->ref1)
		    removeMarkDotFromList(mesureData, mesureData->ref1);
		  mesureData->xRef1 = info->x;
		  mesureData->yRef1 = info->y;
		  mesureData->ref1 = info->node;
		  mesureData->eleRef1 = info->node->posElement;
		  mesureData->posRef1 = info->node->posNode;
		  addMarkDotToList(mesureData, mesureData->ref1, PICK_MESURE_MARK_BIG_SQUARE);
		}
	    }
	  else
	    {
	      if (mesureData->formatFlag)
		mesureData->errors = g_string_append(mesureData->errors, _("Unset reference 1.\n"));
	      if (mesureData->ref1)
		removeMarkDotFromList(mesureData, mesureData->ref1);
	      mesureData->ref1 = (VisuNode*)0;
	      if (mesureData->ref2)
		removeMarkDotFromList(mesureData, mesureData->ref2);
	      mesureData->ref2 = (VisuNode*)0;
	    }
	}
      else
	{
	  if (mesureData->ref2)
	    removeMarkDotFromList(mesureData, mesureData->ref2);
	  mesureData->ref2 = (VisuNode*)0;
	  if (info->node)
	    {
	      if (!mesureData->ref1)
		{
		  if (mesureData->formatFlag)
		    mesureData->errors =
		      g_string_append(mesureData->errors,
				      _("Unable to set reference 2\n"
					"without existing reference 1, use\n"
					"middle button alone to set reference 1.\n"));
		}
	      else if (mesureData->ref1 == info->node)
		{
		  if (mesureData->formatFlag)
		    mesureData->errors =
		      g_string_append(mesureData->errors,
				      _("2nd reference node should be distinct\n"
					"from 1st one.\n"));
		}
	      else
		{
		  mesureData->xRef2 = info->x;
		  mesureData->yRef2 = info->y;
		  mesureData->ref2 = info->node;
		  mesureData->eleRef2 = info->node->posElement;
		  mesureData->posRef2 = info->node->posNode;
		  addMarkDotToList(mesureData, mesureData->ref2, PICK_MESURE_MARK_SMALL_SQUARE);
		}
	    }
	  else
	    {
	      if (mesureData->formatFlag)
		mesureData->errors =
		  g_string_append(mesureData->errors,
				  _("Unset reference 2.\n"));
	    }
	}
    }
  else
    {
      DBG_fprintf(stderr, "Mesure pick : unknown clic %d %d .\n",
		  info->button, info->shiftMod);
      mesureData->newValuesAvailable = FALSE;
      return;
    }

  if (!mesureData->formatFlag)
    return;

  /* Create a string with informations. */
  if (mesureData->ref1)
    {
      g_string_append_printf(mesureData->info,
			     _("Reference node #%d\n"
			       "  x = %7.3f\n"
			       "  y = %7.3f\n"
			       "  z = %7.3f\n"),
			     mesureData->ref1->number, mesureData->ref1->x,
			     mesureData->ref1->y, mesureData->ref1->z);
    }
  if (mesureData->ref2)
    {
      visuDataGet_nodePosition(mesureData->data, mesureData->ref1, posRef1);
      visuDataGet_nodePosition(mesureData->data, mesureData->ref2, posRef2);
      dx = posRef2[0] - posRef1[0];
      dy = posRef2[1] - posRef1[1];
      dz = posRef2[2] - posRef1[2];
      g_string_append_printf(mesureData->info,
			     _("2nd Reference node #%d\n"
			       "  x = %7.3f   dx = %7.3f\n"
			       "  y = %7.3f   dy = %7.3f\n"
			       "  z = %7.3f   dz = %7.3f\n"),
			     mesureData->ref2->number,
			     mesureData->ref2->x, dx,
			     mesureData->ref2->y, dy,
			     mesureData->ref2->z, dz);
      dr = sqrt(dx*dx + dy*dy + dz*dz);
      if (mesureData->selected)
	g_string_append_printf(mesureData->info, _("           i.e. dr = %7.3f\n"), dr);
      else
	g_string_append_printf(mesureData->info, _("           i.e. dr = %7.3f"), dr);
    }
  if (mesureData->selected)
    {
      if (!mesureData->ref2)
        {
	  if (mesureData->ref1)
	    {
	      visuDataGet_nodePosition(mesureData->data, mesureData->selected, posSelect);
	      visuDataGet_nodePosition(mesureData->data, mesureData->ref1, posRef1);
	      dx = posSelect[0] - posRef1[0];
	      dy = posSelect[1] - posRef1[1];
	      dz = posSelect[2] - posRef1[2];
	      g_string_append_printf(mesureData->info,
				     _("Newly picked node #%d\n"
				       "  x = %7.3f   dx = %7.3f\n"
				       "  y = %7.3f   dy = %7.3f\n"
				       "  z = %7.3f   dz = %7.3f\n"),
				     mesureData->selected->number,
				     mesureData->selected->x, dx,
				     mesureData->selected->y, dy,
				     mesureData->selected->z, dz);
	      dr = sqrt(dx*dx + dy*dy + dz*dz);
	      g_string_append_printf(mesureData->info, _("           i.e. dr = %7.3f"), dr);
	    }
	  else
	    g_string_append_printf(mesureData->info,
				   _("Newly picked node #%d\n"
				     "  x = %7.3f   theta = %7.3f\n"
				     "  y = %7.3f   phi   = %7.3f\n"
				     "  z = %7.3f\n"),
				   mesureData->selected->number, mesureData->selected->x,
				   mesureData->selected->theta, mesureData->selected->y,
				   mesureData->selected->phi, mesureData->selected->z
				   );
	}
      else
        {
	  visuDataGet_nodePosition(mesureData->data, mesureData->selected, posSelect);
	  visuDataGet_nodePosition(mesureData->data, mesureData->ref1, posRef1);
	  visuDataGet_nodePosition(mesureData->data, mesureData->ref2, posRef2);
	  dx1 = posSelect[0] - posRef1[0];
	  dy1 = posSelect[1] - posRef1[1];
	  dz1 = posSelect[2] - posRef1[2];
	  dx2 = posRef2[0] - posRef1[0];
	  dy2 = posRef2[1] - posRef1[1];
	  dz2 = posRef2[2] - posRef1[2];
	  g_string_append_printf(mesureData->info,
				 _("Newly picked node #%d\n"
				   "  x = %7.3f   dx1 = %7.3f  dx2 = %7.3f\n"
				   "  y = %7.3f   dy1 = %7.3f  dx2 = %7.3f\n"
				   "  z = %7.3f   dz1 = %7.3f  dx2 = %7.3f\n"),
				 mesureData->selected->number,
				 mesureData->selected->x, dx1, dx2,
				 mesureData->selected->y, dy1, dy2,
				 mesureData->selected->z, dz1, dz2);
	  dr1 = sqrt(dx1*dx1 + dy1*dy1 + dz1*dz1);
	  dr2 = sqrt(dx2*dx2 + dy2*dy2 + dz2*dz2);
	  ang = acos((dx2*dx1+dy2*dy1+dz2*dz1)/(dr2*dr1))/PI180;
	    g_string_append_printf(mesureData->info,
				   _("           i.e. dr = %7.3f  dr2 = %7.3f  \n"
				     "  (Ref-Ref2, Ref-New) = %5.2f degrees"),
				   dr1, dr2, ang);
        }
    }

  DBG_fprintf(stderr, "Pick mesure info : %s\n", mesureData->info->str);
  DBG_fprintf(stderr, "Pick mesure error : %s\n", mesureData->errors->str);
}

gchar* getPickMesureInfos(PickMesure *mesureData)
{
  g_return_val_if_fail(mesureData, (gchar*)0);

  if (!mesureData->info || !mesureData->formatFlag)
    return (gchar*)0;
  return mesureData->info->str;
}
gchar* getPickMesureErrors(PickMesure *mesureData)
{
  g_return_val_if_fail(mesureData, (gchar*)0);

  if (!mesureData->errors || !mesureData->formatFlag)
    return (gchar*)0;
  return mesureData->errors->str;
}
gboolean pickMesureGet_newsAvailable(PickMesure *mesureData)
{
  g_return_val_if_fail(mesureData, FALSE);

  return mesureData->newValuesAvailable;
}
void rebuildMarkListOnFileChange(GObject *visu, VisuData *dataObj, gpointer data)
{
  GList *list;
  PickMesure *pick;
  struct MarkInfo_struct* mark;
  gboolean remove;

  g_return_if_fail(data);

  DBG_fprintf(stderr, "Visu PickMesure : caught the 'dataReadyForRendering'"
	      " signal, updating list of marks.\n");

  pick = (PickMesure*)data;
  if (pick->data != dataObj)
    {
      DBG_fprintf(stderr, " | New VisuData, freeing handle on last VisuData (%p).\n",
		  (gpointer)pick->data);
      g_object_unref(G_OBJECT(pick->data));
      if (dataObj)
	{
	  pick->onPositionChangeSignalId =
	    g_signal_connect(G_OBJECT(dataObj), "NodePositionChanged",
			     G_CALLBACK(updateListOnNodeChange), (gpointer)pick);
	  pick->onNodeRenderedChangeSignalId =
	    g_signal_connect(G_OBJECT(dataObj), "NodeRenderedChanged",
			     G_CALLBACK(updateListOnNodeChange), (gpointer)pick);
	  pick->onElementRenderedChangeSignalId =
	    g_signal_connect(G_OBJECT(dataObj), "ElementRenderedChanged",
			     G_CALLBACK(updateListOnElementChange), (gpointer)pick);
	}
    }
  if (!dataObj)
    {
      DBG_fprintf(stderr, " | No new VisuData, emptying list.\n");
      /* Empty mark list. */
      list = pick->storedMarks;
      while (list)
	{
	  g_free(list->data);
	  list = g_list_next(list);
	}
      g_list_free(pick->storedMarks);
      pick->storedMarks = (GList*)0;
      pick->ref1 = (VisuNode*)0;
      pick->ref2 = (VisuNode*)0;
      pick->selected = (VisuNode*)0;
    }
  else
    {
      DBG_fprintf(stderr, " | New VisuData, updating distance mark list.\n");
      if (pick->data != dataObj)
	{
	  pick->data = dataObj;
	  g_object_ref(G_OBJECT(pick->data));
	}

      /* Try to match previous marks on new VisuData. */
      list = pick->storedMarks;
      while (list)
	{
	  mark = (struct MarkInfo_struct*)list->data;
	  list = g_list_next(list);
	  DBG_fprintf(stderr, " | actual mark %p of type %d.\n", (gpointer)mark, mark->type);
	  remove = FALSE;
	  /* We remove all dot marks. */
	  /* If the distance mark can't match on new data, we remove also. */
	  if (mark->type < PICK_MESURE_END_MARK_DOT)
	    {
	      mark->node1 = visuDataGet_nodeFromId(dataObj, mark->eleNode1, mark->posNode1);
	      remove = remove || !mark->node1;
	    }
	  else if (mark->type > PICK_MESURE_END_MARK_DOT &&
		   mark->type < PICK_MESURE_END_MARK_LINK)
	    {
	      mark->node1 = visuDataGet_nodeFromId(dataObj, mark->eleNode1, mark->posNode1);
	      mark->node2 = visuDataGet_nodeFromId(dataObj, mark->eleNode2, mark->posNode2);
	      remove = remove || !(mark->node1 && mark->node2);
	    }
	  if (remove)
	    {
	      pick->storedMarks = g_list_remove(pick->storedMarks, mark);
	      DBG_fprintf(stderr, " | removing mark %p of type %d.\n", (gpointer)mark, mark->type);
	    }
	}
      DBG_fprintf(stderr, " | Mark list is now pointing to %p.\n", (gpointer)pick->storedMarks);
      if (pick->ref1)
	pick->ref1 = visuDataGet_nodeFromId(dataObj, pick->eleRef1, pick->posRef1);
      if (pick->ref2)
	pick->ref2 = visuDataGet_nodeFromId(dataObj, pick->eleRef2, pick->posRef2);
      if (pick->selected)
	pick->selected = visuDataGet_nodeFromId(dataObj, pick->eleSelected,
						pick->posSelected);
    }
  updateMarkList(pick->data, pick->storedMarks);
}
VisuNode* pickMesureGet_selectedNode(PickMesure *mesureData)
{
  g_return_val_if_fail(mesureData, (VisuNode*)0);

  return mesureData->selected;
}
VisuNode* pickMesureGet_firstReference(PickMesure *mesureData)
{
  g_return_val_if_fail(mesureData, (VisuNode*)0);

  return mesureData->ref1;
}
void updateListOnNodeChange(VisuData *dataObj, gpointer data)
{
  g_return_if_fail(data);

  DBG_fprintf(stderr, "Visu PickMesure : caught a node rendering"
	      " changing signal (%p).\n", data);
  updateMarkList(dataObj, ((PickMesure*)data)->storedMarks);
}
void updateListOnElementChange(VisuData *dataObj, VisuElement *ele, gpointer data)
{
  g_return_if_fail(data);

  DBG_fprintf(stderr, "Visu PickMesure : caught an element rendering"
	      " changing signal (%p).\n", data);
  updateMarkList(dataObj, ((PickMesure*)data)->storedMarks);
}
void pickMesureSet_storeDistance(PickMesure *mesureData, gboolean storeDistance)
{
  g_return_if_fail(mesureData);

  DBG_fprintf(stderr, "Visu PickMesure : set store distance to %d.\n", (int)storeDistance);
  mesureData->storeDistance = storeDistance;
  if (storeDistance)
    openGLText_initFontList();
}
