/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien CALISTE, laboratoire L_Sim, (2001-2011)
  
	Adresse 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: Damien CALISTE, L_Sim laboratory, (2001-2011)

	E-mail address:
	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_basic.h>
#include <visu_rendering.h>
#include <visu_data.h>
#include <renderingMethods/renderingAtomic_ascii.h>
#include <coreTools/toolMatrix.h>
#include <coreTools/toolPhysic.h>
#include <extraFunctions/scalarFields.h>
#include <extraGtkFunctions/gtk_toolPanelWidget.h>
#include <extraGtkFunctions/gtk_numericalEntryWidget.h>
#include <extraGtkFunctions/gtk_elementComboBox.h>
#include <panelModules/panelSurfaces.h>
#include <gtk_main.h>
#include <visu_gtk.h>

#include <bigdft.h>
#include <string.h>
#include <glib/gstdio.h>

#include <support.h>

#include "bigdftplug.h"

static GtkWidget *btRun = NULL, *btSCF = NULL;
static GtkWidget *btVext, *btDens, *cbOrbsu, *cbOrbsd;
#if GTK_MINOR_VERSION > 19  || GTK_MAJOR_VERSION > 2
static GtkWidget *spRun;
#endif
static gulong sigDens, sigVext;
static GtkWidget *cbKpt, *cbWf;

enum
  {
    LABEL,
    ACTIVATED,
    VALUE,
    N_COLUMNS
  };
static GtkListStore *orbsd_kpt, *orbsd_spu, *orbsd_spd;
typedef struct _StoredWfData
{
  gulong signal;
  const VisuSurface *surf;
} StoredWfData;
enum
  {
    SCF_ITER,
    SCF_TYPE,
    SCF_LABEL,
    SCF_IKPT,
    SCF_IORB,
    SCF_ISPIN,
    SCF_NSPINOR,
    SCF_EVAL,
    SCF_OCCUP,
    SCF_TOL,
    SCF_MEMORY,
    SCF_DATA,
    SCF_OBJECT,
    SCF_GLR,
    SCF_N_COLUMNS
  };
enum
  {
    SCF_TYPE_WF,
    SCF_TYPE_V_EXT,
    SCF_TYPE_DENSITY,
    SCF_TYPE_ITER_HAM,
    SCF_TYPE_ITER_SUB,
    SCF_TYPE_ITER_WFN
  };
static GtkTreeStore *scfdata;

struct _socketStuff
{
  BigDFT_SignalsClient *client;
  GCancellable *cancellable;

  GAsyncQueue *message;

  BigDFT_Wf *wf;
  BigDFT_LocalFields *denspot;
  BigDFT_Energs *energs;
  BigDFT_OptLoop *optloop;
};

/* Local methods. */
static void update_orbsd(const VisuData *dataObj);
static void treeGetSCFIter(GtkTreeIter *parent, guint iscf, BigDFT_OptLoop *loop);

/* Callbacks. */
static void onRunBigDFT(GtkToggleButton *bt, gpointer data);
static void onCheckDens(GtkToggleButton *bt, gpointer data);
static void onCheckVext(GtkToggleButton *bt, gpointer data);
static void onOrbsdToggled(GtkComboBox *cb, gpointer user_data);
static void ptInfos(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
                    GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data);
static void onGetSCF(GtkButton *bt, gpointer data);
static gboolean onTreeViewClicked(GtkWidget *widget, GdkEventButton *event,
				  gpointer user_data);
static void onVextReady(BigDFT_LocalFields *denspot, gpointer data);
static void onDensityReady(BigDFT_LocalFields *denspot, guint iter, gpointer data);
static void onIter(BigDFT_OptLoop *optloop, BigDFT_Energs *energs, gpointer data);
static void onDone(BigDFT_OptLoop *optloop, BigDFT_Energs *energs, gpointer data);
static void onPsiReady(BigDFT_Wf *wf, guint iter, gpointer data);
static void onOneWaveReady(BigDFT_Wf *wf, guint iter, GArray *psic, BigDFT_PsiId ipsi,
                           guint ikpt, guint iorb, guint ispin, gpointer data);
static void onSocketClose(gpointer data);
static void onDataReady(GObject *obj, VisuData *dataObj, gpointer data);

void bdft_run_init()
{
  cbKpt = (GtkWidget*)0;
  cbWf = (GtkWidget*)0;

  orbsd_kpt = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT);
  orbsd_spu = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT);
  orbsd_spd = gtk_list_store_new(N_COLUMNS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_UINT);
  scfdata = gtk_tree_store_new(SCF_N_COLUMNS, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_STRING,
                               G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT,
                               G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_DOUBLE, G_TYPE_UINT,
                               G_TYPE_ARRAY, G_TYPE_OBJECT, G_TYPE_OBJECT);

  sigDens = 0;
  sigVext = 0;

  g_signal_connect(VISU_OBJECT_INSTANCE, "dataRendered",
                   G_CALLBACK(onDataReady), NULL);
}

static void onDataReady(GObject *obj _U_, VisuData *dataObj, gpointer data _U_)
{
  double *radii;
  guint *radiiStatus;

  /* We update the combos. */
  DBG_fprintf(stderr, "BigDFT Run: caught 'dataRendered' signal.\n");
  update_orbsd(dataObj);
  gtk_tree_store_clear(scfdata);

  if (!btRun || !btSCF)
    return;

  gtk_widget_set_sensitive(btRun, FALSE);
  gtk_widget_set_sensitive(btSCF, FALSE);

  if (dataObj)
    {
      radii = (double*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radii");
      radiiStatus = (guint*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radiiStatus");
    }
  else
    {
      radii = (double*)0;
      radiiStatus = (guint*)0;
    }

  if (radii && radiiStatus)
    gtk_widget_set_sensitive(btRun, TRUE);

  DBG_fprintf(stderr, "BigDFT run: 'DataReady' signal OK.\n");
}

GtkWidget* bdft_run_tab(guint nEle, BigDFT_Wf *wf)
{
  GtkWidget *hbox, *wd, *ct, *vbox2;
  GtkCellRenderer *render;
  GtkTreeViewColumn *col;

  /* Running BigDFT. */
  vbox2 = gtk_vbox_new(FALSE, 2);

  /* The run line. */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  wd = gtk_combo_box_text_new_with_entry();
  gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(wd), "local", _("Run locally"));
  gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(wd), "localhost", g_get_host_name());
  gtk_combo_box_set_active(GTK_COMBO_BOX(wd), 0);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  btRun = gtk_toggle_button_new_with_label(_("Go go go..."));
  gtk_widget_set_sensitive(btRun, (nEle > 0));
  g_signal_connect(G_OBJECT(btRun), "toggled",
		   G_CALLBACK(onRunBigDFT), (gpointer)wd);
  gtk_box_pack_end(GTK_BOX(hbox), btRun, FALSE, FALSE, 0);
#if GTK_MINOR_VERSION > 19  || GTK_MAJOR_VERSION > 2
  spRun = gtk_spinner_new();
  gtk_widget_set_no_show_all(GTK_WIDGET(spRun), TRUE);
  gtk_box_pack_end(GTK_BOX(hbox), spRun, FALSE, FALSE, 0);
#endif

  /* The denspot line. */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("Rho / V:"));
  gtk_misc_set_alignment(GTK_MISC(wd), 0., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  btDens = gtk_check_button_new_with_mnemonic(_("_Density"));
  g_signal_connect(G_OBJECT(btDens), "toggled",
                   G_CALLBACK(onCheckDens), (gpointer)0);
  gtk_box_pack_start(GTK_BOX(hbox), btDens, FALSE, FALSE, 0);
  btVext = gtk_check_button_new_with_mnemonic(_("V_ext"));
  g_signal_connect(G_OBJECT(btVext), "toggled",
                   G_CALLBACK(onCheckVext), (gpointer)0);
  /* gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btVext), TRUE); */
  gtk_box_pack_start(GTK_BOX(hbox), btVext, FALSE, FALSE, 0);
  wd = gtk_check_button_new_with_mnemonic(_("V_hartr"));
  gtk_widget_set_sensitive(wd, FALSE);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  wd = gtk_check_button_new_with_mnemonic(_("V_xc"));
  gtk_widget_set_sensitive(wd, FALSE);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  /* The wf lines. */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("Wavefunctions:"));
  gtk_misc_set_alignment(GTK_MISC(wd), 0., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  wd = gtk_label_new(_("up"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  cbOrbsu = gtk_combo_box_new_with_model(GTK_TREE_MODEL(orbsd_spu));
  render = gtk_cell_renderer_toggle_new();
  g_signal_connect(G_OBJECT(cbOrbsu), "changed",
                   G_CALLBACK(onOrbsdToggled), (gpointer)orbsd_spu);
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cbOrbsu), render, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(cbOrbsu), render,
                                 "active", ACTIVATED);
  render = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cbOrbsu), render, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(cbOrbsu), render,
                                 "text", LABEL);
  gtk_box_pack_start(GTK_BOX(hbox), cbOrbsu, FALSE, FALSE, 0);
  wd = gtk_label_new(_("down"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  cbOrbsd = gtk_combo_box_new_with_model(GTK_TREE_MODEL(orbsd_spd));
  render = gtk_cell_renderer_toggle_new();
  g_signal_connect(G_OBJECT(cbOrbsd), "changed",
                   G_CALLBACK(onOrbsdToggled), (gpointer)orbsd_spd);
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cbOrbsd), render, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(cbOrbsd), render,
                                 "active", ACTIVATED);
  render = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cbOrbsd), render, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(cbOrbsd), render,
                                 "text", LABEL);
  gtk_box_pack_start(GTK_BOX(hbox), cbOrbsd, FALSE, FALSE, 0);
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("for k-point(s)"));
  gtk_misc_set_alignment(GTK_MISC(wd), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  cbKpt = gtk_combo_box_new_with_model(GTK_TREE_MODEL(orbsd_kpt));
  render = gtk_cell_renderer_toggle_new();
  g_signal_connect(G_OBJECT(cbKpt), "changed",
                   G_CALLBACK(onOrbsdToggled), (gpointer)orbsd_kpt);
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cbKpt), render, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(cbKpt), render,
                                 "active", ACTIVATED);
  render = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(cbKpt), render, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(cbKpt), render,
                                 "text", LABEL);
  if (wf && BIGDFT_ORBS(wf)->nkpts ==1)
    gtk_combo_box_set_active(GTK_COMBO_BOX(cbKpt), 0);
  gtk_box_pack_start(GTK_BOX(hbox), cbKpt, FALSE, FALSE, 0);

  /* The treeview. */
  ct = gtk_scrolled_window_new((GtkAdjustment*)0, (GtkAdjustment*)0);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ct),
				      GTK_SHADOW_ETCHED_IN);
  gtk_box_pack_start(GTK_BOX(vbox2), ct, TRUE, TRUE, 0);
  wd = gtk_tree_view_new_with_model(GTK_TREE_MODEL(scfdata));
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(wd), FALSE);
  gtk_container_add(GTK_CONTAINER(ct), wd);
  /* Columns. */
  render = gtk_cell_renderer_text_new();
  col = gtk_tree_view_column_new_with_attributes("", render, "markup", SCF_LABEL, NULL);
  gtk_tree_view_column_set_expand(col, TRUE);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), col);
  render = gtk_cell_renderer_text_new();
  col = gtk_tree_view_column_new_with_attributes("", render, NULL);
  gtk_tree_view_column_set_cell_data_func(col, render, ptInfos,
                                          (gpointer)0, (GDestroyNotify)0);
  g_signal_connect(G_OBJECT(wd), "button-release-event",
		   G_CALLBACK(onTreeViewClicked), (gpointer)col);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), col);

  /* Action buttons. */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  btSCF = gtk_button_new_with_label(_("retrieve selection"));
  gtk_widget_set_sensitive(btSCF, FALSE);
  g_signal_connect(G_OBJECT(btSCF), "clicked",
		   G_CALLBACK(onGetSCF), (gpointer)wd);
  gtk_box_pack_end(GTK_BOX(hbox), btSCF, FALSE, FALSE, 0);
  wd = gtk_label_new(_("Wfn. repr.:"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  cbWf = gtk_combo_box_text_new();
  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cbWf), _("real part"));
  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cbWf), _("imaginary part"));
  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cbWf), _("partial density"));
  gtk_combo_box_set_active(GTK_COMBO_BOX(cbWf), 0);
  gtk_box_pack_start(GTK_BOX(hbox), cbWf, FALSE, FALSE, 0);

  return vbox2;
}

static void onOrbsdToggled(GtkComboBox *cb, gpointer data)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  gboolean valid;
  VisuData *dataObj;
  BigDFT_Wf *wf;
  guint i, j, val;
  gchar *name;
  StoredWfData *dt;

  if (!gtk_combo_box_get_active_iter(cb, &iter))
    return;

  gtk_tree_model_get(GTK_TREE_MODEL(data), &iter,
                     ACTIVATED, &valid, VALUE, &val, -1);
  gtk_list_store_set(GTK_LIST_STORE(data), &iter,
                     ACTIVATED, !valid, -1);

  /* Connect or disconnect the one-wave-ready signals. */
  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT));
  if (!dataObj)
    return;
  wf = (BigDFT_Wf*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_wf");
  dt = (StoredWfData*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_storedWf");
  if (!dt || !wf)
    return;

  if (valid)
    {
      /* We remove all signals according to val. */
      if (cb == GTK_COMBO_BOX(cbOrbsd))
        val += BIGDFT_ORBS(wf)->norbu;
      for (i = 0; i < (cb == GTK_COMBO_BOX(cbKpt))?BIGDFT_ORBS(wf)->norb:
             BIGDFT_ORBS(wf)->nkpts; i++)
        {
          if (cb == GTK_COMBO_BOX(cbKpt))
            j = val * BIGDFT_ORBS(wf)->norb + i;
          else
            j = i * BIGDFT_ORBS(wf)->norb + val;
          if (dt[j].signal)
            g_signal_handler_disconnect(G_OBJECT(wf), dt[j].signal);
          dt[j].signal = 0;
        }
    }
  else
    {
      /* We add signals to each checked values. */
      for (i = 0; i < ((cb == GTK_COMBO_BOX(cbKpt))?BIGDFT_ORBS(wf)->norb:
                       BIGDFT_ORBS(wf)->nkpts); i++)
        {
          if (cb == GTK_COMBO_BOX(cbKpt) && i < BIGDFT_ORBS(wf)->norbu)
            {
              model = GTK_TREE_MODEL(orbsd_spu);
              j = BIGDFT_ORBS(wf)->norbu - i - 1;
            }
          else if (cb == GTK_COMBO_BOX(cbKpt))
            {
              model = GTK_TREE_MODEL(orbsd_spd);
              j = BIGDFT_ORBS(wf)->norb - i - 1;
            }
          else
            {
              model = GTK_TREE_MODEL(orbsd_kpt);
              j = i;
            }
          if (gtk_tree_model_iter_nth_child(model, &iter, NULL, j))
            {
              gtk_tree_model_get(model, &iter, ACTIVATED, &valid, VALUE, &j, -1);
              if (valid)
                {
                  if (cb == GTK_COMBO_BOX(cbOrbsu))
                    {
                      name = g_strdup_printf("one-wave-ready::%d-%d-up", j + 1, val + 1);
                      j = j * BIGDFT_ORBS(wf)->norb + val;
                    }
                  else if (cb == GTK_COMBO_BOX(cbOrbsd))
                    {
                      name = g_strdup_printf("one-wave-ready::%d-%d-down", j + 1, val + 1);
                      j = j * BIGDFT_ORBS(wf)->norb + val;
                    }
                  else if (i < BIGDFT_ORBS(wf)->norbu)
                    {
                      name = g_strdup_printf("one-wave-ready::%d-%d-up", val + 1, j + 1);
                      j = val * BIGDFT_ORBS(wf)->norb + j;
                    }
                  else
                    {
                      name = g_strdup_printf("one-wave-ready::%d-%d-down", val + 1, j + 1);
                      j = val * BIGDFT_ORBS(wf)->norb + j;
                    }
                  DBG_fprintf(stderr, "BigDFT Run: adding '%s' signal.\n", name);
                  dt[j].signal = g_signal_connect(G_OBJECT(wf), name,
                                                  G_CALLBACK(onOneWaveReady), (gpointer)0);
                  g_free(name);
                }
            }
        }
    }
}
static void ptInfos(GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
                    GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data _U_)
{
  double eval, occ, fact, tol;
  gchar *lbl;
  guint kind, units;
  gpointer unitsFact;
  const gchar *unitsLbl[] = {"Ht", "eV"};

  units = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tree_column), "units"));
  unitsFact = g_object_get_data(G_OBJECT(tree_column), "unitsFact");
  if (unitsFact)
    fact = *(double*)unitsFact;
  else
    fact = 1.;
  gtk_tree_model_get(tree_model, iter, SCF_EVAL, &eval,
                     SCF_OCCUP, &occ, SCF_TYPE, &kind,
                     SCF_TOL, &tol, -1);
  switch (kind)
    {
    case SCF_TYPE_WF:
      lbl = g_strdup_printf("%5.5f | %3.3f", eval * fact, occ);
      break;
    case SCF_TYPE_ITER_WFN:
      if (eval != 0.)
        if (tol > 0. && occ < tol)
          lbl = g_strdup_printf("%10.10f %s <span size=\"smaller\" "
                                "color=\"YellowGreen\">(%2.2e)</span>",
                                eval * fact, unitsLbl[units], occ);
        else if (tol > 0. && occ >= tol)
          lbl = g_strdup_printf("%10.10f %s <span size=\"smaller\" "
                                "color=\"Tomato\">(%2.2e)</span>",
                                eval * fact, unitsLbl[units], occ);
        else
          lbl = g_strdup_printf("%10.10f %s <span size=\"smaller\" "
                                "color=\"#555555\">(%2.2e)</span>",
                                eval * fact, unitsLbl[units], occ);
      else
        lbl = g_strdup("");
      break;
    default:
      lbl = g_strdup("");
    }
  g_object_set(G_OBJECT(cell), "markup", lbl, NULL);
  g_free(lbl);
}
static void onGetSCF(GtkButton *bt _U_, gpointer user_data)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  GArray *psic, *wrap;
  double *data, *data2;
  const BigDFT_Locreg *glr;
  BigDFT_Wf *wf;
  BigDFT_LocalFields *denspot;
  VisuScalarField *field;
  gchar *lbl, *str;
  double box[3][3];
  float shift[3];
  guint kind, istep, ikpt, iorb, ispin, nspinor, psiSize, i;
  const VisuSurface *surf;
  float *isoValues;
  VisuData *dataObj;
  StoredWfData *dt;
  const gchar *names;
  VisuBox *boxObj;

  if (!gtk_tree_selection_get_selected
      (gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data)), &model, &iter))
    return;

  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT));
  if (!dataObj)
    return;

  gtk_tree_model_get(model, &iter, SCF_TYPE, &kind, -1);

  dt = (StoredWfData*)0;
  surf = (const VisuSurface*)0;
  switch (kind)
    {
    case SCF_TYPE_WF:
      gtk_tree_model_get(model, &iter, SCF_DATA, &psic, SCF_LABEL, &str,
                         SCF_ITER, &istep, SCF_GLR, &glr, SCF_OBJECT, &wf,
                         SCF_IKPT, &ikpt, SCF_IORB, &iorb, SCF_NSPINOR, &nspinor,
                         SCF_ISPIN, &ispin, SCF_MEMORY, &psiSize, -1);
      if (gtk_combo_box_get_active(GTK_COMBO_BOX(cbWf)) == BIGDFT_PARTIAL_DENSITY)
        lbl = g_strdup_printf(_("partial density (iter: %d)\n  ikpt: %d, "
                                "iorb: %d"), istep, ikpt + 1, iorb + 1);
      else
        lbl = g_strdup_printf(_("wavefunction (iter: %d)\n  ikpt: %d, "
                                "iorb: %d"), istep, ikpt + 1, iorb + 1);
      /* Depending on the spinor representation, we work on data. */
      data = (double*)0;
      if (gtk_combo_box_get_active(GTK_COMBO_BOX(cbWf)) == BIGDFT_REAL ||
          gtk_combo_box_get_active(GTK_COMBO_BOX(cbWf)) == BIGDFT_PARTIAL_DENSITY)
        data = bigdft_locreg_convert_to_isf(glr, (double*)psic->data);
      else if (nspinor == 2)
        data = bigdft_locreg_convert_to_isf(glr, ((double*)psic->data) + psiSize / 2);
      else
        visu_ui_raiseWarning(_("Retrieve wavefunction"),
                             _("Wavefunction has no imaginary part"), NULL);
      if (gtk_combo_box_get_active(GTK_COMBO_BOX(cbWf)) == BIGDFT_PARTIAL_DENSITY &&
          nspinor == 1)
        for (i = 0; i < glr->ni[0] * glr->ni[1] * glr->ni[2]; i++)
          data[i] = data[i] * data[i];
      if (gtk_combo_box_get_active(GTK_COMBO_BOX(cbWf)) == BIGDFT_PARTIAL_DENSITY &&
          nspinor == 2)
        {
          data2 = bigdft_locreg_convert_to_isf(glr, ((double*)psic->data) + psiSize / 2);
          for (i = 0; i < glr->ni[0] * glr->ni[1] * glr->ni[2]; i++)
            data[i] = data[i] * data[i] + data2[i] * data2[i];
          g_free(data2);
        }
      g_array_unref(psic);
      g_free(str);
      /* We get the surface of the first wavefunction drawn. */
      dt = (StoredWfData*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_storedWf");
      surf = (dt)?dt[ikpt * BIGDFT_ORBS(wf)->norb + iorb +
                     ((ispin == BIGDFT_SPIN_DOWN)?BIGDFT_ORBS(wf)->norbu:0)].surf:
        (const VisuSurface*)0;
      break;
    case SCF_TYPE_V_EXT:
      gtk_tree_model_get(model, &iter, SCF_OBJECT, &denspot, SCF_GLR, &glr, -1);
      data = denspot->v_ext;
      lbl = g_strdup(_("external potential"));
      break;
    case SCF_TYPE_DENSITY:
      gtk_tree_model_get(model, &iter, SCF_DATA, &psic, SCF_GLR, &glr,
                         SCF_OBJECT, &denspot, SCF_ITER, &istep, -1);
      data = (double*)psic->data;
      lbl = g_strdup_printf(_("electronic density (iter: %d)"), istep);
      break;
    default:
      glr = (const BigDFT_Locreg*)0;
      data = (double*)0;
      psic = (GArray*)0;
      break;
    }
  if (!data || !glr)
    return;

  field = visu_scalar_field_new(lbl);
  g_free(lbl);
  if (!field)
    g_warning("impossible to create a VisuScalarField object.");
  else
    {
      memset(box, 0, sizeof(double) * 9);
      box[0][0] = glr->h[0] * glr->ni[0] * 0.5;
      box[1][1] = glr->h[1] * glr->ni[1] * 0.5;
      box[2][2] = glr->h[2] * glr->ni[2] * 0.5;
      boxObj = visu_box_new_full(box, (BIGDFT_ATOMS(glr)->geocode != 'F')?VISU_BOX_PERIODIC:VISU_BOX_FREE);
      visu_boxed_setBox(VISU_BOXED(field), VISU_BOXED(boxObj));
      g_object_unref(boxObj);
      visu_scalar_field_setGridSize(field, glr->ni);
      shift[0] = glr->h[0] * (glr->ni[0] - 2 * glr->n[0] - 2) * 0.25 -
        glr->h[0] * glr->ns[0];
      shift[1] = glr->h[1] * (glr->ni[1] - 2 * glr->n[1] - 2) * 0.25 -
        glr->h[1] * glr->ns[1];
      shift[2] = glr->h[2] * (glr->ni[2] - 2 * glr->n[2] - 2) * 0.25 -
        glr->h[2] * glr->ns[2];
      if (BIGDFT_ATOMS(glr)->geocode == 'S')
        shift[1] = glr->h[1] * (glr->ni[1] - 2 * glr->n[1] - 3) * 0.25 -
          glr->h[1] * glr->ns[1];
      visu_scalar_field_setOriginShift(field, shift);
      wrap = g_array_new(FALSE, FALSE, sizeof(double));
      wrap->data = (gchar*)data;
      wrap->len = glr->ni[0] * glr->ni[1] * glr->ni[2];
      visu_scalar_field_setData(field, wrap, TRUE);
      g_array_free(wrap, FALSE);
      visu_scalarfield_set_add(visu_scalarfield_set_getDefault(), lbl, field);
      visu_ui_panel_surfaces_setUsed(TRUE);
      if (surf)
        {
          isoValues = visu_surface_getPropertyFloat((VisuSurface*)surf,
                                                   VISU_SURFACE_PROPERTY_POTENTIAL);
          names = visu_surface_resource_getLabel(visu_surface_getResource((VisuSurface*)surf));
          visu_ui_panel_surfaces_compute(field, isoValues, &names, 1);
        }
      else
        visu_ui_panel_surfaces_computeAuto(field);
    }

  g_object_unref(G_OBJECT(glr));
  switch (kind)
    {
    case SCF_TYPE_WF:
      if (dt)
        dt[ikpt * BIGDFT_ORBS(wf)->norb + iorb +
           ((ispin == BIGDFT_SPIN_DOWN)?BIGDFT_ORBS(wf)->norbu:0)].surf = surf;
      g_object_unref(wf);
      g_free(data);
      break;
    case SCF_TYPE_DENSITY:
      g_array_unref(psic);
    case SCF_TYPE_V_EXT:
      g_object_unref(G_OBJECT(denspot));
      break;
    default:
      break;
    }
}
static void onUnitSetHt(GtkCheckMenuItem *item, gpointer data)
{
  static double fact = 1.;

  if (!gtk_check_menu_item_get_active(item))
    return;

  g_object_set_data(G_OBJECT(data), "unitsFact", &fact);
  g_object_set_data(G_OBJECT(data), "units", GINT_TO_POINTER(0));
}
static void onUnitSeteV(GtkCheckMenuItem *item, gpointer data)
{
  static double fact = 27.2113834;

  if (!gtk_check_menu_item_get_active(item))
    return;

  g_object_set_data(G_OBJECT(data), "unitsFact", &fact);
  g_object_set_data(G_OBJECT(data), "units", GINT_TO_POINTER(1));
}
static void onSaveFile(GtkMenuItem *item _U_, gpointer data)
{
  GtkTreeIter iter;
  GtkTreeModel *model;
  guint kind, ikpt, iorb, ispin;
  GArray *psic;
  BigDFT_Wf *wf;

  if (!gtk_tree_selection_get_selected
      (gtk_tree_view_get_selection(GTK_TREE_VIEW(data)), &model, &iter))
    return;

  gtk_tree_model_get(model, &iter, SCF_TYPE, &kind, -1);

  switch (kind)
    {
    case SCF_TYPE_WF:
      gtk_tree_model_get(model, &iter, SCF_DATA, &psic, SCF_OBJECT, &wf,
                         SCF_IKPT, &ikpt, SCF_IORB, &iorb, SCF_ISPIN, &ispin, -1);
      bigdft_wf_write_psi_compress(wf, "wfn", BIGDFT_WF_FORMAT_BINARY, (double*)psic->data,
                                   ikpt + 1, iorb + 1, ispin,
                                   psic->len / BIGDFT_ORBS(wf)->nspinor);

      break;
    case SCF_TYPE_V_EXT:
      break;
    case SCF_TYPE_DENSITY:
      break;
    default:
      break;
    }
}
static gboolean onTreeViewClicked(GtkWidget *widget, GdkEventButton *event,
				  gpointer user_data)
{
  GtkWidget *menu, *item;
  GSList *group;
  guint units, kind;
  gboolean sensitive;
  GtkTreeIter iter;
  GtkTreeModel *model;

  if (event->button != 3)
    return FALSE;

  DBG_fprintf(stderr, "BigDFT: right clic detected.\n");
  units = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(user_data), "units"));

  menu = gtk_menu_new();
  
  item = gtk_radio_menu_item_new_with_mnemonic((GSList*)0, _("Energies in _Ht"));
  group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item));
  g_signal_connect(G_OBJECT(item), "toggled",
                   G_CALLBACK(onUnitSetHt), user_data);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  item = gtk_radio_menu_item_new_with_mnemonic(group, _("Energies in _eV"));
  if (units == 1)
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
  g_signal_connect(G_OBJECT(item), "toggled",
                   G_CALLBACK(onUnitSeteV), user_data);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);

  item = gtk_separator_menu_item_new();
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);

  /* See if we have a save item. */
  sensitive = FALSE;
  if (gtk_tree_selection_get_selected
      (gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)), &model, &iter))
    {
      gtk_tree_model_get(model, &iter, SCF_TYPE, &kind, -1);
      sensitive = (kind == SCF_TYPE_WF);
    }
  item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL);
  gtk_widget_set_sensitive(item, sensitive);
  g_signal_connect(G_OBJECT(item), "activate",
                   G_CALLBACK(onSaveFile), widget);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);

  gtk_widget_show_all(menu);
  gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 
		 3, event->time);

  return FALSE;
}

static void onVextReady(BigDFT_LocalFields *denspot, gpointer data)
{
  GtkTreeIter parent, child;
  guint vSize;
  gchar *lbl, *lblSize;

  DBG_fprintf(stderr, "BigDFT (%p): get V_ext ready signal.\n", (gpointer)g_thread_self());
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btVext)))
    return;

  gtk_widget_set_sensitive(btSCF, TRUE);

  treeGetSCFIter(&parent, 0, NULL);

  vSize = denspot->ni[0] * denspot->ni[1] * denspot->ni[2] * sizeof(double);
#if GLIB_MINOR_VERSION < 30
  lblSize = g_format_size_for_display((goffset)vSize);
#else
  lblSize = g_format_size((guint64)vSize);
#endif
  lbl = g_strdup_printf("external potential "
                        "<span size=\"smaller\">- %s</span>", lblSize);
  gtk_tree_store_insert(scfdata, &child, &parent, -1);
  gtk_tree_store_set(scfdata, &child, SCF_ITER, 0,
                     SCF_TYPE, SCF_TYPE_V_EXT,
                     SCF_LABEL, lbl,
                     SCF_MEMORY, vSize,
                     SCF_OBJECT, denspot,
                     SCF_GLR, G_OBJECT(data),
                     -1);
  g_free(lblSize);
  g_free(lbl);
}
static void onDensityReady(BigDFT_LocalFields *denspot, guint iter, gpointer data)
{
  GtkTreeIter parent, child;
  guint vSize;
  gchar *lbl, *lblSize;
  GArray *dens;

  DBG_fprintf(stderr, "BigDFT (%p): get density ready signal.\n", (gpointer)g_thread_self());
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btDens)))
    return;

  gtk_widget_set_sensitive(btSCF, TRUE);

  treeGetSCFIter(&parent, iter, NULL);

  vSize = denspot->ni[0] * denspot->ni[1] * denspot->ni[2];
#if GLIB_MINOR_VERSION < 30
  lblSize = g_format_size_for_display((goffset)(vSize * sizeof(double)));
#else
  lblSize = g_format_size((guint64)(vSize * sizeof(double)));
#endif
  lbl = g_strdup_printf("electronic density "
                        "<span size=\"smaller\">- %s</span>", lblSize);
  dens = g_array_sized_new(FALSE, FALSE, sizeof(double), vSize);
  memcpy(dens->data, denspot->rhov, sizeof(double) * vSize);
  dens = g_array_set_size(dens, vSize);
  gtk_tree_store_insert(scfdata, &child, &parent, -1);
  gtk_tree_store_set(scfdata, &child, SCF_ITER, iter,
                     SCF_TYPE, SCF_TYPE_DENSITY,
                     SCF_LABEL, lbl,
                     SCF_MEMORY, vSize,
                     SCF_DATA, dens,
                     SCF_OBJECT, denspot,
                     SCF_GLR, G_OBJECT(data),
                     -1);
  g_free(lblSize);
  g_free(lbl);
  g_array_unref(dens);
}
static void onIter(BigDFT_OptLoop *optloop, BigDFT_Energs *energs, gpointer data _U_)
{
  GtkTreeIter parent;

  DBG_fprintf(stderr, "BigDFT (%p): get iter signal.\n", (gpointer)g_thread_self());
  treeGetSCFIter(&parent, optloop->iter, optloop);
  gtk_tree_store_set(scfdata, &parent, SCF_EVAL, energs->eKS,
                     SCF_OCCUP, optloop->gnrm, -1);
}
static void onDone(BigDFT_OptLoop *optloop, BigDFT_Energs *energs, gpointer data _U_)
{
  GtkTreeIter parent;

  DBG_fprintf(stderr, "BigDFT (%p): get done signal.\n", (gpointer)g_thread_self());
  treeGetSCFIter(&parent, optloop->iter, optloop);
  gtk_tree_store_set(scfdata, &parent, SCF_EVAL, energs->eKS,
                     SCF_OCCUP, optloop->gnrm, SCF_TOL, optloop->gnrm_cv, -1);
}
static void psiInsert(BigDFT_Wf *wf, GArray *psic, BigDFT_PsiId ipsi,
                      guint ikpt, guint iorb, BigDFT_Spin spin,
                      guint iscf, GtkTreeIter *parent)
{
  BigDFT_Orbs *orbs;
  gchar *lbl, *lblSize;
  const gchar *lblSpin[3] = {"\342\206\221", "\342\206\223", "\342\207\265"},
    *lblPsi[2] = {"|\316\250", "H|\316\250"};
  GtkTreeIter child;
  double eval;
  guint iorbp;

#if GLIB_MINOR_VERSION < 30
  lblSize = g_format_size_for_display((goffset)(psic->len * sizeof(double)));
#else
  lblSize = g_format_size((guint64)(psic->len * sizeof(double)));
#endif
  orbs = BIGDFT_ORBS(wf);
  iorbp = ikpt * orbs->norb + iorb;
  if (!bigdft_orbs_get_linear(orbs))
    lbl = g_strdup_printf("%s<sup>%s</sup><sub><i>ikpt</i>: %d, <i>iorb</i>: %d</sub>"
                          "\342\237\251 <span size=\"smaller\">(%s)</span>",
                          lblPsi[ipsi], lblSpin[(BIGDFT_ORBS(wf)->nspin == 1)?2:spin],
                          ikpt + 1, iorb + 1, lblSize);
  else
    lbl = g_strdup_printf("%s<sup>%s</sup><sub><i>ikpt</i>: %d, <i>iorb</i>: %d</sub>"
                          "\342\237\251 <i>ilr</i>: %d "
                          "<span size=\"smaller\">(%s)</span>",
                          lblPsi[ipsi], lblSpin[(BIGDFT_ORBS(wf)->nspin == 1)?2:spin],
                          ikpt + 1, iorb + 1, orbs->inwhichlocreg[iorbp], lblSize);
  eval = (orbs->eval)?orbs->eval[iorbp]:0.;
  gtk_tree_store_insert(scfdata, &child, parent, -1);
  gtk_tree_store_set(scfdata, &child, SCF_ITER, iscf,
                     SCF_TYPE, SCF_TYPE_WF,
                     SCF_LABEL, lbl,
                     SCF_MEMORY, psic->len,
                     SCF_IKPT, ikpt,
                     SCF_IORB, iorb,
                     SCF_ISPIN, spin,
                     SCF_NSPINOR, orbs->nspinor,
                     SCF_EVAL, eval,
                     SCF_OCCUP, orbs->occup[iorbp],
                     SCF_DATA, psic,
                     SCF_OBJECT, wf,
                     SCF_GLR, bigdft_wf_get_locreg(wf, ikpt + 1, iorb + 1, spin, 0),
                     -1);
  g_free(lblSize);
  g_free(lbl);
}
static gboolean psiAdd(BigDFT_Wf *wf, guint iscf, BigDFT_PsiId ipsi, guint ikpt,
                       BigDFT_Spin spin, GtkTreeIter *parent, gboolean init)
{
  GtkTreeIter iter;
  guint iorb, psiSize;
  gboolean valid, checked;
  const double *psic;
  GArray *psic_;
  GtkTreeModel *orbsd_spin;

  if (spin == BIGDFT_SPIN_UP)
    orbsd_spin = GTK_TREE_MODEL(orbsd_spu);
  else
    orbsd_spin = GTK_TREE_MODEL(orbsd_spd);
  for (valid = gtk_tree_model_get_iter_first(orbsd_spin, &iter); valid;
       valid = gtk_tree_model_iter_next(orbsd_spin, &iter))
    {
      gtk_tree_model_get(orbsd_spin, &iter, ACTIVATED, &checked, VALUE, &iorb, -1);
      if (checked)
        {
          if (!init)
            {
              treeGetSCFIter(parent, iscf, NULL);
              gtk_widget_set_sensitive(btSCF, TRUE);
              init = TRUE;
            }
          DBG_fprintf(stderr, "BigDFT: greping compressed wfn %d %d %d.\n",
                      ikpt, iorb, spin);
          psic = bigdft_wf_get_psi_compress(wf, ikpt + 1, iorb + 1, spin,
                                            BIGDFT_PARTIAL_DENSITY, &psiSize, 0);
          psic_ = g_array_sized_new(FALSE, FALSE, sizeof(double), psiSize);
          memcpy(psic_->data, psic, sizeof(double) * psiSize);
          psic_ = g_array_set_size(psic_, psiSize);
          DBG_fprintf(stderr, " | storage size is %d.\n", psiSize);
          psiInsert(wf, psic_, ipsi, ikpt, iorb, spin, iscf, parent);
          g_array_unref(psic_);
        }
    }
  return init;
}
static void onPsiReady(BigDFT_Wf *wf, guint iscf, gpointer data _U_)
{
  GtkTreeIter iter, parent;
  guint ikpt;
  gboolean init, valid, checked;

  init = FALSE;
  
  DBG_fprintf(stderr, "BigDFT (%p): get psi ready signal.\n", (gpointer)g_thread_self());
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(orbsd_kpt), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(orbsd_kpt), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(orbsd_kpt), &iter,
                         ACTIVATED, &checked,
                         VALUE, &ikpt, -1);
      if (checked)
        {
          init = psiAdd(wf, iscf, BIGDFT_PSI, ikpt, BIGDFT_SPIN_UP, &parent, init);
          init = psiAdd(wf, iscf, BIGDFT_PSI, ikpt, BIGDFT_SPIN_DOWN, &parent, init);
        }
    }
}
static void onOneWaveReady(BigDFT_Wf *wf, guint iter, GArray *psic, BigDFT_PsiId ipsi,
                           guint ikpt, guint iorb, guint ispin, gpointer data _U_)
{
  GtkTreeIter parent;

  DBG_fprintf(stderr, "BigDFT (%p): get one-wave ready signal (%d %d %d -> %d)"
              " at iter %d.\n", (gpointer)g_thread_self(),
              ikpt, iorb, ispin, psic->len, iter);

  treeGetSCFIter(&parent, iter, NULL);

  psiInsert(wf, psic, ipsi, ikpt - 1, iorb - 1, ispin, iter, &parent);

  gtk_widget_set_sensitive(btSCF, TRUE);
}

static void onSocketClose(gpointer data)
{
  struct _socketStuff *ct = (struct _socketStuff*)data;

  g_object_unref(G_OBJECT(ct->denspot));
  g_object_unref(G_OBJECT(ct->wf));
  g_object_unref(G_OBJECT(ct->energs));
  g_object_unref(G_OBJECT(ct->optloop));
  g_object_unref(G_OBJECT(ct->cancellable));
  bigdft_signals_client_free(ct->client);
  g_async_queue_unref(ct->message);
  g_free(ct);
  g_object_set_data(G_OBJECT(btRun), "ct", (gpointer)0);

  gtk_widget_set_sensitive(btRun, TRUE);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(btRun), FALSE);

#if GTK_MINOR_VERSION > 19  || GTK_MAJOR_VERSION > 2
  gtk_widget_hide(spRun);
  gtk_spinner_stop(GTK_SPINNER(spRun));
#endif
}

static void onRunBigDFT(GtkToggleButton *bt, gpointer data)
{
  VisuData *dataObj;
  BigDFT_Inputs *in;
  BigDFT_Wf *wf, *wf_;
  BigDFT_Proj *proj;
  BigDFT_LocalFields *denspot;
  BigDFT_Energs *energs;
  BigDFT_OptLoop *optloop;
  double frmult;
  gchar *host;
  GError *error;
  struct _socketStuff *ct;

  if (!gtk_toggle_button_get_active(bt))
    {
      ct = (struct _socketStuff *)g_object_get_data(G_OBJECT(bt), "ct");
      if (!ct)
        return;
      g_cancellable_cancel(ct->cancellable);
      gtk_widget_set_sensitive(btRun, FALSE);
      return;
    }

  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT));
  if (!dataObj)
    return;

  wf = (BigDFT_Wf*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_wf");
  in = (BigDFT_Inputs*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_inputs");
  if (!in || !wf)
    return;

  wf_ = (BigDFT_Wf*)g_object_get_data(G_OBJECT(bt), "BigDFT_wf");
  if (wf != wf_)
    gtk_tree_store_clear(scfdata);

  frmult = GET_OPT_DBL(fmtWvl, "frmult");
  proj    = bigdft_proj_new(BIGDFT_LOCREG(wf->lzd), BIGDFT_ORBS(wf), frmult);
  denspot = bigdft_localfields_new(wf->lzd, in, 0, 1);
  energs  = bigdft_energs_new();
  optloop = bigdft_optloop_new();

  sigDens = g_signal_connect(G_OBJECT(denspot), "density-ready",
                             G_CALLBACK(onDensityReady), BIGDFT_LOCREG(wf->lzd));
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btDens)))
    g_signal_handler_block(G_OBJECT(denspot), sigDens);
  sigVext = g_signal_connect(G_OBJECT(denspot), "v-ext-ready",
                             G_CALLBACK(onVextReady), BIGDFT_LOCREG(wf->lzd));
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btVext)))
    g_signal_handler_block(G_OBJECT(denspot), sigVext);
  g_signal_connect(G_OBJECT(wf), "psi-ready",
                   G_CALLBACK(onPsiReady), (gpointer)0);
  g_signal_connect(G_OBJECT(optloop), "iter-wavefunctions",
                   G_CALLBACK(onIter), (gpointer)0);
  g_signal_connect(G_OBJECT(optloop), "done-wavefunctions",
                   G_CALLBACK(onDone), (gpointer)0);

  if (gtk_combo_box_get_active(GTK_COMBO_BOX(data)) == 0)
    {
      bigdft_wf_optimization(wf, proj, denspot, energs, optloop, in, TRUE, 0, 1);
      gtk_widget_set_sensitive(GTK_WIDGET(bt), FALSE);
    }
  else
    {
      ct = g_malloc0(sizeof(struct _socketStuff));
      /* host = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(data)); */
      host = g_strdup(gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(data)))));
      DBG_fprintf(stderr, "BigDFT: try to connect to '%s'.\n", host);
      error = (GError*)0;
      ct->client = bigdft_signals_client_new(host, NULL, &error);
      g_free(host);
      if (!ct->client)
        {
          visu_ui_raiseWarning(_("Running BigDFT on a host"),
                               error->message, NULL);
          g_error_free(error);
          gtk_toggle_button_set_active(bt, FALSE);
          g_free(ct);
        }
      else
        {
#if GTK_MINOR_VERSION > 19  || GTK_MAJOR_VERSION > 2
          gtk_spinner_start(GTK_SPINNER(spRun));
          gtk_widget_show(spRun);
#endif

          ct->message = g_async_queue_new();

          ct->wf      = wf;
          ct->denspot = denspot;
          ct->energs  = energs;
          ct->optloop = optloop;
          ct->cancellable = g_cancellable_new();
          g_object_ref(G_OBJECT(denspot));
          g_object_ref(G_OBJECT(wf));
          g_object_ref(G_OBJECT(energs));
          g_object_ref(G_OBJECT(optloop));
          g_object_set_data(G_OBJECT(bt), "ct", ct);
          bigdft_signals_client_create_thread
            (ct->client, ct->energs, ct->wf, ct->denspot, ct->optloop, ct->cancellable,
             onSocketClose, ct);
        }
      DBG_fprintf(stderr, "BigDFT: connexion done.\n");
    }
  g_object_set_data(G_OBJECT(bt), "BigDFT_wf", wf);

  g_object_unref(G_OBJECT(denspot));
  g_object_unref(G_OBJECT(proj));
  g_object_unref(G_OBJECT(energs));
  g_object_unref(G_OBJECT(optloop));

  DBG_fprintf(stderr, "BigDFT (%p): now running.\n", (gpointer)g_thread_self());
}

static void treeInsertIter(GtkTreeIter *iter, GtkTreeIter *parent, guint iLoop,
                           const gchar *label, guint type)
{
  gchar *lbl;

  /* We need to create a root node for this iteration. */
  lbl = (iLoop == 0)?
    g_strdup_printf(_("<b>%s</b>"), label):
    g_strdup_printf("<b>%s</b> <span font=\"mono\">%3d</span>", label, iLoop);
  gtk_tree_store_insert(scfdata, iter, parent, -1);
  gtk_tree_store_set(scfdata, iter, SCF_LABEL, lbl, SCF_ITER, iLoop, SCF_TYPE, type, -1);
  g_free(lbl);
}
static void treeGetIter(GtkTreeIter *iter, GtkTreeIter *parent, guint iLoop,
                        const gchar *label, guint type, guint iLoopChild)
{
  gboolean valid;
  guint ival, ivalChild;
  GtkTreeIter child;

  if (iLoop > 0)
    for (valid = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(scfdata), iter, parent, 0);
         valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(scfdata), iter))
      {
        gtk_tree_model_get(GTK_TREE_MODEL(scfdata), iter, SCF_ITER, &ival, -1);
        if (ival == iLoop)
          break;
      }
  else
    {
      ival = 0;
      /* Get the last one if his children SCF_ITER are below iLoopChild. */
      if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(scfdata), parent) > 0)
        valid = gtk_tree_model_iter_nth_child
          (GTK_TREE_MODEL(scfdata), iter, parent,
           gtk_tree_model_iter_n_children(GTK_TREE_MODEL(scfdata), parent) - 1);
      else
        valid = FALSE;
      if (valid)
        {
          gtk_tree_model_get(GTK_TREE_MODEL(scfdata), iter, SCF_ITER, &ival, -1);
          valid = (ival > 0);
          if (valid && iLoopChild > 0)
            {
              for (valid = gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(scfdata), &child, iter, 0);
                   valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(scfdata), &child))
                {
                  gtk_tree_model_get(GTK_TREE_MODEL(scfdata), &child, SCF_ITER, &ivalChild, -1);
                  if (ivalChild > iLoopChild)
                    break;
                }
              valid = (ivalChild <= iLoopChild);
            }
        }
    } 
  if (!valid)
    treeInsertIter(iter, parent, (iLoop == 0)?ival + 1:iLoop, label, type);
}
static void treeGetSCFIter(GtkTreeIter *parent, guint iscf, BigDFT_OptLoop *loop)
{
  GtkTreeIter iterHam, iterSub;
  
  if (iscf > 0)
    {
      treeGetIter(&iterHam, NULL, (loop)?loop->itrp:0,
                  _("Hamiltonian optimisation"), SCF_TYPE_ITER_HAM, 0);
      treeGetIter(&iterSub, &iterHam, (loop)?loop->itrep:0,
                  _("Sub-space optimisation"), SCF_TYPE_ITER_SUB, iscf);
      treeGetIter(parent, &iterSub, iscf,
                  _("Wfn iteration"), SCF_TYPE_ITER_WFN, 0);
    }
  else
    {
      if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(scfdata), parent))
        treeInsertIter(parent, NULL, 0, _("Initial stage"), SCF_TYPE_ITER_WFN);
    }
}

static void update_orbsd(const VisuData *dataObj)
{
  BigDFT_Wf *wf;
  guint i;
  GtkTreeIter iter;
  gchar *lbl;
  StoredWfData *dt;

  gtk_list_store_clear(orbsd_kpt);
  gtk_list_store_clear(orbsd_spu);
  gtk_list_store_clear(orbsd_spd);

  if (!dataObj)
    return;
  wf = (BigDFT_Wf*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_wf");
  if (!wf)
    return;
  
  for (i = 0; i < BIGDFT_ORBS(wf)->nkpts; i++)
    {
      lbl = g_strdup_printf(_("kpt %d (%3.3f,%3.3f,%3.3f - %3.3f)"), i + 1,
                            BIGDFT_ORBS(wf)->kpts[i * 3 + 0],
                            BIGDFT_ORBS(wf)->kpts[i * 3 + 1],
                            BIGDFT_ORBS(wf)->kpts[i * 3 + 2],
                            BIGDFT_ORBS(wf)->kwgts[i]);
      gtk_list_store_append(orbsd_kpt, &iter);
      gtk_list_store_set(orbsd_kpt, &iter, LABEL, lbl, ACTIVATED, FALSE, VALUE, i, -1);
      g_free(lbl);
    }
  for (i = 0; i < BIGDFT_ORBS(wf)->norbu; i++)
    {
      lbl = g_strdup_printf(_("orb. %d"), BIGDFT_ORBS(wf)->norbu - i);
      gtk_list_store_append(orbsd_spu, &iter);
      gtk_list_store_set(orbsd_spu, &iter, LABEL, lbl, ACTIVATED, FALSE,
                         VALUE, BIGDFT_ORBS(wf)->norbu - i - 1, -1);
      g_free(lbl);
    }
  for (i = 0; i < BIGDFT_ORBS(wf)->norbd; i++)
    {
      lbl = g_strdup_printf(_("orb. %d"), BIGDFT_ORBS(wf)->norbd - i);
      gtk_list_store_append(orbsd_spd, &iter);
      gtk_list_store_set(orbsd_spd, &iter, LABEL, lbl, ACTIVATED, FALSE,
                         VALUE, BIGDFT_ORBS(wf)->norbd - i - 1, -1);
      g_free(lbl);
    }
  if (cbKpt)
    gtk_combo_box_set_active(GTK_COMBO_BOX(cbKpt), 0);
  dt = g_malloc0(sizeof(StoredWfData) * BIGDFT_ORBS(wf)->norb * BIGDFT_ORBS(wf)->nkpts);
  g_object_set_data_full(G_OBJECT(dataObj), "BigDFT_storedWf", dt, (GDestroyNotify)g_free);
}

static void onCheckDens(GtkToggleButton *bt _U_, gpointer data _U_)
{
}
static void onCheckVext(GtkToggleButton *bt _U_, gpointer data _U_)
{
}
