/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	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-2005)

	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 <math.h>
#include <string.h>

#include "visu_nodes.h"

/**
 * SECTION:visu_pairs
 * @short_description: V_Sim can draw link between nodes. This part
 * defines a pair object and interface to draw pairs.
 *
 * <para>The visu_pairs.c defines only general methods to draw
 * pairs. It introduces a new object called #VisuPairLink. This stores
 * some characteristics on links between two #VisuElement. The main
 * characteristic is that pairs are drawn only if the length between
 * two nodes is in a specific range. Use visu_pair_link_setDistance() and
 * visu_pair_link_getDistance() to tune this range.</para>
 *
 * <para>This file does not draw any pairs. But it gives some
 * interface to create rendering capabilities. To create a new pair
 * rendering module, called #VisuGlExtPairs, use
 * visu_gl_ext_pairs_new(). Basically, a #VisuGlExtPairs is characterized
 * by it drawing method. But it can have other methods that are called
 * in different cases. See main() and
 * startStop() prototypes to have more informations.</para>
 */

/**
 * VisuPairClass:
 * @parent: the parent class.
 *
 * Class of #VisuPair objects.
 */
/**
 * VisuPair:
 * 
 * An opaque structure to define links (i.e. several #VisuPairLink)
 * between elements.
 */
/* This structure is made to store pairs information between two
   elements. */
struct _VisuPairPrivate
{
  gboolean dispose_has_run;

  VisuElement *ele1;
  VisuElement *ele2;

  /* This is a set of link (VisuPairLink). */
  GArray *links;
};

enum
  {
    PROP_0,
    ELE1_PROP,
    ELE2_PROP,
    LINKS_PROP,
    N_PROP
  };
static GParamSpec *_properties[N_PROP];

/* Local methods. */
static void visu_pair_dispose(GObject* obj);
static void visu_pair_finalize(GObject* obj);
static void visu_pair_get_property(GObject* obj, guint property_id,
                                   GValue *value, GParamSpec *pspec);
static void visu_pair_set_property(GObject* obj, guint property_id,
                                   const GValue *value, GParamSpec *pspec);

G_DEFINE_TYPE_WITH_CODE(VisuPair, visu_pair, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuPair))

/********************/
/* Visu pair stuff. */
/********************/
static void visu_pair_class_init(VisuPairClass *klass)
{
  DBG_fprintf(stderr, "Visu Pair: creating the class of the object.\n");
  DBG_fprintf(stderr, "                - adding new signals ;\n");
  
  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_pair_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_pair_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_pair_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_pair_get_property;

  /**
   * VisuPair::first-element:
   *
   * Store the link first element.
   *
   * Since: 3.8
   */
  _properties[ELE1_PROP] = g_param_spec_object("first-element", "First element",
                                               "first element", VISU_TYPE_ELEMENT,
                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  g_object_class_install_property(G_OBJECT_CLASS(klass), ELE1_PROP,
				  _properties[ELE1_PROP]);

  /**
   * VisuPair::second-element:
   *
   * Store the link second element.
   *
   * Since: 3.8
   */
  _properties[ELE2_PROP] = g_param_spec_object("second-element", "Second element",
                                               "second element", VISU_TYPE_ELEMENT,
                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  g_object_class_install_property(G_OBJECT_CLASS(klass), ELE2_PROP,
				  _properties[ELE2_PROP]);

  /**
   * VisuPair::links:
   *
   * Store the set of links of this pair.
   *
   * Since: 3.8
   */
  _properties[LINKS_PROP] = g_param_spec_boxed("links", "Links",
                                               "array of links", G_TYPE_ARRAY,
                                               G_PARAM_READABLE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), LINKS_PROP,
				  _properties[LINKS_PROP]);
}
static void visu_pair_init(VisuPair *pair)
{
  DBG_fprintf(stderr, "Visu Pair: initializing a new object (%p).\n",
	      (gpointer)pair);
  pair->priv = visu_pair_get_instance_private(pair);
  pair->priv->dispose_has_run = FALSE;

  pair->priv->ele1  = (VisuElement*)0;
  pair->priv->ele2  = (VisuElement*)0;
  pair->priv->links = g_array_new(FALSE, FALSE, sizeof(VisuPairLink*));
}
static void visu_pair_get_property(GObject* obj, guint property_id,
                                   GValue *value, GParamSpec *pspec)
{
  VisuPair *self = VISU_PAIR(obj);

  DBG_fprintf(stderr, "Visu Pair: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case ELE1_PROP:
      g_value_set_object(value, self->priv->ele1);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->ele1);
      break;
    case ELE2_PROP:
      g_value_set_object(value, self->priv->ele2);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->ele2);
      break;
    case LINKS_PROP:
      g_value_set_boxed(value, self->priv->links);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->links);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_pair_set_property(GObject* obj, guint property_id,
                                   const GValue *value, GParamSpec *pspec)
{
  VisuPair *self = VISU_PAIR(obj);
  float mM[2] = {0.f, 0.f};
  VisuPairLink *link;

  DBG_fprintf(stderr, "Visu Pair: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case ELE1_PROP:
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      self->priv->ele1 = g_value_dup_object(value);
      break;
    case ELE2_PROP:
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      self->priv->ele2 = g_value_dup_object(value);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
  if (self->priv->ele1 && self->priv->ele2 && !self->priv->links->len)
    {
      link = visu_pair_link_new(self->priv->ele1, self->priv->ele2, mM);
      g_array_append_val(self->priv->links, link);
    }
}
static void visu_pair_dispose(GObject* obj)
{
  VisuPair *data;
  guint i;

  data = VISU_PAIR(obj);
  DBG_fprintf(stderr, "Visu Pair: dispose object %p (%s - %s).\n", (gpointer)obj,
              data->priv->ele1->name, data->priv->ele2->name);

  if (data->priv->dispose_has_run)
    return;
  data->priv->dispose_has_run = TRUE;

  g_object_unref(data->priv->ele1);
  g_object_unref(data->priv->ele2);
  for (i = 0; i < data->priv->links->len; i++)
    g_object_unref(g_array_index(data->priv->links, GObject*, i));

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_pair_parent_class)->dispose(obj);
}
static void visu_pair_finalize(GObject* obj)
{
  VisuPair *data;

  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Visu Pair: finalize object %p.\n", (gpointer)obj);

  data = VISU_PAIR(obj);
  g_array_free(data->priv->links, TRUE);

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Visu Pair: chain to parent.\n");
  G_OBJECT_CLASS(visu_pair_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Visu Pair: freeing ... OK.\n");
}
/**
 * visu_pair_new:
 * @ele1: a #VisuElement object.
 * @ele2: a #VisuElement object.
 *
 * Creates a #VisuPair between @ele1 and @ele2.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created object.
 **/
VisuPair* visu_pair_new(VisuElement *ele1, VisuElement *ele2)
{
  return VISU_PAIR(g_object_new(VISU_TYPE_PAIR, "first-element", ele1,
                                "second-element", ele2, NULL));
}
/**
 * visu_pair_getElements:
 * @pair: a #VisuPair object.
 * @ele1: (out) (allow-none) (transfer none): a location to store a
 * #VisuElement object pointer.
 * @ele2: (out) (allow-none) (transfer none): a location to store a
 * #VisuElement object pointer.
 *
 * Retrieve the #VisuElement constituting the pair.
 *
 * Since: 3.7
 **/
void visu_pair_getElements(const VisuPair *pair, VisuElement **ele1, VisuElement **ele2)
{
  g_return_if_fail(VISU_IS_PAIR(pair));

  if (ele1)
    *ele1 = pair->priv->ele1;
  if (ele2)
    *ele2 = pair->priv->ele2;
}

/**
 * visu_pair_foreach:
 * @pair: a #VisuPair object.
 * @whatToDo: (scope call) (closure user_data): method to apply.
 * @user_data: (closure): some user data.
 *
 * Apply @whatToDo on every #VisuPairLink of @pair.
 *
 * Since: 3.8
 **/
void visu_pair_foreach(VisuPair *pair, VisuPairForeachFunc whatToDo, gpointer user_data)
{
  guint i;

  g_return_if_fail(VISU_IS_PAIR(pair));

  for (i = 0; i < pair->priv->links->len; i++)
    whatToDo(pair, g_array_index(pair->priv->links, VisuPairLink*, i), user_data);
}
/**
 * visu_pair_getLinks:
 * @pair: a #VisuPair object.
 *
 * There can be one or several links between elements, retrieve them
 * with this routine.
 *
 * Returns: (element-type VisuPairLink*) (transfer container): a list of
 * #VisuPairLink. The list content is owned by V_Sim but the list
 * should be freed with g_list_free() after use.
 */
GList* visu_pair_getLinks(VisuPair *pair)
{
  GList *lst;
  guint i;

  g_return_val_if_fail(VISU_IS_PAIR(pair), (GList*)0);

  lst = (GList*)0;
  for (i = 0; i < pair->priv->links->len; i++)
    lst = g_list_append(lst, g_array_index(pair->priv->links, gpointer, i));
  return lst;
}
/**
 * visu_pair_getNthLink:
 * @pair: a #VisuPair object.
 * @pos: the position in the list of links.
 *
 * A link can also be retrieved by its position.
 *
 * Returns: (transfer none): the #VisuPairLink object associated to the given two
 *          elements and distances. If none exists NULL is returned.
 */
VisuPairLink* visu_pair_getNthLink(VisuPair *pair, guint pos)
{
  g_return_val_if_fail(VISU_IS_PAIR(pair), (VisuPairLink*)0);

  return (pos < pair->priv->links->len) ?
    g_array_index(pair->priv->links, VisuPairLink*, pos) : (VisuPairLink*)0;
}
/**
 * visu_pair_addLink:
 * @pair: a #VisuPair object.
 * @minMax: (array fixed-size=2): the two min and max distances.
 *
 * A link between two elements is characterized by its boundary distances.
 *
 * Returns: (transfer none): the #VisuPairLink object associated to the given two
 *          elements and distances. If none exists it is created. The
 *          returned value should not be freed.
 */
VisuPairLink* visu_pair_addLink(VisuPair *pair, const float minMax[2])
{
  guint i;
  VisuPairLink *data;
  const gfloat zeros[2] = {0.f, 0.f};

  g_return_val_if_fail(VISU_IS_PAIR(pair), (VisuPairLink*)0);

  DBG_fprintf(stderr, "Visu Pair: look for link %g - %g.\n", minMax[0], minMax[1]);
  for (i = 0; i < pair->priv->links->len; i++)
    if (visu_pair_link_match(g_array_index(pair->priv->links, VisuPairLink*, i), minMax))
      return g_array_index(pair->priv->links, VisuPairLink*, i);
  if (pair->priv->links->len == 1 &&
      visu_pair_link_match(g_array_index(pair->priv->links, VisuPairLink*, 0), zeros))
    {
      data = g_array_index(pair->priv->links, VisuPairLink*, 0);
      visu_pair_link_setDistance(data, minMax[0], VISU_DISTANCE_MIN);
      visu_pair_link_setDistance(data, minMax[1], VISU_DISTANCE_MAX);
      DBG_fprintf(stderr, "Visu Pair: updating 0 - 0.\n");
    }
  else
    {
      data = visu_pair_link_new(pair->priv->ele1, pair->priv->ele2, minMax);
      g_array_append_val(pair->priv->links, data);
      g_object_notify_by_pspec(G_OBJECT(pair), _properties[LINKS_PROP]);
      DBG_fprintf(stderr, "Visu Pair: create a new link.\n");
    }

  return data;
}
/**
 * visu_pair_removeLink:
 * @pair: a #VisuPair object.
 * @data: a link object.
 *
 * Delete the given link.
 *
 * Returns: TRUE if the link exists and has been successfully removed.
 */
gboolean visu_pair_removeLink(VisuPair *pair, VisuPairLink *data)
{
  guint i;
  gfloat zeros[2] = {0.f, 0.f};

  g_return_val_if_fail(VISU_IS_PAIR(pair), FALSE);

  for (i = 0; i < pair->priv->links->len; i++)
    if (g_array_index(pair->priv->links, VisuPairLink*, i) == data)
      {
        g_array_remove_index(pair->priv->links, i);
        g_object_unref(data);
        if (!pair->priv->links->len)
          visu_pair_addLink(pair, zeros);
        else
          g_object_notify_by_pspec(G_OBJECT(pair), _properties[LINKS_PROP]);
        return TRUE;
      }
  return FALSE;
}
/**
 * visu_pair_contains:
 * @pair: a #VisuPair object.
 * @link: a #VisuPairLink object.
 *
 * Tests if @link is contained in @pair.
 *
 * Since: 3.8
 *
 * Returns: TRUE if @link is a member of @pair.
 **/
gboolean visu_pair_contains(const VisuPair *pair, const VisuPairLink *link)
{
  guint i;

  g_return_val_if_fail(VISU_IS_PAIR(pair), FALSE);

  for (i = 0; i < pair->priv->links->len; i++)
    if (g_array_index(pair->priv->links, VisuPairLink*, i) == link)
      return TRUE;
  return FALSE;
}
/**
 * visu_pair_getBondDistance:
 * @pair: a #VisuPair object.
 * @dataObj: a #VisuData object.
 * @from: (out caller-allocates): a location for a float.
 * @to: (out caller-allocates): a location for a float.
 *
 * Compute the bond distribution for the given @pair and look for the
 * first peak and returns its span. When given, @from and @to contains
 * the lengths between which the bond for @pair is the most probable.
 *
 * Since: 3.8
 *
 * Returns: TRUE if a significant bond length can be found.
 **/
gboolean visu_pair_getBondDistance(VisuPair *pair, VisuData *dataObj, gfloat *from, gfloat *to)
{
  VisuPairDistribution *dd;
  guint sum, startStopId[2];

  g_return_val_if_fail(VISU_IS_PAIR(pair), FALSE);

  dd = visu_pair_getDistanceDistribution(pair, dataObj, -1.f, -1.f, -1.f);
  g_return_val_if_fail(dd, FALSE);

  startStopId[0] = 0;
  startStopId[1] = dd->nValues - 1;
  if (!visu_pair_distribution_getNextPick(dd, startStopId, &sum, (guint*)0, (guint*)0))
    return FALSE;

  if (from)
    *from = dd->initValue + startStopId[0] * dd->stepValue;
  if (to)
    *to   = dd->initValue + startStopId[1] * dd->stepValue;
  return TRUE;
}

/****************************/
/* Pair distribution stuff. */
/****************************/
#define BONDHISTOGRAM_ID   "bondDistribution_data"
#define BONDHISTOGRAM_STEP 0.1f
#define BONDHISTOGRAM_MIN  0.f
#define BONDHISTOGRAM_MAX  10.f

static void freeHistoData(gpointer data)
{
  DBG_fprintf(stderr, "Visu Pair: free '%s' data.\n", BONDHISTOGRAM_ID);
  g_free(((VisuPairDistribution*)data)->histo);
  g_free(data);
}
/**
 * visu_pair_getDistanceDistribution:
 * @pair: a #VisuPair ;
 * @dataObj: a #VisuData ;
 * @step: a float for the distance mesh (negative value to use
 * built-in default) ;
 * @min: a float for the minimum scanning value (negative value to use
 * built-in default).
 * @max: a float for the maximum scanning value (negative value to use
 * built-in default).
 * 
 * This will compute the distance distribution of nodes for the given
 * @pair.
 *
 * Returns: a structure defining the distance distribution. This
 * structure is private and should not be freed.
 */
VisuPairDistribution* visu_pair_getDistanceDistribution(VisuPair *pair,
						       VisuData *dataObj,
						       float step,
						       float min, float max)
{
  VisuPairDistribution *dd;
  guint i;
  VisuNodeArrayIter iter1, iter2;
  float d2, inv;
  float xyz1[3], xyz2[3];
#if DEBUG == 1
  guint nRef;
  GTimer *timer;
  gulong fractionTimer;
#endif

  g_return_val_if_fail(VISU_IS_PAIR(pair) && VISU_IS_DATA(dataObj), (VisuPairDistribution*)0);

#if DEBUG == 1
  timer = g_timer_new();
  g_timer_start(timer);
#endif

  /* We create the storage structure. */
  dd = (VisuPairDistribution*)g_object_get_data(G_OBJECT(pair), BONDHISTOGRAM_ID);
  if (dd)
    g_free(dd->histo);
  else
    {
      dd = g_malloc(sizeof(VisuPairDistribution));
      g_object_set_data_full(G_OBJECT(pair), BONDHISTOGRAM_ID,
                             (gpointer)dd, freeHistoData);
    }
  visu_pair_getElements(pair, &dd->ele1, &dd->ele2);
  dd->nNodesEle1 = 0;
  dd->nNodesEle2 = 0;
  dd->stepValue = (step > 0.f)?step:BONDHISTOGRAM_STEP;
  dd->initValue = (min > 0.f)?min:BONDHISTOGRAM_MIN;
  dd->nValues   = (int)((((max > 0.f)?max:BONDHISTOGRAM_MAX) -
			 dd->initValue) / dd->stepValue) + 1;
  dd->histo     = g_malloc0(sizeof(int) * dd->nValues);

  DBG_fprintf(stderr, "Visu Pair: compute distance distribution (%p %g %d).\n",
	      (gpointer)dd, dd->stepValue, dd->nValues);

  /* We compute the distribution. */
  visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter1);
  inv = 1.f / dd->stepValue;
  visu_pair_getElements(pair, &iter1.element, NULL);
  for(visu_node_array_iterRestartNode(VISU_NODE_ARRAY(dataObj), &iter1); iter1.node;
      visu_node_array_iterNextNodeOriginal(VISU_NODE_ARRAY(dataObj), &iter1))
    {
      if (!iter1.node->rendered)
	continue;
      dd->nNodesEle1 += 1;
      visu_data_getNodePosition(dataObj, iter1.node, xyz1);

      visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter2);
      visu_pair_getElements(pair, NULL, &iter2.element);
/*       fprintf(stderr, "## %d\n", dd->histo[177]); */
      for(visu_node_array_iterRestartNode(VISU_NODE_ARRAY(dataObj), &iter2); iter2.node;
	  visu_node_array_iterNextNode(VISU_NODE_ARRAY(dataObj), &iter2))
	{
	  if (!iter2.node->rendered)
	    continue;
	  /* Don't count the inter element pairs two times. */
	  if (iter1.element == iter2.element &&
	      iter2.node == iter1.node)
	    continue;

	  visu_data_getNodePosition(dataObj, iter2.node, 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]);
	  /* We put the distance into the histogram. */
	  dd->histo[MIN((guint)((sqrt(d2) - dd->initValue) * inv), dd->nValues - 1)] += 1;
/* 	  fprintf(stderr, "%d-%d %d\n", iter1.node->number, iter2.node->number, dd->histo[177]); */
	}
/*       fprintf(stderr, "-> %d\n", dd->histo[177]); */
    }
  for(visu_node_array_iterRestartNode(VISU_NODE_ARRAY(dataObj), &iter2); iter2.node;
      visu_node_array_iterNextNode(VISU_NODE_ARRAY(dataObj), &iter2))
    if (iter2.node->rendered)
      dd->nNodesEle2 += 1;
  if (iter1.element == iter2.element)
    for (i = 0; i < dd->nValues; i++)
      dd->histo[i] /= 2;

#if DEBUG == 1
  g_timer_stop(timer);

  for (nRef = 0; nRef < dd->nValues; nRef++)
    fprintf(stderr, " | %03d -> %6.3f, %5d\n", nRef,
            dd->initValue + dd->stepValue * nRef, dd->histo[nRef]);
  fprintf(stderr, "Visu Pair: distances analysed in %g micro-s.\n",
	  g_timer_elapsed(timer, &fractionTimer)/1e-6);

  g_timer_destroy(timer);
#endif

  return dd;
}

/**
 * visu_pair_distribution_getNextPick:
 * @dd: a #VisuPairDistribution object.
 * @startStopId: two ids.
 * @integral: a location for a guint value, can be NULL.
 * @max: a location to store the value ;
 * @posMax: a location to store the position of the pick.
 *
 * Try to find the next pick in the distribution. A pick is a group of
 * consecutive non-null values, with a significant integral. On enter,
 * @startStopId contains the span to look into for the pick, and on
 * output, it contains the span of the pick itself.
 * 
 * Since: 3.6
 * 
 * Returns: TRUE if a pick is found.
 */
gboolean visu_pair_distribution_getNextPick(VisuPairDistribution *dd,
                                            guint startStopId[2], guint *integral,
                                            guint *max, guint *posMax)
{
  float min, start, stop;
  guint i, iStart, iStop, sum, _posMax, _max;

  g_return_val_if_fail(dd, FALSE);
  g_return_val_if_fail(startStopId[1] < dd->nValues, FALSE);

  iStart = startStopId[0];
  iStop  = startStopId[1];
  _max = 0;
  _posMax = 0;
  min = 1.5f * MIN(dd->nNodesEle1, dd->nNodesEle2);
  DBG_fprintf(stderr, "Visu Pair: look for one pick in %d-%d.\n",
              startStopId[0], startStopId[1]);
  do
    {
      min  *= 0.5f;
      start = -1.f;
      stop  = -1.f;
      sum   = 0;
      for (i = startStopId[0] ; i < startStopId[1]; i++)
        {
          if (start < 0.f && dd->histo[i] > 0)
            {
              start = dd->stepValue * i + dd->initValue;
              sum = dd->histo[i];
              iStart = i;
              _max = dd->histo[i];
              _posMax = i;
            }
          else if (start > 0.f)
            {
              if (dd->histo[i] == 0)
                {
                  if (sum >= min)
                    {
                      stop = dd->stepValue * i + dd->initValue;
                      iStop = i;
                      break;
                    }
                  else
                    start = -1.f;
                }
              else
                {
                  sum += dd->histo[i];
                  if (dd->histo[i] > _max)
                    {
                      _max = dd->histo[i];
                      _posMax = i;
                    }
                }
            }
        }
      DBG_fprintf(stderr, "Visu Pair: found one pick at %d-%d (%d).\n",
                  iStart, iStop, sum);
    }
  while (start < 0.f && min > 0.1f * MIN(dd->nNodesEle1, dd->nNodesEle2));
  DBG_fprintf(stderr, "Visu Pair: set start and stop at %f, %f (%f %d).\n",
              start, stop, min, sum);
  if (start <= 0.f || stop <= 0.f)
    return FALSE;

  startStopId[0] = iStart;
  startStopId[1] = iStop;
  if (integral)
    *integral = sum;
  if (max)
    *max = _max;
  if (posMax)
    *posMax = _posMax;

  return TRUE;
}
