/*   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_pairs.h"
#include "pairsModeling/externalPairsExtensions.h"

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

#include <math.h>

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

#define PAIRS_ONOFF_DEFAULT 0

struct foreachPairsData_struct
{
  foreachPairsFunc func;
  gpointer userData;
};

OpenGLExtension* extensionPairs;
int openGlListPairsId;

/* This hashtable as VisuElement* as keys and pointer to other hashtable as value.
   The main idea is to have an hashtable of 2 dimension, each two keys are
   VisuElement* and the final values are PairsData. Nevertheless, this hashtable
   must not be access directly but through the get and put methods. */
GHashTable *DminDmax;

GList *availablePairsExtensions;
PairsExtension *currentPairsExtension;
gboolean rebuildPairsNeeded;

/* Parameters */
Color *defaultPairColor;

void newDataAvailable(GObject* obj, VisuData *dataObj, gpointer data);
void rebuildPairs(VisuData *dataObj);
void facetteChanged(VisuData* obj, gpointer data);
void createPairsOnNodeRenderedChanged(VisuData *dataObj, gpointer data);
void createPairsOnElementRenderedChanged(VisuData *dataObj, VisuElement *element, gpointer data);
void createPairsOnNodePositionChanged(VisuData *dataObj, gpointer data);
static void createPairsOnResources(GObject *obj, gpointer bool);

void freeGHTable(gpointer ele);
void freePairsData(gpointer ele);

/* This function details how to read what is needed by PairsData. */
gboolean readPairsData(gchar **lines, int nbLines,
		       int position, GString *errorMessage);
gboolean readPairsAreOn(gchar **lines, int nbLines,
			int position, GString *errorMessage);
gboolean readFavPairsMethod(gchar **lines, int nbLines,
			    int position, GString *errorMessage);
/* This function save the resources. */
gboolean exportResourcesPairs(GString *data, int *nbLinesWritten);
#define FLAG_RESOURCES_PAIRS      "pairs_are_on"
#define DESC_RESOURCES_PAIRS      "Ask the opengl engine to draw pairs between elements ; boolean 0 or 1"
#define RESOURCES_PAIRS_DEFAULT 0
#define FLAG_RESOURCES_PAIRS_DATA "pair_data"
#define DESC_RESOURCES_PAIRS_DATA "Draw pairs between [ele1] [ele2] [0. <= dmin] [0. <= dmax] [0. <= RGB <= 1.]x3"

#define FLAG_RESOURCES_FAV_PAIRS  "pairs_favoriteMethod"
#define DESC_RESOURCES_FAV_PAIRS  "Favorite method used to render files ; chain"



int initPairsModule()
{
  char *name = _("Pairs");
  char *description = _("Draw pairs between elements with a criterion of distance.");
  int i, res;
  PairsExtension *extension;
  float rgbOfPairs[4] = {1.0, 0.6, 0.2, 1.};
  VisuConfigFileEntry *resourceEntry;

  openGlListPairsId = openGLObjectList_new(1);
  extensionPairs = OpenGLExtension_new(name, description, openGlListPairsId, rebuildPairs);
  OpenGLExtensionSet_priority(extensionPairs, OPENGL_EXTENSION_PRIORITY_HIGH);
  OpenGLExtensionSet_sensitiveToRenderingMode(extensionPairs, TRUE);
  extensionPairs->used = PAIRS_ONOFF_DEFAULT;
  OpenGLExtensionRegister(extensionPairs);

  /* Create a VisuModule to registered the new resources as
     rgb and material. */
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_RESOURCE,
					  FLAG_RESOURCES_PAIRS,
					  DESC_RESOURCES_PAIRS,
					  1, readPairsAreOn);
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_RESOURCE,
					  FLAG_RESOURCES_PAIRS_DATA,
					  DESC_RESOURCES_PAIRS_DATA,
					  1, readPairsData);
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_RESOURCE,
					  FLAG_RESOURCES_FAV_PAIRS,
					  DESC_RESOURCES_FAV_PAIRS,
					  1, readFavPairsMethod);
  visuConfigFileAdd_exportFunction(VISU_CONFIGFILE_RESOURCE,
				   exportResourcesPairs);


  g_signal_connect(G_OBJECT(visu), "dataReadyForRendering",
		   G_CALLBACK(newDataAvailable), (gpointer)0);
  g_signal_connect(G_OBJECT(visu), "resourcesLoaded",
		   G_CALLBACK(createPairsOnResources), (gpointer)0);

  DminDmax = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, freeGHTable);
  
  defaultPairColor = colorNew_floatRGBA(rgbOfPairs);
  colorAdd_color(defaultPairColor);

  /* Initialise the pairs extensions. */
  availablePairsExtensions = (GList*)0;
  res = 1;
  for (i = 0; listInitPairsFunc[i]; i++)
    {
      extension = listInitPairsFunc[i]();
      if (!extension)
	res = 0;
      registerPairsExtension(extension);
    }
  if (!res)
    g_warning("Some pairs extensions can't initialse.\n");

  rebuildPairsNeeded = TRUE;
  
  if (availablePairsExtensions)
    currentPairsExtension = (PairsExtension*)availablePairsExtensions->data;
  else
    currentPairsExtension = (PairsExtension*)0;

  return res;
}

void freeGHTable(gpointer ele)
{
  g_hash_table_destroy((GHashTable*)ele);
}
void freePairsData(gpointer ele)
{
  free((PairsData*)ele);
}

/* Create a new PairsData structure with default values.
   The newly created structure can be freed by a call to free. */
PairsData* newPairsData(char *name1, char* name2)
{
  PairsData *data;

  if (!name1 || !name2)
    return (PairsData*)0;

  data = malloc(sizeof(PairsData));
  if (!data)
    {
      allocationProblems();
      exit(1);
    }
  data->minMax[0] = 0.;
  data->minMax[1] = 0.;
  data->properties = g_hash_table_new(g_str_hash, g_str_equal);
  if (!data->properties)
    {
      allocationProblems();
      exit(1);
    }
  g_hash_table_insert(data->properties, (gpointer)"color", (gpointer)defaultPairColor);
  data->drawn = TRUE;
  data->name1 = name1;
  data->name2 = name2;
  return data;
}

int setPairsOnOff(int onOff)
{
  if (onOff == extensionPairs->used)
    return 0;

  extensionPairs->used = onOff;
  DBG_fprintf(stderr, "Visu Pairs : change pairs on/off status (rebuild"
	      " flag is %d).\n", rebuildPairsNeeded);
/*   createPairs(); */
  return 1;
}
void setPairsAreOutOfDate()
{
  rebuildPairsNeeded = TRUE;
}
int setPairsMethod(PairsExtension *extension)
{
  DBG_fprintf(stderr, "Visu Pairs : set pairs drawing method to '%s'"
	      " (previous '%s').\n", extension->name, currentPairsExtension->name);

  if (extension == currentPairsExtension)
    return 0;
  
  currentPairsExtension = extension;

  rebuildPairsNeeded = TRUE;

  if (!extensionPairs->used)
    return 0;
  return 1;
}
int setPairsAll(PairsData *data, float rgb[3], float minMax[2])
{
  int i;
  int chgt;
  Color *color;
  float rgba[4];

  if (!data || rgb[0] < 0. || rgb[0] > 1. ||
      rgb[1] < 0. || rgb[1] > 1. ||
      rgb[2] < 0. || rgb[2] > 1. ||
      minMax[0] < 0. || minMax[1] < 0.)
    {
      fprintf(stderr, "WARNING! The parameters of function setPairsAll are wrong.\n");
      return 0;
    }

  for(i = 0; i < 3; i++)
    rgba[i] = rgb[i];
  rgba[3] = 1.;
  color = colorAdd_floatRGBA(rgba, (int*)0);

  chgt = setPairsColor(data, color);
  chgt = setPairsDistance(minMax[PAIRS_MIN], data, PAIRS_MIN) || chgt;
  chgt = setPairsDistance(minMax[PAIRS_MAX], data, PAIRS_MAX) || chgt;
  
  if (chgt)
    rebuildPairsNeeded = TRUE;

  if (!chgt || !data->drawn || !extensionPairs->used)
    return 0;
  return 1;
}
int setPairsDrawn(PairsData *data, gboolean drawn)
{
  if (!data)
    {
      fprintf(stderr, "WARNING! The parameters of function setPairsDrawn are wrong.\n");
      return 0;
    }
  if (data->drawn == drawn)
    return 0;
  data->drawn = drawn;

  rebuildPairsNeeded = TRUE;
  return 1;
}
gboolean getPairsDrawn(PairsData *data)
{
  if (!data)
    {
      fprintf(stderr, "WARNING! The parameters of function getPairsDrawn are wrong.\n");
      return TRUE;
    }
  return data->drawn;
}
int setPairsColor(PairsData *data, Color* destColor)
{
  Color *color;

  if (!data || !destColor )
    {
      fprintf(stderr, "WARNING! The parameters of function setPairsColor are wrong.\n");
      return 0;
    }
  color = g_hash_table_lookup(data->properties, (gpointer)"color");
  if (!color)
    {
      fprintf(stderr, "WARNING! Can't find the '%s' property in '%s'.\n",
	      "color", "setPairsColor");
      return 0;
    }
  if (color == destColor)
    return 0;
  g_hash_table_insert(data->properties, (gpointer)"color", (gpointer)destColor);

  rebuildPairsNeeded = TRUE;
  return 1;
}
int setPairsDistance(float val, PairsData *data, int minOrMax)
{
  if (!data || val < 0. || (minOrMax != PAIRS_MIN && minOrMax != PAIRS_MAX) )
    {
      fprintf(stderr, "WARNING! The parameters of function setPairsDistanceWithData are wrong.\n");
      return 0;
    }
  if (data->minMax[minOrMax] == val)
    return 0;
  data->minMax[minOrMax] = val;
  if (data->minMax[PAIRS_MIN] > data->minMax[PAIRS_MAX] ||
      data->minMax[PAIRS_MAX] == 0.)
    data->drawn = 0;
  else
    data->drawn = 1;
  rebuildPairsNeeded = TRUE;

  if (!extensionPairs->used)
    return 0;

  return 1;
}
void setPairsProperty(PairsData *data, const gchar* key, gpointer value)
{
  if (data && key)
    g_hash_table_insert(data->properties, (gpointer)key, (gpointer)value);

  rebuildPairsNeeded = TRUE;
}

int getPairsOnOff()
{
  return extensionPairs->used;
}
GList* getAllPairsMethods()
{
  return availablePairsExtensions;
}
char* getNameOfPairsMethod(PairsExtension *extension)
{
  if (!extension)
    return (char*)0;

  return extension->name;
}
float getPairsDistance(VisuElement *ele1, VisuElement *ele2, int minOrMax)
{
  PairsData *data;

  if (minOrMax != PAIRS_MIN && minOrMax != PAIRS_MAX)
    {
      fprintf(stderr, "WARNING! The parameters of function getPairsDistance are wrong.\n");
      return 0.;
    }
  data = getPairsData(ele1, ele2);
  if (!data)
    return 0.;
  return data->minMax[minOrMax];
}
gpointer getPairsProperty(PairsData *data, const gchar* key)
{
  if (data)
    return g_hash_table_lookup(data->properties, (gpointer)key);
  else
    return (gpointer)0;
}

PairsData* getPairsData(VisuElement *ele1, VisuElement *ele2)
{
  GHashTable *table, *tableTmp;
  PairsData *data;
  VisuElement *tmp1, *tmp2;
  VisuElement *l1OK, *l2OK;

  if (!ele1 || !ele2)
    {
      fprintf(stderr, "WARNING! The parameters of function getPairsData are wrong.\n");
      return (PairsData*)0;
    }

  tmp1 = ele1;
  tmp2 = ele2;
  l1OK = (VisuElement*)0;
  l2OK = (VisuElement*)0;
  /* Search for ele1 first */
  data = (PairsData*)0;
  table = (GHashTable*)0;
  tableTmp = (GHashTable*)g_hash_table_lookup(DminDmax, (gpointer)ele1);
  if (tableTmp)
    {
      l1OK = ele1;
      table = tableTmp;
      tmp1 = ele1;
      tmp2 = ele2;
      data = (PairsData*)g_hash_table_lookup(table, (gpointer)ele2);
      if (data)
	l2OK = ele2;
    }
  /* Search for ele2 first */
  if (!l2OK)
    {
      tableTmp = (GHashTable*)g_hash_table_lookup(DminDmax, (gpointer)ele2);
      if (tableTmp)
	{
	  l1OK = ele2;
	  table = tableTmp;
	  tmp1 = ele2;
	  tmp2 = ele1;
	  data = (PairsData*)g_hash_table_lookup(table, (gpointer)ele1);
	  if (data)
	    l2OK = ele1;
	}
    }
  if (!l1OK)
    {
      /* None found create a new one. */
      table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
				    freePairsData);
      if (!table)
	{
	  allocationProblems();
	  exit(1);
	}
      g_hash_table_insert(DminDmax, (gpointer)ele1, (gpointer)table);
      data = newPairsData(ele1->name, ele2->name);
      g_hash_table_insert(table, (gpointer)ele2, (gpointer)data);
    }
  else
    {
      if (!l2OK)
	{
	  data = newPairsData(tmp1->name, tmp2->name);
	  g_hash_table_insert(table, (gpointer)tmp2, (gpointer)data);
	}
    }
  return data;
}
PairsExtension* getCurrentPairsMethods()
{
  return currentPairsExtension;
}


void createPairsOnNodeRenderedChanged(VisuData *dataObj, gpointer data)
{
  DBG_fprintf(stderr, "Visu Pairs : catch 'NodeRenderedChanged' signal,"
	      " recreating pairs...\n");
  rebuildPairsNeeded = TRUE;
  createPairs(dataObj);
}
void createPairsOnElementRenderedChanged(VisuData *dataObj, VisuElement *element, gpointer data)
{
  DBG_fprintf(stderr, "Visu Pairs : catch 'ElementRenderedChanged' signal,"
	      " recreating pairs...\n");
  rebuildPairsNeeded = TRUE;
  createPairs(dataObj);
}
void createPairsOnNodePositionChanged(VisuData *dataObj, gpointer data)
{
  DBG_fprintf(stderr, "Visu Pairs : catch 'NodePositionChanged' signal,"
	      " recreating pairs...\n");
  rebuildPairsNeeded = TRUE;
  createPairs(dataObj);
}
static void createPairsOnResources(GObject *obj, gpointer bool)
{
  GList *tmpLst;

  if (!rebuildPairsNeeded)
    return;

  DBG_fprintf(stderr, "Visu Pairs : catch 'resourcesLoaded' signal,"
	      " recreating pairs...\n");
  tmpLst = visuDataGet_allObjects();
  while (tmpLst)
    {
      createPairs(VISU_DATA(tmpLst->data));
      tmpLst = g_list_next(tmpLst);
    }
}

int createPairs(VisuData *dataObj)
{
  int i, j;
  int ii, jj;
  float d2, d2min, d2max;
  PairsData *data;
  VisuElement *ele1, *ele2;
  float xyz1[3], xyz2[3];
  OpenGLView *view;

  if (!dataObj)
    {
      glDeleteLists(openGlListPairsId, 1);
      rebuildPairsNeeded = TRUE;
      return 1;
    }

  if (!extensionPairs->used)
    {
      DBG_fprintf(stderr, "Visu Pairs : aborting creating pairs, extension is not used.\n");
      return 0;
    }
  if (!rebuildPairsNeeded)
    {
      DBG_fprintf(stderr, "Visu Pairs : aborting creating pairs, pairs are up to date.\n");
      return 1;
    }

  DBG_fprintf(stderr, "Visu Pairs : creating pairs between elements (%s).\n",
	      currentPairsExtension->name);

  view = visuDataGet_openGLView(dataObj);

  glDeleteLists(openGlListPairsId, 1);
  if (!currentPairsExtension)
    {
      fprintf(stderr, "WARNING! Can't create the pairs list since no"
	      " method is currently specified.\n");
      return 0;
    }

  rebuildPairsNeeded = FALSE;
  glNewList(openGlListPairsId, GL_COMPILE);

  if (currentPairsExtension->initOpenGl)
    {
      DBG_fprintf(stderr, "Visu Pairs : call to init OpenGl (%d).\n",
		  GPOINTER_TO_INT(currentPairsExtension->initOpenGl));
      currentPairsExtension->initOpenGl();
    }

  for(i = 0; i < dataObj->ntype; i++)
    {
      if (!visuElementGet_rendered(dataObj->fromIntToVisuElement[i]))
	continue;
      for(j = 0; j <= i; j++)
	{
	  if (!visuElementGet_rendered(dataObj->fromIntToVisuElement[j]))
	    continue;
	  ele1 = dataObj->fromIntToVisuElement[i];
	  ele2 = dataObj->fromIntToVisuElement[j];
	  data = getPairsData(ele1, ele2);
	  if (!data->drawn)
	    continue;
	  d2min = data->minMax[PAIRS_MIN] * data->minMax[PAIRS_MIN];
	  d2max = data->minMax[PAIRS_MAX] * data->minMax[PAIRS_MAX];
	  if (currentPairsExtension->beginDrawingPairs)
	    currentPairsExtension->beginDrawingPairs(ele1, ele2, data);
	  if(d2min < d2max && d2max > 0.)
	    {
	      for(ii = 0; ii < dataObj->numberOfStoredNodes[i]; ii++)
		{
		  if (dataObj->nodes[i][ii].rendered)
		    {
		      for(jj = 0; jj < dataObj->numberOfStoredNodes[j]; jj++)
			{
			  if (dataObj->nodes[j][jj].rendered)
			    {
			      if (i == j && jj >= ii)
				break;
			      visuDataGet_nodePosition(dataObj,
						       dataObj->nodes[i] + ii, xyz1);
			      visuDataGet_nodePosition(dataObj,
						       dataObj->nodes[j] + jj, xyz2);
			      d2 = (xyz1[0] - xyz2[0]) * (xyz1[0] - xyz2[0]) + 
				(xyz1[1] - xyz2[1]) * (xyz1[1] - xyz2[1]) + 
				(xyz1[2] - xyz2[2]) * (xyz1[2] - xyz2[2]);
			      if(d2 > 0. && d2 >= d2min && d2 <= d2max)
				currentPairsExtension->drawPairs(ele1, ele2, data, view,
								 xyz1[0] - view->box->dxxs2,
								 xyz1[1] - view->box->dyys2,
								 xyz1[2] - view->box->dzzs2,
								 xyz2[0] - view->box->dxxs2,
								 xyz2[1] - view->box->dyys2,
								 xyz2[2] - view->box->dzzs2,
								 d2);
			    }
			}
		    }
		}
	    }
	  if (currentPairsExtension->endDrawingPairs)
	    currentPairsExtension->endDrawingPairs(ele1, ele2, data);
	}
    }

  if (currentPairsExtension->stopOpenGl)
    {
      DBG_fprintf(stderr, "Visu Pairs : call to stop OpenGl (%d).\n",
		  GPOINTER_TO_INT(currentPairsExtension->stopOpenGl));
      currentPairsExtension->stopOpenGl();
    }
      
  glEndList();
  
  return 1;
}

void rebuildPairs(VisuData *dataObj)
{
  if (dataObj && extensionPairs->used)
    {
      /* Force to recreate pairs. */
      rebuildPairsNeeded = TRUE;
      createPairs(dataObj);
    }
}

void newDataAvailable(GObject* obj, VisuData *dataObj, gpointer data)
{
  if (dataObj)
    {
      g_signal_connect(G_OBJECT(dataObj), "ElementRenderedChanged",
		       G_CALLBACK(createPairsOnElementRenderedChanged), (gpointer)0);
      g_signal_connect(G_OBJECT(dataObj), "NodeRenderedChanged",
		       G_CALLBACK(createPairsOnNodeRenderedChanged), (gpointer)0);
      g_signal_connect(G_OBJECT(dataObj), "NodePositionChanged",
		       G_CALLBACK(createPairsOnNodePositionChanged), (gpointer)0);
      g_signal_connect(G_OBJECT(dataObj), "OpenGLFacetteChanged",
		       G_CALLBACK(facetteChanged), (gpointer)0);

      rebuildPairsNeeded = TRUE;
    }

  if (extensionPairs->used)
    createPairs(dataObj);
}

void facetteChanged(VisuData* obj, gpointer data)
{
  if (extensionPairs->used && currentPairsExtension->sensitiveToFacette)
    createPairs(obj);
}




/*****************************************/
/* Methods to organize pairs extensions. */
/*****************************************/

PairsExtension* pairsExtension_new(char* name, char* printName, char* description, int sensitive,
				   initEndOpenGlPairsFunc init, initEndOpenGlPairsFunc stop,
				   startEndPairsFunc start, startEndPairsFunc end,
				   pairDefinitionFunc draw)
{
  PairsExtension *extension;

  extension = malloc(sizeof(PairsExtension));
  if (!extension)
    {
      allocationProblems();
      return (PairsExtension*)0;
    }
  extension->name = (char*)0;
  extension->printName= (char*)0;
  extension->description = (char*)0;
  extension->name = malloc(sizeof(char) * (strlen(name) + 1));
  if (!extension->name)
    {
      allocationProblems();
      PairsExtension_free(extension);
      return (PairsExtension*)0;
    }
  strcpy(extension->name, name);
  extension->printName = g_strdup(printName);
  if (!extension->printName)
    {
      allocationProblems();
      PairsExtension_free(extension);
      return (PairsExtension*)0;
    }
  extension->description = malloc(sizeof(char) * (strlen(description) + 1));
  if (!extension->description)
    {
      allocationProblems();
      PairsExtension_free(extension);
      return (PairsExtension*)0;
    }
  strcpy(extension->description, description);
  
  extension->sensitiveToFacette = sensitive;

  extension->initOpenGl = init;
  extension->stopOpenGl = stop;
  extension->beginDrawingPairs = start;
  extension->endDrawingPairs = end;
  extension->drawPairs = draw;

  return extension;
}
void PairsExtension_free(PairsExtension* extension)
{
  if (!extension)
    return;
  if (extension->name)
    free(extension->name);
  if (extension->printName)
    free(extension->printName);
  if (extension->description)
    free(extension->description);
  free(extension);
}
void registerPairsExtension(PairsExtension *extension)
{
  DBG_fprintf(stderr, "Visu Pairs : registering a new pairs extension ... ");
  g_return_if_fail(extension && extension->drawPairs && extension->name);

  availablePairsExtensions = g_list_append(availablePairsExtensions, (gpointer)extension);
  DBG_fprintf(stderr, "'%s' (%d).\n", extension->name, GPOINTER_TO_INT(extension));
}
int isValidPairsExtension(PairsExtension* pairsModel)
{
  GList *pnt;

  pnt = availablePairsExtensions;
  while (pnt)
    {
      if (pnt->data == pairsModel)
	return 1;
      pnt = g_list_next(pnt);
    }
  return 0;
}

void foreachLevel2(gpointer key, gpointer value, gpointer userData)
{
  PairsData *data;
  struct foreachPairsData_struct *storage;

  data = (PairsData*)value;
  storage = (struct foreachPairsData_struct *)userData;
  storage->func(data, storage->userData);
}
void foreachLevel1(gpointer key, gpointer value, gpointer data)
{
  g_hash_table_foreach((GHashTable*)value, (GHFunc)foreachLevel2, data);
}
void foreachPairsData(foreachPairsFunc whatToDo, gpointer userData)
{
  struct foreachPairsData_struct storage;

  storage.func = whatToDo;
  storage.userData = userData;
  g_hash_table_foreach(DminDmax, (GHFunc)foreachLevel1, (gpointer)(&storage));
}


/*****************************************/
/* Dealing with parameters and resources */
/*****************************************/

/* This function details how to read what is needed by PairsData. */
gboolean readPairsData(gchar **lines, int nbLines,
		      int position, GString *errorMessage)
{
  char *cursor;
  VisuElement* ele1, *ele2;
  int res, errorFlag;
  float rgb[4];
  float minMax[2];
  PairsData *data;

  cursor = lines[0];
  ele1 = visuElementGet_fromValueLine(&cursor, -1, &errorFlag);
  if (!ele1 )
    {
      if (errorMessage)
	visuElementPrint_errorFromValueLine(errorMessage, position, errorFlag);
      return FALSE;
    }
  ele2 = visuElementGet_fromValueLine(&cursor, -1, &errorFlag);
  if (!ele2 )
    {
      if (errorMessage)
	visuElementPrint_errorFromValueLine(errorMessage, position, errorFlag);
      return FALSE;
    }
  res = sscanf(cursor, "%f %f %f %f %f", &minMax[0], &minMax[1],
	       &rgb[0], &rgb[1], &rgb[2]);
  if (res != 5)
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 5 floating point values must appear"
					       " after the %s markup.\n"),
			       position, FLAG_RESOURCES_PAIRS_DATA);
      return FALSE;
    }
  res = 0;
  res = res || visuConfigFileSet_floatValue(&rgb[0], rgb[0], 0., 1.);
  res = res || visuConfigFileSet_floatValue(&rgb[1], rgb[1], 0., 1.);
  res = res || visuConfigFileSet_floatValue(&rgb[2], rgb[2], 0., 1.);
  res = res || visuConfigFileSet_floatValue(&minMax[0], minMax[0], 0., -1.);
  res = res || visuConfigFileSet_floatValue(&minMax[1], minMax[1], 0., -1.);
  if (res)
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 5 floating point values must appear"
					       " after the %s markup.\n"),
			       position, FLAG_RESOURCES_PAIRS_DATA);
      return FALSE;
    }
  data = getPairsData(ele1, ele2);
  if (!data)
    return 1;

  rgb[3] = 1.;
/*   colorAdd_floatRGBA(rgb); */
  setPairsAll(data, rgb, minMax);
  
  return TRUE;
}
/* This function details how to read the parameter FLAG_PARAMETER_PAIRS. */
gboolean readPairsAreOn(gchar **lines, int nbLines,
			int position, GString *errorMessage)
{
  int res, val;

  res = sscanf(lines[0],"%d", &val);
  if (res != 1)
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 1 boolean value must appear"
					       " after the %s markup.\n"),
			       position, FLAG_RESOURCES_PAIRS);
      val = RESOURCES_PAIRS_DEFAULT;
      res = 1;
    }
  else
    res = 0;
  setPairsOnOff(val);
/*   createPairs(); */

  if (res)
    return FALSE;
  else
    return TRUE;
}
gboolean readFavPairsMethod(gchar **lines, int nbLines,
			    int position, GString *errorMessage)
{
  char *methName;
  GList *pairsMethods;

  lines[0] = g_strstrip(lines[0]);

  if (!lines[0][0])
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 1 string value must appear"
					       " after the %s markup.\n"),
			       position, FLAG_RESOURCES_FAV_PAIRS);
      return FALSE;
    }
  else
    {
      pairsMethods = availablePairsExtensions;
      while (pairsMethods && strcmp(((PairsExtension*)pairsMethods->data)->name, lines[0]))
	pairsMethods = g_list_next(pairsMethods);
      if (!pairsMethods)
	{
	  if (errorMessage)
	    g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
						   " the specified method (%s) is unknown.\n"),
				   position, lines[0]);
	  return FALSE;
	}
      setPairsMethod((PairsExtension*)pairsMethods->data);
    }

  return TRUE;
}
/* These functions save the resources. */
void exportLevel2(gpointer key, gpointer value, gpointer dataUser)
{
  struct foreachFuncExport_struct *str;
  PairsData *data;
  Color *color;
  
  str = (struct foreachFuncExport_struct*)dataUser;
  data = (PairsData*)value;
  
  color = g_hash_table_lookup(data->properties, (gpointer)"color");
  if (!color)
    {
      fprintf(stderr, "WARNING! Can't find the 'color' property when exporting pairs data.\n");
      return;
    }

  g_string_append_printf(str->data, "%s:\n", FLAG_RESOURCES_PAIRS_DATA);
  g_string_append_printf(str->data, "    %s %s %4.3f %4.3f   %4.3f %4.3f %4.3f\n",
			 data->name1, data->name2, data->minMax[PAIRS_MIN],
			 data->minMax[PAIRS_MAX], color->rgba[PAIRS_COLOR_R],
			 color->rgba[PAIRS_COLOR_G], color->rgba[PAIRS_COLOR_B]);
  *str->nbLinesWritten += 2;
}
void exportLevel1(gpointer key, gpointer value, gpointer data)
{
  g_hash_table_foreach((GHashTable*)value, (GHFunc)exportLevel2, data);
}
gboolean exportResourcesPairs(GString *data, int *nbLinesWritten)
{
  struct foreachFuncExport_struct str;
  GList *pairsMeth;

  g_string_append_printf(data, "# %s\n", DESC_RESOURCES_PAIRS);
  g_string_append_printf(data, "%s:\n    %i\n", FLAG_RESOURCES_PAIRS,
			 (int)extensionPairs->used);
  *nbLinesWritten += 3;

  if (currentPairsExtension)
    {
      g_string_append_printf(data, "# %s (", DESC_RESOURCES_FAV_PAIRS);
      pairsMeth = availablePairsExtensions;
      while (pairsMeth)
	{
	  g_string_append_printf(data, "'%s'", ((PairsExtension*)pairsMeth->data)->name);
	  if (pairsMeth->next)
	    g_string_append_printf(data, ", ");
	  pairsMeth = g_list_next(pairsMeth);
	}
      g_string_append_printf(data, ")\n");
      g_string_append_printf(data, "%s:\n    %s\n", FLAG_RESOURCES_FAV_PAIRS,
			     currentPairsExtension->name);
      *nbLinesWritten += 3;
    }

  g_string_append_printf(data, "# %s\n", DESC_RESOURCES_PAIRS_DATA);
  *nbLinesWritten = 1;
  str.data = data;
  str.nbLinesWritten = nbLinesWritten;
  g_hash_table_foreach(DminDmax, (GHFunc)exportLevel1, (gpointer)&str);

  g_string_append_printf(data, "\n");
  *nbLinesWritten += 1;

  return TRUE;
}
