/*   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 <gtk/gtk.h>
#include <math.h>

#include <abinit.h>

#include <support.h>
#include <visu_gtk.h>
#include <gtk_main.h>
#include <gtk_interactive.h>
#include <gtk_renderingWindowWidget.h>
#include <extensions/marks.h>
#include <openGLFunctions/interactive.h>

#include "abinit.h"

#define ABINIT_SYMMETRIES_INFO \
  _("left-button\t\t: select one atom to get the equivalent ones\n"	\
    "right-button\t\t: switch to observe")

enum
  {
    SYM_ID,
    SYM_MATRIX_00,
    SYM_MATRIX_01,
    SYM_MATRIX_02,
    SYM_MATRIX_10,
    SYM_MATRIX_11,
    SYM_MATRIX_12,
    SYM_MATRIX_20,
    SYM_MATRIX_21,
    SYM_MATRIX_22,
    SYM_TRANS_0,
    SYM_TRANS_1,
    SYM_TRANS_2,
    SYM_COMMENT,
    SYM_N_COLS
  };

static gulong onSpin_id;
static AbSymmetry *sym;
static GtkWidget *lblSymName, *lblSymId, *lblSymWarning, *spinTol;
static GtkWidget *spinNode, *vbox, *vboxSym;
static guint timeout = 0;
static VisuInteractive *inter;
static GtkListStore *symList;

static void onSelection(VisuInteractive *inter, VisuInteractivePick pick,
                        VisuNodeArray *array, VisuNode *node0,
                        VisuNode *node1, VisuNode *node2, gpointer data);
static void onSymmetryToggled(GtkToggleButton *toggle, gpointer data);
static void onSymmetryClicked(GtkButton *button, gpointer data);
static void onTolChanged(GtkSpinButton *spin, gpointer data);
static void onVisuDataChanged(VisuUiMain *visu, VisuData *dataObj, gpointer data);
static void onSpinNode(GtkSpinButton *button, gpointer data);

static gchar* symAnalyse(AbSymmetry *sym, int iSym);
static void getEquivalents(VisuData *dataObj, VisuNode *node);
static void updateSymmetries(VisuData *dataObj, gdouble tol);
static void formatSymOperators(GtkTreeViewColumn *column, GtkCellRenderer *cell,
                               GtkTreeModel *model, GtkTreeIter *iter, gpointer data);

GtkWidget* buildTab(VisuUiMain *main, gchar **label,
		    gchar **help, GtkWidget **radio)
{
  GtkWidget *wd, *hbox, *bt, *scroll;
  VisuData *data;
  VisuUiRenderingWindow *window;
  VisuGlNodeScene *scene;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;

  window = visu_ui_main_class_getDefaultRendering();
  scene = visu_ui_rendering_window_getGlScene(window);
  data = visu_gl_node_scene_getData(scene);
  g_return_val_if_fail(data, (GtkWidget*)0);

  inter = visu_interactive_new(interactive_pick);
  g_signal_connect_swapped(G_OBJECT(inter), "stop",
                           G_CALLBACK(visu_ui_interactive_toggle), (gpointer)0);
  g_signal_connect(G_OBJECT(inter), "node-selection",
		   G_CALLBACK(onSelection), (gpointer)0);

  *label = _("Symmetries");
  *help  = g_strdup(ABINIT_SYMMETRIES_INFO);

  symList = gtk_list_store_new(SYM_N_COLS, G_TYPE_INT,
                               G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
                               G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
                               G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
                               G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_FLOAT,
                               G_TYPE_STRING);

  vbox = gtk_vbox_new(FALSE, 0);
  g_signal_connect_swapped(vbox, "destroy", G_CALLBACK(g_object_unref), inter);

  sym = (AbSymmetry*)0;

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);

  *radio = gtk_radio_button_new_with_mnemonic
    (NULL, _("Analyse the symmetries"));
  gtk_box_pack_start(GTK_BOX(hbox), *radio, FALSE, FALSE, 0);
  gtk_widget_set_name(*radio, "message_radio");
  g_signal_connect(G_OBJECT(*radio), "toggled",
		   G_CALLBACK(onSymmetryToggled), (gpointer)0);
  wd = gtk_button_new_with_mnemonic(_("Compute symmetries"));
  bt = wd;
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  wd = gtk_label_new(") ");
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  wd = gtk_spin_button_new_with_range(-10, -2, 1);
  gtk_entry_set_width_chars(GTK_ENTRY(wd), 2);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(wd), -6);
  g_signal_connect(G_OBJECT(wd), "value-changed",
		   G_CALLBACK(onTolChanged), (gpointer)0);
  g_signal_connect(G_OBJECT(bt), "clicked",
		   G_CALLBACK(onSymmetryClicked), (gpointer)wd);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  spinTol = wd;
  wd = gtk_label_new("(tolsym = 10^");
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  vboxSym = gtk_vbox_new(FALSE, 0);
  gtk_widget_set_sensitive(vboxSym, FALSE);
  gtk_box_pack_start(GTK_BOX(vbox), vboxSym, TRUE, TRUE, 0);

  /* A message for ABINIT. */
  wd = gtk_label_new(_("<span size=\"smaller\">The symmetry routines"
		       " are provided by ABINIT ("
		       "<span font_desc=\"courier\" color=\"blue\">"
		       "http://www.abinit.org</span>).</span>"));
  gtk_widget_set_halign(wd, 1.0);
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_box_pack_end(GTK_BOX(vbox), wd, FALSE, FALSE, 5);

  /* The labels showing the symmetry. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vboxSym), hbox, FALSE, FALSE, 10);
  wd = gtk_label_new(_("<b>Space group:</b>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_widget_set_margin_start(wd, 10);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  wd = gtk_label_new(_("<span font_desc=\"courier\" color=\"blue\">"
		       "http://en.wikipedia.org/wiki/Space_group</span>"));
  gtk_label_set_selectable(GTK_LABEL(wd), TRUE);
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 10);
  wd = gtk_image_new_from_icon_name("help-browser", GTK_ICON_SIZE_MENU);
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vboxSym), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("Crystal system:"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  lblSymName = gtk_label_new("");
  gtk_widget_set_halign(lblSymName, 0.);
  gtk_box_pack_start(GTK_BOX(hbox), lblSymName, TRUE, TRUE, 5);
  wd = gtk_label_new(_("space group:"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  lblSymId = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(lblSymId), TRUE);
  gtk_widget_set_halign(lblSymId, 0.);
  gtk_box_pack_start(GTK_BOX(hbox), lblSymId, TRUE, TRUE, 5);
  lblSymWarning = gtk_label_new(_("<span color=\"red\">Warning:</span> the Bravais lattice determined from the primitive vectors is more symmetric than the real one obtained from coordinates (printed)."));
  gtk_label_set_use_markup(GTK_LABEL(lblSymWarning), TRUE);
  gtk_label_set_line_wrap(GTK_LABEL(lblSymWarning), TRUE);
  gtk_label_set_line_wrap_mode(GTK_LABEL(lblSymWarning), PANGO_WRAP_WORD);
  gtk_box_pack_start(GTK_BOX(vboxSym), lblSymWarning, FALSE, FALSE, 0);
  wd = gtk_label_new(_("List of symmetry operations:"));
  gtk_widget_set_halign(wd, 0.);
  gtk_box_pack_start(GTK_BOX(vboxSym), wd, FALSE, FALSE, 3);
  scroll = gtk_scrolled_window_new(NULL, NULL);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
				 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  gtk_box_pack_start(GTK_BOX(vboxSym), scroll, TRUE, TRUE, 0);
  wd = gtk_tree_view_new_with_model(GTK_TREE_MODEL(symList));
  gtk_container_add(GTK_CONTAINER(scroll), wd);
  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes(_("Id"), renderer,
						    "text", SYM_ID, NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(column, _("operation"));
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func
    (column, renderer, formatSymOperators,
     GINT_TO_POINTER(SYM_MATRIX_00), (GDestroyNotify)0);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(column, _("translation"));
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func
    (column, renderer, formatSymOperators,
     GINT_TO_POINTER(SYM_TRANS_0), (GDestroyNotify)0);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes(_("comment"), renderer,
						    "text", SYM_COMMENT, NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);

  /* The interface to choose one atom to select. */
  wd = gtk_label_new(_("<b>Equivalent atoms:</b>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_widget_set_halign(wd, 0.);
  gtk_widget_set_margin_start(wd, 10);
  gtk_box_pack_start(GTK_BOX(vboxSym), wd, FALSE, FALSE, 10);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vboxSym), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("Visualise the equivalent nodes of node:"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  spinNode = gtk_spin_button_new_with_range(0, 1, 1);
  onSpin_id = g_signal_connect(G_OBJECT(spinNode), "value-changed",
			       G_CALLBACK(onSpinNode), (gpointer)0);
  gtk_box_pack_start(GTK_BOX(hbox), spinNode, FALSE, FALSE, 5);
  wd = gtk_label_new(_(" or pick directly."));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  gtk_widget_show_all(vbox);
  gtk_widget_hide(lblSymWarning);
    
  g_signal_connect(main, "DataFocused", G_CALLBACK(onVisuDataChanged), (gpointer)0);
  onVisuDataChanged((VisuUiMain*)0, data, (gpointer)0);

  return vbox;
}
static void formatSymOperators(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                               GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
  gchar *str;
  gfloat valf[3];
  gint vali[9];
  
  str = (gchar*)0;
  if (GPOINTER_TO_INT(data) == SYM_MATRIX_00)
    {
      gtk_tree_model_get(model, iter, SYM_MATRIX_00, vali,
                         SYM_MATRIX_01, vali + 1,
                         SYM_MATRIX_02, vali + 2,
                         SYM_MATRIX_10, vali + 3,
                         SYM_MATRIX_11, vali + 4,
                         SYM_MATRIX_12, vali + 5,
                         SYM_MATRIX_20, vali + 6,
                         SYM_MATRIX_21, vali + 7,
                         SYM_MATRIX_22, vali + 8,
                         -1);
      str = g_strdup_printf("[ %2d %2d %2d\n"
                            "  %2d %2d %2d\n"
                            "  %2d %2d %2d ]", vali[0], vali[1], vali[2],
                            vali[3], vali[4], vali[5], vali[6], vali[7], vali[8]);
    }
  else if (GPOINTER_TO_INT(data) == SYM_TRANS_0)
    {
      gtk_tree_model_get(model, iter, SYM_TRANS_0, valf,
                         SYM_TRANS_1, valf + 1,
                         SYM_TRANS_2, valf + 2,
                         -1);
      str = g_strdup_printf("[ %2f\n"
                            "  %2f\n"
                            "  %2f ]", valf[0], valf[1], valf[2]);
    }
  if (str)
    {
      g_object_set(G_OBJECT(cell), "text", str, NULL);
      g_free(str);
    }
}

void startSelect(VisuUiRenderingWindow *window)
{
  visu_ui_rendering_window_pushInteractive(window, inter);
}
void stopSelect(VisuUiRenderingWindow *window)
{
  visu_ui_rendering_window_popInteractive(window, inter);
}

static void onSelection(VisuInteractive *inter _U_, VisuInteractivePick pick _U_,
                        VisuNodeArray *array, VisuNode *node0,
                        VisuNode *node1 _U_, VisuNode *node2 _U_, gpointer data _U_)
{
  getEquivalents(VISU_DATA(array), node0);
  g_signal_handler_block(G_OBJECT(spinNode), onSpin_id);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinNode), node0->number + 1);
  g_signal_handler_unblock(G_OBJECT(spinNode), onSpin_id);
}

static void onSymmetryToggled(GtkToggleButton *toggle _U_, gpointer data _U_)
{
}
static void onSymmetryClicked(GtkButton *button _U_, gpointer spin)
{
  VisuData *dataObj;

  /* Get the current VisuData object. */
  dataObj = visu_gl_node_scene_getData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));

  updateSymmetries(dataObj, gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)));
}
static void onTolChanged(GtkSpinButton *spin, gpointer data _U_)
{
  VisuData *dataObj;

  /* Get the current VisuData object. */
  dataObj = visu_gl_node_scene_getData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));

  updateSymmetries(dataObj, gtk_spin_button_get_value(spin));
}
static void onVisuDataChanged(VisuUiMain *visu _U_, VisuData *dataObj,
			      gpointer data _U_)
{
  VisuNodeArrayIter iter;

  gtk_widget_set_sensitive(vbox, (dataObj !=(VisuData*)0));

  updateSymmetries((VisuData*)0, 0);
  
  if (dataObj)
    {
      visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
      gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinNode), 0, iter.idMax + 1);
    }
}
static void onSpinNode(GtkSpinButton *button, gpointer data _U_)
{
  VisuData *dataObj;
  VisuNode *node;

  if (gtk_spin_button_get_value(button) == 0)
    return;

  /* Get the current VisuData object. */
  dataObj = visu_gl_node_scene_getData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));
  node = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj),
                                   (int)gtk_spin_button_get_value(button) - 1);

  getEquivalents(dataObj, node);
}
static gboolean onRemoveEquivalents(gpointer data _U_)
{
  timeout = 0;
  return FALSE;
}

static void removeEquivalents(gpointer data)
{
  VisuGlExtMarks *marks;
  GArray *ids;

  ids = (GArray*)data;

  /* Get the current VisuData object. */
  marks = visu_gl_node_scene_getMarks(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));

  visu_gl_ext_marks_setHighlight(marks, ids, MARKS_STATUS_TOGGLE);
  g_array_unref(ids);
}
static void getEquivalents(VisuData *dataObj, VisuNode *node)
{
  guint j;
  int i, nSym;
  int *nodes;
  GArray *ids;
  gboolean found;
#if GLIB_MINOR_VERSION > 13
#define fact 1
#define G_TIMEOUT_ADD_FULL g_timeout_add_seconds_full
#else
#define fact 1000
#define G_TIMEOUT_ADD_FULL g_timeout_add_full
#endif

  if (!sym)
    updateSymmetries(dataObj, gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinTol)));
  g_return_if_fail(sym);

  if (ab_symmetry_get_equivalent_atom(sym, &nodes, &nSym, node->number + 1) ==
      AB_NO_ERROR)
    {
      ids = g_array_new(FALSE, FALSE, sizeof(guint));
      for (i = 0; i < nSym; i++)
	{
	  found = FALSE;
	  for ( j = 0; j < ids->len && !found; j++)
	    found = (nodes[i * 4 + 3] == g_array_index(ids, int, j) + 1);
	  if (!found)
            {
              j = nodes[i * 4 + 3] - 1;
              g_array_append_val(ids, j);
            }
	}
      g_free(nodes);

      /* We remove possible earlier timeout. */
      if (timeout > 0)
	g_source_remove(timeout);

      /* Set the new highlights. */
      visu_gl_ext_marks_setHighlight
	(visu_gl_node_scene_getMarks(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering())),
	 ids, MARKS_STATUS_SET);

      /* Add the new timeout. */
      timeout = G_TIMEOUT_ADD_FULL(G_PRIORITY_DEFAULT, 3 * fact,
				   onRemoveEquivalents, ids, removeEquivalents);
      /* ids will be freed in the timeout. */
    }
}

gpointer startThreadSymmetry(gpointer data _U_)
{
  float xred0[3], xyz[3];
  char *spGrp;
  double box[3][3], genAfm[3], *xred;
  int i, *typat, grpId, grpMagnId;
  AbError errno;
  VisuNodeArrayIter iter;

  AbinitData *dt;

  DBG_fprintf(stderr, "AB symmetry(%p): starting symmetry detection.\n",
              (gpointer)g_thread_self());
  dt = abinit_getDt();

  visu_box_getCellMatrix(visu_boxed_getBox(VISU_BOXED(dt->data)), box);
  ab_symmetry_set_lattice(dt->sym, box);
      
  visu_node_array_iter_new(VISU_NODE_ARRAY(dt->data), &iter);
  i = 0;
  typat = g_malloc(sizeof(int) * iter.nAllStoredNodes);
  xred = g_malloc(sizeof(double) * 3 * iter.nAllStoredNodes);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(dt->data), &iter); iter.node;
       visu_node_array_iterNext(VISU_NODE_ARRAY(dt->data), &iter))
    {
      typat[i] = iter.iElement;
      visu_data_getNodePosition(dt->data, iter.node, xyz);
      visu_box_convertXYZtoBoxCoordinates(visu_boxed_getBox(VISU_BOXED(dt->data)), xred0, xyz);
      xred[3 * i + 0] = (double)xred0[0];
      xred[3 * i + 1] = (double)xred0[1];
      xred[3 * i + 2] = (double)xred0[2];
      i += 1;
    }
  ab_symmetry_set_structure(dt->sym, iter.nAllStoredNodes, typat, xred);
  g_free(typat);
  g_free(xred);

  /* Ask for the calculation of the symmetries. */
  DBG_fprintf(stderr, "AB symmetry(%p): Ready to get symmetries from ABINIT.\n",
              (gpointer)g_thread_self());
  errno = ab_symmetry_get_group(dt->sym, &spGrp, &grpId,
				 &grpMagnId, genAfm);
  DBG_fprintf(stderr, "AB symmetry(%p): return from ABINIT (%d).\n",
              (gpointer)g_thread_self(), errno);
  if (errno == AB_NO_ERROR || errno == AB_ERROR_SYM_BRAVAIS_XRED)
    g_free(spGrp);
  else if (errno != AB_ERROR_SYM_NOT_PRIMITIVE)
    dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
                            "An error occured in ABINIT plug-in.");
  abinit_mutexUnlock();

  return (gpointer)0;
}

static void updateSymmetries(VisuData *dataObj, gdouble tol)
{
  double genAfm[3];
  int grpId, grpMagnId, centerId, nbrv;
  char *spGrp;
  gchar *str;
  AbSymmetryMat *brvSyms;
  int brvMat[3][3];
  gchar *bravais[7] = {"triclinic", "monoclinic", "orthorhombic",
		       "tetragonal", "trigonal", "hexagonal", "cubic"};
  gchar *center[7] = {"F", "F", "I", "P", "A", "B", "C"};
  AbError errno;
#ifdef G_THREADS_ENABLED
  GThread *ld_thread;
#endif
  int nSym, *amf, iSym;
  AbSymmetryMat *symOps;
  AbSymmetryTrans *trans;
  GtkTreeIter iter;

  AbinitData *dt;

  DBG_fprintf(stderr, "Abinit: upadte symmetries from %p.\n", (gpointer)dataObj);

  if (sym)
    ab_symmetry_free(sym);
  sym = (AbSymmetry*)0;
  gtk_list_store_clear(symList);

  if (dataObj)
    {
      dt = abinit_getDirectDt();
      dt->data = dataObj;
      dt->sym = ab_symmetry_new();
      dt->getMessages = FALSE;
      dt->useSignals  = FALSE;
      DBG_fprintf(stderr, "AB symmetry: set tolerance to 10^%g.\n", tol);
      ab_symmetry_set_tolerance(dt->sym, pow(10., tol));
#ifdef G_THREADS_ENABLED
      abinit_mutexInit();
      dt->error = (GError*)0;
#if GLIB_MINOR_VERSION < 32
      ld_thread = g_thread_create(startThreadSymmetry, (gpointer)0,
				  TRUE, &(dt->error));
#else
      ld_thread = g_thread_new(NULL, startThreadSymmetry, (gpointer)0);
#endif
      DBG_fprintf(stderr, "AB symmetry: run ABINIT symmetry into a thread (%p).\n",
                  (gpointer)ld_thread);
      if (ld_thread)
	g_thread_join(ld_thread);
      else
	g_warning("Can't run thread for ABINIT symmetry.");
      abinit_mutexRelease();
#else
      startThreadSymmetry((gpointer)(&dt));
#endif
      DBG_fprintf(stderr, "AB symmetry: return after thread exec (%p).\n",
                  (gpointer)dt->error);
      if (!dt->error)
	{
	  sym = dt->sym;

	  /* We get then the space group. */
          DBG_fprintf(stderr, "AB symmetry: get group.\n");
	  errno = ab_symmetry_get_group(sym, &spGrp, &grpId,
					 &grpMagnId, genAfm);
	  if (errno == AB_NO_ERROR || errno == AB_ERROR_SYM_BRAVAIS_XRED)
	    {
	      str = g_strdup_printf("%s (#%d)", spGrp, grpId);
	      gtk_label_set_text(GTK_LABEL(lblSymId), str);
	      g_free(str);
	      g_free(spGrp);

              DBG_fprintf(stderr, "AB symmetry: get matrices.\n");
              if (ab_symmetry_get_matrices(sym, &nSym, &symOps, &trans, &amf) ==
                  AB_NO_ERROR)
                {
                  for (iSym = 0; iSym < nSym; iSym++)
                    {
                      str = symAnalyse(dt->sym, iSym + 1);
                      gtk_list_store_append(symList, &iter);
                      gtk_list_store_set(symList, &iter, SYM_ID, iSym + 1,
                                         SYM_MATRIX_00, symOps[iSym].mat[0][0],
                                         SYM_MATRIX_01, symOps[iSym].mat[0][1],
                                         SYM_MATRIX_02, symOps[iSym].mat[0][2],
                                         SYM_MATRIX_10, symOps[iSym].mat[1][0],
                                         SYM_MATRIX_11, symOps[iSym].mat[1][1],
                                         SYM_MATRIX_12, symOps[iSym].mat[1][2],
                                         SYM_MATRIX_20, symOps[iSym].mat[2][0],
                                         SYM_MATRIX_21, symOps[iSym].mat[2][1],
                                         SYM_MATRIX_22, symOps[iSym].mat[2][2],
                                         SYM_TRANS_0, trans[iSym].vect[0],
                                         SYM_TRANS_1, trans[iSym].vect[1],
                                         SYM_TRANS_2, trans[iSym].vect[2],
                                         SYM_COMMENT, str,
                                         -1);
                      g_free(str);
                    }
                  g_free(symOps);
                  g_free(trans);
                  g_free(amf);
                }
	    }
	  else
	    {
	      gtk_label_set_markup(GTK_LABEL(lblSymId), _("<i>not primitive</i>"));
	    }
          DBG_fprintf(stderr, "AB symmetry: get bravais.\n");
	  if (ab_symmetry_get_bravais(sym, brvMat, &grpId,
				       &centerId, &nbrv, &brvSyms) == AB_NO_ERROR)
	    {
	      g_free(brvSyms);
	      str = g_strdup_printf("%s (%s)", _(bravais[grpId - 1]),
				    center[centerId+3]);
	      gtk_label_set_text(GTK_LABEL(lblSymName), str);
	      g_free(str);
	    }
	  else
	    gtk_label_set_text(GTK_LABEL(lblSymName), "!");

	  /* If the bravais lattice doesn't match with the xred bravais
	     lattice, we print a message. */
	  if (errno == AB_ERROR_SYM_BRAVAIS_XRED)
	    gtk_widget_show(lblSymWarning);
	  else
	    gtk_widget_hide(lblSymWarning);

	  gtk_widget_set_sensitive(vboxSym, (dataObj != (VisuData*)0));
	}
      else
	{
	  visu_ui_raiseWarning(_("ABINIT symmetry calculation"),
			       dt->error->message, (GtkWindow*)0);
	  g_error_free(dt->error);
	  ab_symmetry_free(dt->sym);
	  sym = (AbSymmetry*)0;

	  gtk_widget_set_sensitive(vboxSym, FALSE);
	}
    }
  else
    {
      gtk_label_set_text(GTK_LABEL(lblSymName), "");
      gtk_label_set_text(GTK_LABEL(lblSymId), "");
    }
}

static gchar* symAnalyse(AbSymmetry *sym, int iSym)
{
  AbError error;
  int type;
  gchar *label;


  error = ab_symmetry_get_type(sym, &type, &label, iSym);
  if (error == AB_NO_ERROR)
    return label;
  else
    return g_strdup(_("Unknown symmetry"));
}
