/*   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_object.h>
#include <visu_configFile.h>
#include <visu_dataloader.h>
#include <visu_dataatomic.h>
#include <coreTools/toolMatrix.h>
#include <coreTools/toolPhysic.h>

#include <extraFunctions/scalarFields.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 <unistd.h>
#include <fcntl.h>

#include <support.h>
#include <extraGtkFunctions/gtk_fieldChooser.h>

#include "bigdftplug.h"

#define BIGDFT_DESCRIPTION _("<span size=\"smaller\">"			\
			   "This plug-in adds support for specific\n"   \
                           "capabilities of BigDFT.</span>")
#define BIGDFT_AUTHORS     "Caliste Damien"

#define FLAG_PARAMETER_BIGDFT "bigdft_use_parser"
#define DESC_PARAMETER_BIGDFT "Use or not the BigDFT parser for known files ; a boolean (0 or 1)"

static gboolean bigdftDensityLoad(VisuScalarFieldMethod *meth, const gchar *filename,
                                  GList **fieldList, GError **error);
static gboolean bigdftTestWaveFile(const gchar *filename);

extern void FC_FUNC_(memocc_report, MEMOCC_REPORT)();


/* Local variables */
static gchar *iconPath;
static VisuScalarFieldMethod *fmtBigDFT;
static VisuDataLoader *wvlLoader = NULL;
static gboolean isPanelInitialised;
static gboolean useCGrid, useFGrid, useLoadWithGrid;
static GtkWidget *lblCGrid, *lblFGrid;
static GtkTextBuffer *bufMemory;
static GtkWidget *entryHgrid[3], *entryCrmult, *entryFrmult, *entryIxc;
static GtkWidget *entryRadius[2], *cbRadEle[2];
enum
  {
    RADII_VALUE_FILE,
    RADII_VALUE_COMPUTED,
    RADII_VALUE_EDITED
  };
static gchar *radiiIcons[3] = {GTK_STOCK_FILE, GTK_STOCK_EXECUTE, GTK_STOCK_EDIT};
static gboolean disableCallbacks;

/* Local methods. */
static void updateGridLabels(VisuData *dataObj);
static void initialisePanel(VisuUiPanel *panel);
static gboolean loadBigDFTIn(VisuDataLoader *self, const gchar* filename,
                             VisuData *data, guint nSet,
                             GCancellable *cancel, GError **error);
static void update_memory_estimation(VisuData *data);
static int redirect_init(int out_pipe[2]);
static void redirect_dump(int out_pipe[2], int stdout_fileno_old);
static void exportParameters(GString *data, VisuData *dataObj);

/* Callbacks. */
static gboolean onFieldChooserHook(GSignalInvocationHint *ihint, guint nvalues,
                                   const GValue *param_values, gpointer data);
static void onBand(GtkSpinButton *spin, gpointer data);
static void onKpt(GtkSpinButton *spin, gpointer data);
static void onSpin(GtkComboBox *combo, gpointer data);
static void onSpinor(GtkComboBox *combo, gpointer data);
static void onDataNew(GObject *obj, VisuData *dataObj, gpointer data);
static void onDataReady(GObject *obj, VisuData *dataObj, gpointer data);
static void onFilesChanged(VisuData *dataObj, guint kind, gpointer data);
static void onPanelEnter(VisuUiPanel *panel, gpointer data);
static void onToggleGrid(GtkToggleButton *toggle, gpointer data);
static void onToggleLoad(GtkToggleButton *toggle, gpointer data);
static void onHgrid(VisuUiNumericalEntry *entry, gdouble old_value, gpointer data);
static void onRmult(VisuUiNumericalEntry *entry, gdouble old_value, gpointer data);
static void onRadius(VisuUiNumericalEntry *entry, gdouble old_value, gpointer data);
static void onIxc(VisuUiNumericalEntry *entry, gdouble old_value, gpointer data);
static void onEleChanged(GtkComboBox *combo, gpointer data);
static void onNproc(GtkSpinButton *spin, gpointer data);

/* Includes. */
GtkWidget* bdft_run_tab(guint nEle, BigDFT_Wf *wf);
void bdft_run_init();
GtkWidget* bdft_system_tab(BigDFT_Lzd *lzd);
void bdft_system_init();

gboolean bigdftplugInit()
{
  const gchar *typeBigDFT[] = {"*.etsf", "wavefunction-*", "minBasis-*", (char*)0};
  const gchar *typeWvl[] = {"*.xyz", "*.ascii", "*.yaml", (char*)0};
  VisuConfigFileEntry *resourceEntry;

  fmtBigDFT = visu_scalar_field_method_new(_("Wavefunction file from BigDFT"), typeBigDFT,
                                           bigdftDensityLoad, G_PRIORITY_HIGH - 4);
  tool_file_format_setValidator(TOOL_FILE_FORMAT(fmtBigDFT), bigdftTestWaveFile);

  wvlLoader = visu_data_loader_new(_("BigDFT wavelet boxes"), typeWvl,
                                    FALSE, loadBigDFTIn, 22);
  visu_data_atomic_class_addLoader(wvlLoader);
  tool_file_format_addPropertyDouble(fmtWvl, "hx", _("Hgrid along x"), 0.45);
  tool_file_format_addPropertyDouble(fmtWvl, "hy", _("Hgrid along y"), 0.45);
  tool_file_format_addPropertyDouble(fmtWvl, "hz", _("Hgrid along z"), 0.45);
  tool_file_format_addPropertyDouble(fmtWvl, "crmult", _("Coarse grid multiplier"), 5.);
  tool_file_format_addPropertyDouble(fmtWvl, "frmult", _("Fine grid multiplier"), 8.);
  tool_file_format_addPropertyInt(fmtWvl, "ixc", _("Exchange-correlation functional code"), 1);
  tool_file_format_addPropertyInt(fmtWvl, "nproc", _("Number of processors for memory estimation"), 1);

  iconPath = g_build_filename(V_SIM_PIXMAPS_DIR, "bigdft.png", NULL);

  useCGrid = FALSE;
  useFGrid = FALSE;
  useLoadWithGrid = FALSE;
  disableCallbacks = FALSE;

  lblCGrid = (GtkWidget*)0;
  lblFGrid = (GtkWidget*)0;
  entryHgrid[0] = (GtkWidget*)0;
  entryHgrid[1] = (GtkWidget*)0;
  entryHgrid[2] = (GtkWidget*)0;
  entryCrmult = (GtkWidget*)0;
  entryFrmult = (GtkWidget*)0;
  entryRadius[0] = (GtkWidget*)0;
  entryRadius[1] = (GtkWidget*)0;
  cbRadEle[0] = (GtkWidget*)0;
  cbRadEle[1] = (GtkWidget*)0;
  entryIxc = (GtkWidget*)0;
  bufMemory = gtk_text_buffer_new((GtkTextTagTable*)0);
  gtk_text_buffer_create_tag(bufMemory, "typewriter", "family", "monospace",
                             "scale", PANGO_SCALE_SMALL, NULL);
  isPanelInitialised = FALSE;

  g_signal_connect(VISU_OBJECT_INSTANCE, "dataNew",
                   G_CALLBACK(onDataNew), NULL);
  g_signal_connect(VISU_OBJECT_INSTANCE, "dataRendered",
                   G_CALLBACK(onDataReady), NULL);
  bdft_run_init();
  bdft_system_init();

  visu_config_file_addKnownTag("bigdft");
  resourceEntry = visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_PARAMETER,
                                                   FLAG_PARAMETER_BIGDFT,
                                                   DESC_PARAMETER_BIGDFT,
                                                   &useLoadWithGrid, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.7f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
                                     exportParameters);

  return TRUE;
}
gboolean bigdftplugInitGtk()
{
  DBG_fprintf(stderr, "BigDFT: init Gtk stuff.\n");
  
  panelBigDFT = visu_ui_panel_newWithIconFromPath("Panel_BigDFT", _("BigDFT settings"),
                                              "BigDFT", "stock-bigdft_20.png");
  visu_ui_panel_setDockable(VISU_UI_PANEL(panelBigDFT), TRUE);
  visu_ui_panel_attach(VISU_UI_PANEL(panelBigDFT), visu_ui_panel_class_getCommandPanel());
  g_signal_connect(G_OBJECT(panelBigDFT), "page-entered",
		   G_CALLBACK(onPanelEnter), (gpointer)0);

  g_signal_add_emission_hook(g_signal_lookup("validate", VISU_TYPE_UI_FIELD_CHOOSER),
                             0, onFieldChooserHook, (gpointer)0, (GDestroyNotify)0);

  return TRUE;
}

const char* bigdftplugGet_description()
{
  return BIGDFT_DESCRIPTION;
}

const char* bigdftplugGet_authors()
{
  return BIGDFT_AUTHORS;
}

const char* bigdftplugGet_icon()
{
  return iconPath;
}

static gboolean bigdftTestWaveFile(const gchar *filename)
{
  g_return_val_if_fail(filename, FALSE);

  return bigdft_read_wave_descr(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
}

static void initialisePanel(VisuUiPanel *panel)
{
  GtkWidget *vbox, *hbox, *wd, *ct, *vbox2, *ntbk;
  VisuData *dataObj;
  GArray *radii;
  guint *radiiStatus;
  guint nEle;
  BigDFT_Wf *wf;

  g_return_if_fail(!isPanelInitialised);

  dataObj = visu_ui_panel_getData(panel);
  if (dataObj)
    {
      radii = (GArray*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radii");
      radiiStatus = (guint*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radiiStatus");
      nEle = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dataObj), "BigDFT_ntypes"));
      wf = (BigDFT_Wf*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_wf");
    }
  else
    {
      radii = (GArray*)0;
      radiiStatus = (guint*)0;
      nEle = 0;
      wf = (BigDFT_Wf*)0;
    }

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(panel), vbox);

  wd = gtk_label_new(_("<b>BigDFT specific parameters</b>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_misc_set_alignment(GTK_MISC(wd), 0., 0.5);
  gtk_widget_set_name(wd, "label_head");
  gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);

  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
  wd = gtk_check_button_new_with_mnemonic(_("use BigDFT to load xyz and ASCII files"));
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wd), useLoadWithGrid);
  g_signal_connect(G_OBJECT(wd), "toggled",
                   G_CALLBACK(onToggleLoad), GINT_TO_POINTER(0));
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);

  /* Hgrid and ixc line. */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("hgrids:"));
  gtk_misc_set_alignment(GTK_MISC(wd), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  entryHgrid[0] = visu_ui_numerical_entry_new(GET_OPT_DBL(fmtWvl, "hx"));
  gtk_entry_set_width_chars(GTK_ENTRY(entryHgrid[0]), 8);
  g_signal_connect(G_OBJECT(entryHgrid[0]), "value-changed",
		   G_CALLBACK(onHgrid), GINT_TO_POINTER(0));
  gtk_box_pack_start(GTK_BOX(hbox), entryHgrid[0], FALSE, FALSE, 0);
  entryHgrid[1] = visu_ui_numerical_entry_new(GET_OPT_DBL(fmtWvl, "hy"));
  gtk_entry_set_width_chars(GTK_ENTRY(entryHgrid[1]), 8);
  g_signal_connect(G_OBJECT(entryHgrid[1]), "value-changed",
		   G_CALLBACK(onHgrid), GINT_TO_POINTER(1));
  gtk_box_pack_start(GTK_BOX(hbox), entryHgrid[1], FALSE, FALSE, 0);
  entryHgrid[2] = visu_ui_numerical_entry_new(GET_OPT_DBL(fmtWvl, "hz"));
  gtk_entry_set_width_chars(GTK_ENTRY(entryHgrid[2]), 8);
  g_signal_connect(G_OBJECT(entryHgrid[2]), "value-changed",
		   G_CALLBACK(onHgrid), GINT_TO_POINTER(2));
  gtk_box_pack_start(GTK_BOX(hbox), entryHgrid[2], FALSE, FALSE, 0);
  wd = gtk_label_new(_("ixc:"));
  gtk_misc_set_alignment(GTK_MISC(wd), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  entryIxc = visu_ui_numerical_entry_new(GET_OPT_INT(fmtWvl, "ixc"));
  gtk_entry_set_width_chars(GTK_ENTRY(entryIxc), 6);
  g_signal_connect(G_OBJECT(entryIxc), "value-changed",
		   G_CALLBACK(onIxc), (gpointer)0);
  gtk_box_pack_start(GTK_BOX(hbox), entryIxc, FALSE, FALSE, 0);

  /* The coarse grid parameters. */
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  wd = gtk_check_button_new_with_mnemonic(_("show _coarse grid"));
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wd), useCGrid);
  g_signal_connect(G_OBJECT(wd), "toggled",
                   G_CALLBACK(onToggleGrid), GINT_TO_POINTER(0));
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  lblCGrid = gtk_label_new("");
  gtk_box_pack_end(GTK_BOX(hbox), lblCGrid, FALSE, FALSE, 0);
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("radius of ele."));
  gtk_misc_set_alignment(GTK_MISC(wd), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  cbRadEle[0] = visu_ui_element_combobox_new(FALSE, FALSE, (const gchar*)0);
  g_signal_connect(G_OBJECT(cbRadEle[0]), "changed",
                   G_CALLBACK(onEleChanged), GINT_TO_POINTER(0));
  gtk_box_pack_start(GTK_BOX(hbox), cbRadEle[0], FALSE, FALSE, 0);
  if (radii && radiiStatus)
    {
      entryRadius[0] = visu_ui_numerical_entry_new(g_array_index(radii, double, 0));
      gtk_entry_set_icon_from_stock(GTK_ENTRY(entryRadius[0]), GTK_ENTRY_ICON_SECONDARY,
                                    radiiIcons[radiiStatus[0]]);
    }
  else
    entryRadius[0] = visu_ui_numerical_entry_new(-1.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryRadius[0]), 10);
  g_signal_connect(G_OBJECT(entryRadius[0]), "value-changed",
		   G_CALLBACK(onRadius), GINT_TO_POINTER(0));
  gtk_box_pack_start(GTK_BOX(hbox), entryRadius[0], FALSE, FALSE, 0);
  wd = gtk_label_new("\303\227 (crmult:");
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  entryCrmult = visu_ui_numerical_entry_new(GET_OPT_DBL(fmtWvl, "crmult"));
  gtk_entry_set_width_chars(GTK_ENTRY(entryCrmult), 3);
  g_signal_connect(G_OBJECT(entryCrmult), "value-changed",
		   G_CALLBACK(onRmult), GINT_TO_POINTER(0));
  gtk_box_pack_start(GTK_BOX(hbox), entryCrmult, FALSE, FALSE, 0);
  wd = gtk_label_new(")");
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  /* The fine grid parameters. */
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  wd = gtk_check_button_new_with_mnemonic(_("show _fine grid"));
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wd), useFGrid);
  g_signal_connect(G_OBJECT(wd), "toggled",
                   G_CALLBACK(onToggleGrid), GINT_TO_POINTER(1));
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  lblFGrid = gtk_label_new("");
  gtk_box_pack_end(GTK_BOX(hbox), lblFGrid, FALSE, FALSE, 0);
  updateGridLabels(visu_ui_panel_getData(panel));
  hbox = gtk_hbox_new(FALSE, 5);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("radius of ele."));
  gtk_misc_set_alignment(GTK_MISC(wd), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  cbRadEle[1] = visu_ui_element_combobox_new(FALSE, FALSE, (const gchar*)0);
  g_signal_connect(G_OBJECT(cbRadEle[1]), "changed",
                   G_CALLBACK(onEleChanged), GINT_TO_POINTER(1));
  gtk_box_pack_start(GTK_BOX(hbox), cbRadEle[1], FALSE, FALSE, 0);
  if (radii && radiiStatus)
    {
      entryRadius[1] = visu_ui_numerical_entry_new(g_array_index(radii, double, nEle));
      gtk_entry_set_icon_from_stock(GTK_ENTRY(entryRadius[1]), GTK_ENTRY_ICON_SECONDARY,
                                    radiiIcons[radiiStatus[nEle]]);
    }
  else
    entryRadius[1] = visu_ui_numerical_entry_new(-1.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryRadius[1]), 10);
  g_signal_connect(G_OBJECT(entryRadius[1]), "value-changed",
		   G_CALLBACK(onRadius), GINT_TO_POINTER(1));
  gtk_box_pack_start(GTK_BOX(hbox), entryRadius[1], FALSE, FALSE, 0);
  wd = gtk_label_new("\303\227 (frmult:");
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  entryFrmult = visu_ui_numerical_entry_new(GET_OPT_DBL(fmtWvl, "frmult"));
  gtk_entry_set_width_chars(GTK_ENTRY(entryFrmult), 3);
  g_signal_connect(G_OBJECT(entryFrmult), "value-changed",
		   G_CALLBACK(onRmult), GINT_TO_POINTER(1));
  gtk_box_pack_start(GTK_BOX(hbox), entryFrmult, FALSE, FALSE, 0);
  wd = gtk_label_new(")");
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  wd = gtk_label_new("<span size=\"smaller\">Powered by BigDFT "
                     BIGDFT_STRING_VERSION "</span>");
  gtk_misc_set_alignment(GTK_MISC(wd), 1., 0.5);
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_box_pack_end(GTK_BOX(vbox), wd, FALSE, FALSE, 0);

  /* The BigDFT tab. */
  ntbk = gtk_notebook_new();
  gtk_box_pack_start(GTK_BOX(vbox), ntbk, TRUE, TRUE, 0);

  /* The memory estimation. */
  vbox2 = gtk_vbox_new(FALSE, 2);
  gtk_notebook_append_page(GTK_NOTEBOOK(ntbk), vbox2,
                           gtk_label_new(_("Memory estimation")));
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  wd = gtk_label_new(_("Memory estimation for"));
  gtk_misc_set_alignment(GTK_MISC(wd), 0., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  wd = gtk_spin_button_new_with_range(1, 4096, 1);
  g_signal_connect(G_OBJECT(wd), "value-changed",
		   G_CALLBACK(onNproc), (gpointer)0);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  wd = gtk_label_new(_("core(s)"));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  ct = gtk_scrolled_window_new((GtkAdjustment*)0, (GtkAdjustment*)0);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ct),
                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_NEVER);
  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_text_view_new_with_buffer(bufMemory);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(wd), FALSE);
  gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(wd), FALSE);
  gtk_container_add(GTK_CONTAINER(ct), wd);

  /* The system tab. */
  vbox2 = bdft_system_tab((wf)?wf->lzd:NULL);
  gtk_notebook_append_page(GTK_NOTEBOOK(ntbk), vbox2,
                           gtk_label_new(_("Basis set")));

  /* The run tab. */
  vbox2 = bdft_run_tab(nEle, wf);
  gtk_notebook_append_page(GTK_NOTEBOOK(ntbk), vbox2,
                           gtk_label_new(_("Running BigDFT")));

  gtk_widget_show_all(vbox);

  isPanelInitialised = TRUE;
}
static void onPanelEnter(VisuUiPanel *panel, gpointer data _U_)
{
  DBG_fprintf(stderr, "Panel BigDFT: caught the 'page-entered' signal %d.\n",
	      isPanelInitialised);
  if (!isPanelInitialised)
    initialisePanel(panel);
}
static gboolean loadGrids(gpointer data)
{
  VisuData *dataObj;
  VisuGlView *view;
  GArray *grid;
  const gchar *gridNames[] = {"BigDFT_coarse_grid", "BigDFT_fine_grid"};
  const gchar *eleNames[] = {"g", "G"};
  guint iGrid = GPOINTER_TO_INT(data);
  gboolean* usage[] = {&useCGrid, &useFGrid};

  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT));
  view = visu_ui_panel_getView(VISU_UI_PANEL(panelBigDFT));
  if (!dataObj || !view)
    return FALSE;
  DBG_fprintf(stderr, "Panel BigDFT: load grid for object %p.\n", (gpointer)dataObj);

  grid = (GArray*)g_object_get_data(G_OBJECT(dataObj), gridNames[iGrid]);

  bigdft_show_grid(dataObj, (*(usage[iGrid]))?grid:(GArray*)0, eleNames[iGrid], 0);

  VISU_REDRAW_ADD;

  return FALSE;
}
static void onToggleGrid(GtkToggleButton *toggle, gpointer data)
{
  gboolean* usage[] = {&useCGrid, &useFGrid};
  guint iGrid = GPOINTER_TO_INT(data);

  DBG_fprintf(stderr, "Panel BigDFT: toggle grid.\n");
  *(usage[iGrid]) = gtk_toggle_button_get_active(toggle);

  g_idle_add(loadGrids, data);
}

static gboolean bigdftDensityLoad(VisuScalarFieldMethod *meth, const gchar *filename,
                                  GList **fieldList, GError **error)
{
  int norbu, norbd, nkpt, nspinor, iorbp;
  int iorbf, ispinf, ikptf, ispinorf;
  int ntot, i, iorb, ispin, ikpt, ispinor;
  guint n[3];
  VisuScalarField *field;
  ToolOption *opt;
  double h[3];
  double box[3][3];
  double *data;
  f90_pointer_double_4D *psiscf;
  GArray *wrap;
  VisuBox *boxObj;

  g_return_val_if_fail(filename, FALSE);
  g_return_val_if_fail(*fieldList == (GList*)0, FALSE);
  g_return_val_if_fail(error && (*error == (GError*)0), FALSE);

  DBG_fprintf(stderr, "BigDFT: test file '%s' as a BigDFT wavefunction file.\n", filename);
  if (!bigdft_read_wave_descr(filename, &norbu, &norbd, &nkpt, &nspinor,
                              &iorbf, &ispinf, &ikptf, &ispinorf))
    /* The file is not a BigDFT file. */
    return FALSE;

  /* Get orbital from option table if the file contains several. */
  if (norbu > 0 || norbd > 0)
    {
      iorb = norbu;
      ispin = 1;
      ikpt = 1;
      ispinorf = 0;
      opt = tool_file_format_getPropertyByName(TOOL_FILE_FORMAT(meth), "i-band");
      if (opt)
        iorb = g_value_get_int(tool_option_getValue(opt));
      opt = tool_file_format_getPropertyByName(TOOL_FILE_FORMAT(meth), "i-spin");
      if (opt)
        ispin = g_value_get_int(tool_option_getValue(opt));
      opt = tool_file_format_getPropertyByName(TOOL_FILE_FORMAT(meth), "i-kpt");
      if (opt)
        ikpt = g_value_get_int(tool_option_getValue(opt));
      ispin   = CLAMP(ispin, 1, (norbd > 0)?2:1);
      iorb    = CLAMP(iorb, 1, (ispin == 1)?norbu:norbd);
      ikpt    = CLAMP(ikpt, 1, nkpt);
    }
  else
    {
      iorb = iorbf;
      ispin = ispinf;
      ikpt = ikptf;
    }
  /* Build the iorbp value from this. */
  iorbp = (ikpt - 1) * (norbu + norbd) + (ispin - 1) * norbu + iorb;
  /* Special treatment for i-complex. */
  opt = tool_file_format_getPropertyByName(TOOL_FILE_FORMAT(meth), "i-complex");
  if (opt)
    ispinor = g_value_get_int(tool_option_getValue(opt));
  else
    ispinor = ispinorf;
  ispinor = CLAMP(ispinor, 0, nspinor);
  
  /* We read the wavefunction. */
  psiscf = bigdft_read_wave_to_isf(filename, iorbp, h, (int*)n, &nspinor);
  if (!psiscf)
    {
      g_set_error(error, TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FORMAT,
                  _("Can't read wavefunction %d from file '%s'."), iorbp, filename); 
      return TRUE;
    }
  /* Choose the real, imag or partial density values. */
  ntot = n[0] * n[1] * n[2];
  if (ispinor == 0)
    {
      data = g_malloc(sizeof(double) * ntot);
      for (i = 0; i < ntot; i++)
        data[i] = psiscf->data[i] * psiscf->data[i];
      if (nspinor == 2)
        for (i = 0; i < ntot; i++)
          data[i] += psiscf->data[i + ntot] * psiscf->data[i + ntot];
    }
  else if (ispinor == 2 && nspinor == 2)
    data = psiscf->data + ntot;
  else
    data = psiscf->data;

  /* Create the scalar field. */
  field = visu_scalar_field_new(filename);
  if (!field)
    g_warning("impossible to create a VisuScalarField object.");
  else
    {
      memset(box, 0, sizeof(double) * 9);
      box[0][0] = h[0] * n[0];
      box[1][1] = h[1] * n[1];
      box[2][2] = h[2] * n[2];
      boxObj = visu_box_new_full(box, VISU_BOX_PERIODIC);
      visu_boxed_setBox(VISU_BOXED(field), VISU_BOXED(boxObj));
      g_object_unref(boxObj);
      visu_scalar_field_setGridSize(field, n);
      DBG_fprintf(stderr, "BigDFT: transfer density into field object.\n");
      wrap = g_array_new(FALSE, FALSE, sizeof(double));
      wrap->data = (gchar*)data;
      wrap->len = ntot;
      visu_scalar_field_setData(field, wrap, TRUE);
      g_array_free(wrap, FALSE);
      /* Add options to the field. */
      opt = tool_option_new("i-band", _("orbital id"), G_TYPE_INT);
      g_value_set_int(tool_option_getValue(opt), iorb);
      visu_scalar_field_addOption(field, opt);
      opt = tool_option_new("i-spin", _("spin id"), G_TYPE_STRING);
      if (ispin == 2)
        g_value_set_string(tool_option_getValue(opt), "down");
      else if (ispin == 1 && norbd > 0)
        g_value_set_string(tool_option_getValue(opt), "up");
      else
        g_value_set_string(tool_option_getValue(opt), "none");
      visu_scalar_field_addOption(field, opt);
      opt = tool_option_new("i-kpt", _("k-point id"), G_TYPE_INT);
      g_value_set_int(tool_option_getValue(opt), ikpt);
      visu_scalar_field_addOption(field, opt);
      opt = tool_option_new("i-complex", _("real/imag or partial density"), G_TYPE_STRING);
      if (ispinor == 2)
        g_value_set_string(tool_option_getValue(opt), "imag");
      else if (ispinor == 1)
        g_value_set_string(tool_option_getValue(opt), "real");
      else
        g_value_set_string(tool_option_getValue(opt), "partial density");
      visu_scalar_field_addOption(field, opt);
      *fieldList = g_list_append(*fieldList, (gpointer)field);
    }

  if (ispinor == 0)
    g_free(data);

  bigdft_free_wave_to_isf(psiscf);

  *error = (GError*)0;
  return TRUE;
}

static gboolean onFieldChooserHook(GSignalInvocationHint *ihint _U_, guint nvalues _U_,
                                   const GValue *param_values, gpointer data _U_)
{
  GtkWidget *hbox, *lbl, *sp, *cb;
  int norbu, norbd, nkpt, nspinor;
  int iorbf, ispinf, ikptf, ispinorf;
  gchar *filename;

  if (g_value_get_object(param_values + 1) != G_OBJECT(fmtBigDFT))
    return FALSE;
  DBG_fprintf(stderr, "BigDFT: hook on wavefunction selection.\n");
  filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(g_value_get_object(param_values)));
  if (!bigdft_read_wave_descr(filename, &norbu, &norbd, &nkpt, &nspinor,
                              &iorbf, &ispinf, &ikptf, &ispinorf))
    {
      g_warning("Can't read file '%s'.", filename);
      g_free(filename);
      return TRUE;
    }
  g_free(filename);

  /* Build the widgets. */
  hbox = gtk_hbox_new(FALSE, 0);
  lbl = gtk_label_new(_("Choose orbital:"));
  gtk_misc_set_alignment(GTK_MISC(lbl), 0., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), lbl, TRUE, TRUE, 0);

  lbl = gtk_label_new(_("band id:"));
  gtk_box_pack_start(GTK_BOX(hbox), lbl, TRUE, TRUE, 0);
  gtk_misc_set_alignment(GTK_MISC(lbl), 1., 0.5);
  if (norbu > 0)
    sp = gtk_spin_button_new_with_range(1, norbu, 1);
  else
    sp = gtk_spin_button_new_with_range(iorbf, iorbf, 1);
  g_signal_connect(G_OBJECT(sp), "value-changed",
                   G_CALLBACK(onBand), (gpointer)0);
  gtk_box_pack_start(GTK_BOX(hbox), sp, FALSE, FALSE, 0);

  lbl = gtk_label_new(_("spin:"));
  gtk_misc_set_alignment(GTK_MISC(lbl), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), lbl, TRUE, TRUE, 0);
  cb = gtk_combo_box_text_new();
  if ((norbu > 0 || norbd > 0) && norbu == norbd)
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cb), _("none"));
  else
    {
      gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cb), _("up"));
      gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cb), _("down"));
    }
  gtk_combo_box_set_active(GTK_COMBO_BOX(cb), ispinf - 1);
  g_signal_connect(G_OBJECT(cb), "changed",
                   G_CALLBACK(onSpin), (gpointer)sp);
  g_object_set_data(G_OBJECT(cb), "norbu", GINT_TO_POINTER(norbu));
  g_object_set_data(G_OBJECT(cb), "norbd", GINT_TO_POINTER(norbd));
  gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0);

  lbl = gtk_label_new(_("k-point:"));
  gtk_misc_set_alignment(GTK_MISC(lbl), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), lbl, TRUE, TRUE, 0);
  if (nkpt > 0)
    sp = gtk_spin_button_new_with_range(1, nkpt, 1);
  else
    sp = gtk_spin_button_new_with_range(ikptf, ikptf, 1);
  g_signal_connect(G_OBJECT(sp), "value-changed",
                   G_CALLBACK(onKpt), (gpointer)0);
  gtk_box_pack_start(GTK_BOX(hbox), sp, FALSE, FALSE, 0);

  lbl = gtk_label_new(_("representation:"));
  gtk_misc_set_alignment(GTK_MISC(lbl), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), lbl, TRUE, TRUE, 0);
  cb = gtk_combo_box_text_new();
  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cb), _("partial density"));
  gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cb), _("real part"));
  if (nspinor > 1)
    gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cb), _("imaginary part"));
  g_signal_connect(G_OBJECT(cb), "changed",
                   G_CALLBACK(onSpinor), (gpointer)0);
  gtk_combo_box_set_active(GTK_COMBO_BOX(cb), ispinorf);
  gtk_box_pack_start(GTK_BOX(hbox), cb, FALSE, FALSE, 0);

  visu_ui_field_chooser_setOptions(VISU_UI_FIELD_CHOOSER(g_value_get_object(param_values)),
                                   hbox);

  return TRUE;
}
static void onBand(GtkSpinButton *spin, gpointer data _U_)
{
  tool_file_format_addPropertyInt(TOOL_FILE_FORMAT(fmtBigDFT), "i-band",
                                  _("orbital id"), (int)gtk_spin_button_get_value(spin));
}
static void onKpt(GtkSpinButton *spin, gpointer data _U_)
{
  tool_file_format_addPropertyInt(TOOL_FILE_FORMAT(fmtBigDFT), "i-kpt",
                                  _("k-point id"), (int)gtk_spin_button_get_value(spin));
}
static void onSpin(GtkComboBox *combo, gpointer data)
{
  gint ispin;

  ispin = gtk_combo_box_get_active(combo) + 1;
  tool_file_format_addPropertyInt(TOOL_FILE_FORMAT(fmtBigDFT), "i-spin",
                                  _("spin id"), ispin);
  
  if (ispin == 1)
    gtk_spin_button_set_range(GTK_SPIN_BUTTON(data), 1,
                              GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo), "norbu")));
  else
    gtk_spin_button_set_range(GTK_SPIN_BUTTON(data), 1,
                              GPOINTER_TO_INT(g_object_get_data(G_OBJECT(combo), "norbd")));
}
static void onSpinor(GtkComboBox *combo, gpointer data _U_)
{
  tool_file_format_addPropertyInt(TOOL_FILE_FORMAT(fmtBigDFT), "i-complex",
                                  _("real/imag or partial density"),
                                  gtk_combo_box_get_active(combo));
}

static void onDataNew(GObject *obj _U_, VisuData *dataObj, gpointer data _U_)
{
  g_signal_connect(G_OBJECT(dataObj), "FilesChanged",
                   G_CALLBACK(onFilesChanged), (gpointer)0);
}
static void onDataReady(GObject *obj _U_, VisuData *dataObj, gpointer data _U_)
{
  GArray *radii;
  guint *radiiStatus;
  guint nEle;
  gint iEle;
  BigDFT_Wf *wf;
  BigDFT_Inputs *in;

  DBG_fprintf(stderr, "Panel BigDFT: caught 'dataRendered' signal.\n");
  if (isPanelInitialised)
    {
      updateGridLabels(dataObj);

      if (dataObj)
        {
          radii = (GArray*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radii");
          radiiStatus = (guint*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radiiStatus");
        }
      else
        {
          radii = (GArray*)0;
          radiiStatus = (guint*)0;
        }
      disableCallbacks = TRUE;
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryHgrid[0]), GET_OPT_DBL(fmtWvl, "hx"));
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryHgrid[1]), GET_OPT_DBL(fmtWvl, "hy"));
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryHgrid[2]), GET_OPT_DBL(fmtWvl, "hz"));
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryCrmult), GET_OPT_DBL(fmtWvl, "crmult"));
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryFrmult), GET_OPT_DBL(fmtWvl, "frmult"));
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryIxc), GET_OPT_INT(fmtWvl, "ixc"));
      if (radii && radiiStatus)
        {
          nEle = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dataObj), "BigDFT_ntypes"));
          DBG_fprintf(stderr, "Panel BigDFT: setup radius combo for %d elements.\n", nEle);
          iEle = gtk_combo_box_get_active(GTK_COMBO_BOX(cbRadEle[0]));
          DBG_fprintf(stderr, " | %d element (coarse).\n", iEle);
          if (iEle >= 0)
            {
              visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryRadius[0]),
                                               g_array_index(radii, double, iEle));
              gtk_entry_set_icon_from_stock(GTK_ENTRY(entryRadius[0]),
                                            GTK_ENTRY_ICON_SECONDARY,
                                            radiiIcons[radiiStatus[iEle]]);
            }
          iEle = gtk_combo_box_get_active(GTK_COMBO_BOX(cbRadEle[1]));
          DBG_fprintf(stderr, " | %d element (fine).\n", iEle);
          if (iEle >= 0)
            {
              visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryRadius[1]),
                                               g_array_index(radii, double, nEle + iEle));
              gtk_entry_set_icon_from_stock(GTK_ENTRY(entryRadius[1]),
                                            GTK_ENTRY_ICON_SECONDARY,
                                            radiiIcons[radiiStatus[nEle + iEle]]);
            }
        }
      disableCallbacks = FALSE;
    }

  if (dataObj)
    {
      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 (wf && in)
        {
          /* We create the orbital descriptors here already for memory
             estimation or later run. */
          DBG_fprintf(stderr, "Panel BigDFT: set-up Lzd.\n");
          bigdft_wf_define(wf, in, 0, 1);

          /* We do the memory estimation for this run. */
          DBG_fprintf(stderr, "Panel BigDFT: do memory estimation.\n");
          update_memory_estimation(dataObj);
        }
    }

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

static void updateGridLabels(VisuData *dataObj)
{
  GArray *grid;
  guint cgrid, fgrid;
  gchar *cgridText, *fgridText;

  DBG_fprintf(stderr, "Panel BigDFT: update grid labels.\n");
  cgrid = 0;
  fgrid = 0;
  if (dataObj)
    {
      grid = (GArray*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_coarse_grid");
      cgrid = (grid)?grid->len:0;
      grid = (GArray*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_fine_grid");
      fgrid = (grid)?grid->len:0;
    }
  
  if (cgrid)
    cgridText = g_strdup_printf(_("%d grid points"), cgrid);
  else
    cgridText = g_strdup(_("no grid"));
  gtk_label_set_text(GTK_LABEL(lblCGrid), cgridText);
  g_free(cgridText);

  if (fgrid)
    fgridText = g_strdup_printf(_("%d grid points"), fgrid);
  else
    fgridText = g_strdup(_("no grid"));
  gtk_label_set_text(GTK_LABEL(lblFGrid), fgridText);
  g_free(fgridText);
}
/* Grid related routines. */
void bigdft_show_grid(VisuData *dataObj, GArray *grid, const gchar *gG, guint id)
{
  VisuElement *ele;
  guint i;
  GArray *eleArr, *nEleArr;
  gboolean newEle;
  gchar *lbl;
  float rgba_y[4] = {1.000, 0.750, 0.040, 1.000};
  float rgba_b[4] = {0.600, 0.600, 1.000, 1.000};

  /* We check that gG is not currently used by dataObj. */
  lbl = (id)?g_strdup_printf("%%%s_%d", gG, id):g_strdup(gG);
  DBG_fprintf(stderr, "BigDFT: show/hide grid '%s' with %d elements on object %p.\n",
              lbl, (grid)?grid->len:0, (gpointer)dataObj);
  ele = visu_element_retrieveFromName(lbl, &newEle);
  g_free(lbl);
  if (newEle)
    {
      visu_rendering_atomic_setRadius(ele, (gG[0] == 'g')?0.5:1.);
      visu_rendering_atomic_setShape(ele, VISU_RENDERING_ATOMIC_POINT);
      if (!strcmp(gG, "g"))
        visu_element_setAllRGBValues(ele, rgba_y);
      else if (!strcmp(gG, "G"))
        visu_element_setAllRGBValues(ele, rgba_b);
      else
        visu_element_setAllRGBValues(ele, (float*)tool_color_new_bright(id - 1)->rgba);
    }

  /* Remove possible old gG nodes. */
  visu_node_array_removeNodesOfElement(VISU_NODE_ARRAY(dataObj), ele);

  /* Allocate space for new gG element. */
  if (grid && grid->len > 0)
    {
      eleArr  = g_array_sized_new(FALSE, FALSE, sizeof(VisuElement*), 1);
      g_array_append_val(eleArr, ele);
      nEleArr = g_array_sized_new(FALSE, FALSE, sizeof(guint), 1);
      g_array_append_val(nEleArr, grid->len);
  
      visu_node_array_allocate(VISU_NODE_ARRAY(dataObj), eleArr, nEleArr);

      g_array_free(eleArr, TRUE);
      g_array_free(nEleArr, TRUE);

      /* Add the coordinates. */
      for (i = 0; i < grid->len; i++)
        visu_data_addNodeFromElement(dataObj, ele, ((float*)grid->data) + (3 * i),
                                     TRUE, (i == (grid->len - 1)));
    }
}

/* Structural loading. */
static gboolean loadBigDFTIn(VisuDataLoader *self, const gchar* filename,
                             VisuData *data, guint nSet _U_,
                             GCancellable *cancel _U_, GError **error)
{
  BigDFT_Locreg *glr;
  BigDFT_Atoms *atoms;
  BigDFT_Inputs *in;
  BigDFT_Wf *wf;
  GArray *eles, *nEle, *gcoord_c, *gcoord_f;
  double box[6], h[3], crmult, frmult;
  GArray *radii;
  guint i, j, k, grid[3], iGrid, nbGrid;
  VisuElement *ele;
  float xyz[3], vect[3];
  gboolean *cgrid, *fgrid;
  guint *radiiStatus;
  int ixc;
  gchar *dirname, *cwd;
  gboolean newAt;
  VisuBox *boxObj;
  VisuNodeValuesString *labels;

  /* gboolean valid; */
  /* BigDFT_LocregIter iter; */

  g_return_val_if_fail(error && *error == (GError*)0, FALSE);
  g_return_val_if_fail(data && filename, FALSE);

  if (!useLoadWithGrid)
    {
      DBG_fprintf(stderr, "BigDFT: format disabled.\n");
      return FALSE;
    }

  /* We change the current working dir to allow BigDFT to look for
     input files and pseudos. */
  dirname = g_path_get_dirname(filename);
  cwd     = g_get_current_dir();
  g_chdir(dirname);
  g_free(dirname);

  /* Get the input parameters. */
  in = g_object_get_data(G_OBJECT(data), "BigDFT_inputs");
  if (!in)
    {
      g_chdir(cwd);
      g_free(cwd);

      return FALSE;
    }

  /* See If we have a atoms in cache. */
  newAt = FALSE;
  wf = (BigDFT_Wf*)g_object_get_data(G_OBJECT(data), "BigDFT_wf");
  if (!wf)
    {
      wf = bigdft_wf_new(in->inputPsiId);
      if (!bigdft_atoms_set_structure_from_file(BIGDFT_ATOMS(wf->lzd), filename))
        {
          DBG_fprintf(stderr, "BigDFT: '%s' is not a BigDFT file.\n", filename);
          g_object_unref(G_OBJECT(wf));
          wf = (BigDFT_Wf*)0;
        }
      else
        g_object_set_data_full(G_OBJECT(data), "BigDFT_wf", wf,
                               (GDestroyNotify)g_object_unref);
      newAt = TRUE;
    }
  if (!wf)
    {
      g_chdir(cwd);
      g_free(cwd);

      return FALSE;
    }
  atoms = BIGDFT_ATOMS(wf->lzd);

  /* We get the symmetries and the atom displacements, if any. */
  if (newAt)
    {
      bigdft_atoms_set_displacement(atoms, in->randdis);
      bigdft_atoms_set_symmetries(atoms, !in->disableSym, -1., in->elecfield);
      bigdft_inputs_parse_additional(in, atoms);
    }
  
  /* We get some input parameters. */
  ixc = GET_OPT_INT(self, "ixc");
  h[0] = GET_OPT_DBL(self, "hx");
  h[1] = GET_OPT_DBL(self, "hy");
  h[2] = GET_OPT_DBL(self, "hz");
  crmult = GET_OPT_DBL(self, "crmult");
  frmult = GET_OPT_DBL(self, "frmult");

  /* We set up the population. */
  eles = g_array_sized_new(FALSE, FALSE, sizeof(VisuElement*), atoms->ntypes);
  for (i = 0; i < atoms->ntypes; i++)
    {
      ele = visu_element_retrieveFromName(atoms->atomnames[i], (gboolean*)0);
      g_array_insert_val(eles, i, ele);
    }
  nEle = g_array_sized_new(FALSE, TRUE, sizeof(guint), atoms->ntypes);
  g_array_set_size(nEle, atoms->ntypes);
  for (i = 0; i < atoms->nat;  i++)
    *(&g_array_index(nEle, guint, atoms->iatype[i] - 1)) += 1;
  visu_node_array_allocate(VISU_NODE_ARRAY(data), eles, nEle);
  g_array_free(eles, TRUE);
  g_array_free(nEle, TRUE);

  /* Radii may already been set for this dataObj. */
  radii = (GArray*)g_object_get_data(G_OBJECT(data), "BigDFT_radii");

  /* We compute the localisation region and the new coordinates. */
  if (newAt)
    bigdft_atoms_set_psp(atoms, ixc, in->nspin, (const gchar*)0);
  if (!radii)
    {
      radii = bigdft_atoms_get_radii(atoms, crmult, frmult, 0.);
      g_array_ref(radii);
      g_object_set_data_full(G_OBJECT(data), "BigDFT_radii",
                             radii, (GDestroyNotify)g_array_unref);
      radiiStatus = g_malloc0(sizeof(guint) * atoms->ntypes * 3);
      for (i = 0; i < atoms->ntypes; i++)
        {
          radiiStatus[i] = (g_array_index(radii, double, i) == atoms->radii_cf[i])?
            RADII_VALUE_FILE:RADII_VALUE_COMPUTED;
          radiiStatus[atoms->ntypes + i] =
            (g_array_index(radii, double, atoms->ntypes + i) ==
             atoms->radii_cf[atoms->ntypes + i])?
            RADII_VALUE_FILE:RADII_VALUE_COMPUTED;
        }
      g_object_set_data_full(G_OBJECT(data), "BigDFT_radiiStatus", radiiStatus, g_free);
      g_object_set_data(G_OBJECT(data), "BigDFT_ntypes", GINT_TO_POINTER(atoms->ntypes));
    }
  bigdft_locreg_set_radii(BIGDFT_LOCREG(wf->lzd), radii);
  bigdft_locreg_set_size(BIGDFT_LOCREG(wf->lzd), h, crmult, frmult);
  bigdft_lzd_init_d(wf->lzd);
  bigdft_lzd_set_irreductible_zone(wf->lzd, in->nspin);
  bigdft_locreg_init_wfd(BIGDFT_LOCREG(wf->lzd));
  glr = BIGDFT_LOCREG(wf->lzd);

  /* We have finished here with BigDFT, we set back the current
     working dir. */
  g_chdir(cwd);
  g_free(cwd);

  for (i = 0; i < atoms->nat; i++)
    {
      xyz[0] = atoms->rxyz.data[3 * i + 0];
      xyz[1] = atoms->rxyz.data[3 * i + 1];
      xyz[2] = atoms->rxyz.data[3 * i + 2];
      visu_data_addNodeFromIndex(data, atoms->iatype[i] - 1, xyz, FALSE, FALSE);
    }

  /* We finish with the box geometry. */
  box[0] = atoms->alat[0];
  box[1] = 0.;
  box[2] = atoms->alat[1];
  box[3] = 0.;
  box[4] = 0.;
  box[5] = atoms->alat[2];
  switch (atoms->geocode)
    {
    case 'P':
      boxObj = visu_box_new(box, VISU_BOX_PERIODIC);
      grid[0] = glr->n[0] + 1;
      grid[1] = glr->n[1] + 1;
      grid[2] = glr->n[2] + 1;
      break;
    case 'S':
      boxObj = visu_box_new(box, VISU_BOX_SURFACE_ZX);
      grid[0] = glr->n[0] + 1;
      grid[1] = glr->n[1];
      grid[2] = glr->n[2] + 1;
      break;
    case 'F':
      boxObj = visu_box_new(box, VISU_BOX_FREE);
      grid[0] = glr->n[0];
      grid[1] = glr->n[1];
      grid[2] = glr->n[2];
      break;
    default:
      g_warning("Unknown geocode.");
      boxObj = visu_box_new(box, VISU_BOX_PERIODIC);
      grid[0] = glr->n[0] + 1;
      grid[1] = glr->n[1] + 1;
      grid[2] = glr->n[2] + 1;
    }
  visu_boxed_setBox(VISU_BOXED(data), VISU_BOXED(boxObj));
  g_object_unref(boxObj);
  visu_box_setMargin(boxObj, visu_node_array_getMaxElementSize(VISU_NODE_ARRAY(data)) +
                     visu_data_getAllNodeExtens(data, boxObj), TRUE);
  visu_box_setUnit(boxObj, TOOL_UNITS_BOHR);
  /* Update h values with the possibly computed values of glr. */
  SET_OPT_DBL(format, "hx", glr->h[0]);
  SET_OPT_DBL(format, "hy", glr->h[1]);
  SET_OPT_DBL(format, "hz", glr->h[2]);

  /* Additional data. */
  nbGrid = (glr->n[0] + 1) * (glr->n[1] + 1) * (glr->n[2] + 1);
  cgrid = bigdft_locreg_get_grid(glr, GRID_COARSE);
  gcoord_c = g_array_sized_new(FALSE, FALSE, sizeof(gfloat) * 3, nbGrid);
  iGrid = 0;
  for (k = 0; k <= glr->n[2]; k++)
    for (j = 0; j <= glr->n[1]; j++)
      for (i = 0; i <= glr->n[0]; i++)
        {
          if (cgrid[iGrid] != 0)
            {
              vect[0] = (float)i / (float)grid[0];
              vect[1] = (float)j / (float)grid[1];
              vect[2] = (float)k / (float)grid[2];
              g_array_append_val(gcoord_c, vect);
            }
          iGrid += 1;
        }
  /* gcoord_c = g_array_sized_new(FALSE, FALSE, sizeof(gfloat) * 3, glr->nseg_f); */
  /* for (valid = bigdft_locreg_iter_new(glr, &iter, GRID_FINE); valid; */
  /*      valid = bigdft_locreg_iter_next(&iter)) */
  /*   { */
  /*     vect[0] = iter.x0; */
  /*     vect[1] = iter.y; */
  /*     vect[2] = iter.z; */
  /*     g_array_append_val(gcoord_c, vect); */
  /*     if (iter.x1 != iter.x0) */
  /*       { */
  /*         vect[0] = iter.x1; */
  /*         vect[1] = iter.y; */
  /*         vect[2] = iter.z; */
  /*         g_array_append_val(gcoord_c, vect); */
  /*       } */
  /*   } */
  DBG_fprintf(stderr, "BigDFT: have %d coarse grid points.\n", gcoord_c->len);
  g_object_set_data_full(G_OBJECT(data), "BigDFT_coarse_grid",
                         (gpointer)gcoord_c, (GDestroyNotify)g_array_unref);
  g_free(cgrid);
  if (gcoord_c->len > 0 && useCGrid)
    bigdft_show_grid(data, gcoord_c, "g", 0);

  fgrid = bigdft_locreg_get_grid(glr, GRID_FINE);
  gcoord_f = g_array_sized_new(FALSE, FALSE, sizeof(gfloat) * 3, nbGrid);
  iGrid = 0;
  for (k = 0; k <= glr->n[2]; k++)
    for (j = 0; j <= glr->n[1]; j++)
      for (i = 0; i <= glr->n[0]; i++)
        {
          if (fgrid[iGrid] != 0)
            {
              vect[0] = (float)i / (float)grid[0];
              vect[1] = (float)j / (float)grid[1];
              vect[2] = (float)k / (float)grid[2];
              g_array_append_val(gcoord_f, vect);
            }
          iGrid += 1;
        }
  DBG_fprintf(stderr, "BigDFT: have %d fine grid points.\n", gcoord_f->len);
  if (gcoord_f->len > 0)
    g_object_set_data_full(G_OBJECT(data), "BigDFT_fine_grid",
                           (gpointer)gcoord_f, (GDestroyNotify)g_array_unref);
  else
    g_array_free(gcoord_f, TRUE);
  if (gcoord_f->len > 0 && useFGrid)
    bigdft_show_grid(data, gcoord_f, "G", 0);
  g_free(fgrid);

  /* Possible label. */
  labels = visu_data_getNodeLabels(data);
  for (i = 0; i < atoms->nat; i++)
    visu_node_values_string_setAt
      (labels, visu_node_array_getFromId(VISU_NODE_ARRAY(data), i),
       bigdft_atoms_get_extra_as_label(BIGDFT_ATOMS(wf->lzd), i));

  return TRUE;
}

static void onToggleLoad(GtkToggleButton *toggle, gpointer data _U_)
{
  VisuData *dataObj;

  useLoadWithGrid = gtk_toggle_button_get_active(toggle);
  
  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT));
  if (!dataObj)
    return;

  visu_ui_rendering_window_reload(visu_ui_main_class_getDefaultRendering());
}

static void onHgrid(VisuUiNumericalEntry *entry, gdouble old_value _U_, gpointer data)
{
  gdouble value;
  const gchar *hgrid[] = {"hx", "hy", "hz"};
  VisuBox *boxObj;

  if (disableCallbacks)
    return;
  boxObj = visu_boxed_getBox(visu_ui_panel_getFocused(VISU_UI_PANEL(panelBigDFT)));
  if (!boxObj)
    return;

  value = CLAMP(visu_ui_numerical_entry_getValue(entry), 0.1, 1.);
  disableCallbacks = TRUE;
  if (visu_box_getBoundary(boxObj) == VISU_BOX_FREE)
    {
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryHgrid[0]), value);
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryHgrid[1]), value);
      visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryHgrid[2]), value);
    }
  else
    visu_ui_numerical_entry_setValue(entry, value);
  disableCallbacks = FALSE;

  if (visu_box_getBoundary(boxObj) == VISU_BOX_FREE)
    {
      SET_OPT_DBL(fmtWvl, hgrid[0], value);
      SET_OPT_DBL(fmtWvl, hgrid[1], value);
      SET_OPT_DBL(fmtWvl, hgrid[2], value);
    }
  else
    SET_OPT_DBL(fmtWvl, hgrid[GPOINTER_TO_INT(data)], value);

  if (useLoadWithGrid)
    visu_ui_rendering_window_reload(visu_ui_main_class_getDefaultRendering());
}
static void onRmult(VisuUiNumericalEntry *entry, gdouble old_value _U_, gpointer data)
{
  gdouble value;
  const gchar *mult[] = {"crmult", "frmult"};

  if (disableCallbacks)
    return;

  value = CLAMP(visu_ui_numerical_entry_getValue(entry), 1., 20.);
  disableCallbacks = TRUE;
  visu_ui_numerical_entry_setValue(entry, value);
  disableCallbacks = FALSE;

  SET_OPT_DBL(fmtWvl, mult[GPOINTER_TO_INT(data)], value);

  if (useLoadWithGrid && visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT)))
    visu_ui_rendering_window_reload(visu_ui_main_class_getDefaultRendering());
}
static void onRadius(VisuUiNumericalEntry *entry, gdouble old_value _U_, gpointer data)
{
  GArray *radii;
  double value;
  guint nEle, iEle;
  guint *radiiStatus;
  VisuData *dataObj;

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

  value = CLAMP(visu_ui_numerical_entry_getValue(entry), 0., 15.);
  disableCallbacks = TRUE;
  visu_ui_numerical_entry_setValue(entry, value);
  disableCallbacks = FALSE;

  if (!dataObj)
    return;

  radii = (GArray*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radii");
  radiiStatus = (guint*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radiiStatus");
  g_return_if_fail(radii && radiiStatus);

  nEle = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dataObj), "BigDFT_ntypes"));
  iEle = gtk_combo_box_get_active(GTK_COMBO_BOX(cbRadEle[GPOINTER_TO_INT(data)]));
  g_array_insert_val(radii, nEle * GPOINTER_TO_INT(data) + iEle, value);
  radiiStatus[nEle * GPOINTER_TO_INT(data) + iEle] = RADII_VALUE_EDITED;
  gtk_entry_set_icon_from_stock(GTK_ENTRY(entry), GTK_ENTRY_ICON_SECONDARY,
                                radiiIcons[RADII_VALUE_EDITED]);

  if (useLoadWithGrid)
    visu_ui_rendering_window_reload(visu_ui_main_class_getDefaultRendering());
}
static void onEleChanged(GtkComboBox *combo, gpointer data)
{
  GArray *radii;
  guint nEle, iEle;
  guint *radiiStatus;
  VisuData *dataObj;

  disableCallbacks = TRUE;

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

  radii = (GArray*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radii");
  radiiStatus = (guint*)g_object_get_data(G_OBJECT(dataObj), "BigDFT_radiiStatus");
  if (!radii || !radiiStatus)
    return;

  nEle = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(dataObj), "BigDFT_ntypes"));
  iEle = gtk_combo_box_get_active(combo);
  if (iEle >= nEle)
    gtk_combo_box_set_active(combo, nEle - 1);
  else
    {
      visu_ui_numerical_entry_setValue
        (VISU_UI_NUMERICAL_ENTRY(entryRadius[GPOINTER_TO_INT(data)]),
         g_array_index(radii, double, nEle * GPOINTER_TO_INT(data) + iEle));
      gtk_entry_set_icon_from_stock(GTK_ENTRY(entryRadius[GPOINTER_TO_INT(data)]),
                                    GTK_ENTRY_ICON_SECONDARY,
                                    radiiIcons[radiiStatus[nEle * GPOINTER_TO_INT(data) +
                                                           iEle]]);
    }
  
  disableCallbacks = FALSE;
}
static void onIxc(VisuUiNumericalEntry *entry, gdouble old_value _U_, gpointer data _U_)
{
  if (disableCallbacks)
    return;

  SET_OPT_INT(fmtWvl, "ixc", visu_ui_numerical_entry_getValue(entry));

  if (useLoadWithGrid && visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT)))
    visu_ui_rendering_window_reload(visu_ui_main_class_getDefaultRendering());
}
static void onFilesChanged(VisuData *dataObj, guint kind, gpointer data _U_)
{
  BigDFT_Inputs *in;
  gchar *basename, *ptr, *dirname, *cwd;

  DBG_fprintf(stderr, "Panel BigDFT: test input from (%d) '%s'.\n",
              kind, visu_data_getFile(dataObj, 0, (ToolFileFormat**)0));
  if (kind != 0 || !useLoadWithGrid ||
      !tool_file_format_match(fmtWvl, visu_data_getFile(dataObj, 0, (ToolFileFormat**)0)))
    return;

  /* We change the current working dir to allow BigDFT to look for
     input files and pseudos. */
  dirname = g_path_get_dirname(visu_data_getFile(dataObj, 0, (ToolFileFormat**)0));
  cwd     = g_get_current_dir();
  g_chdir(dirname);
  g_free(dirname);

  basename = g_path_get_basename(visu_data_getFile(dataObj, 0, (ToolFileFormat**)0));
  ptr = strrchr(basename, '.');
  if (ptr)
    *ptr = '\0';
  DBG_fprintf(stderr, "Panel BigDFT: read input files for '%s'.\n", basename);
  if (strcmp(basename, "posinp"))
    in = bigdft_inputs_new(basename);
  else
    in = bigdft_inputs_new((const gchar*)0);
  g_free(basename);
  /* We store the input parameters for future use with this dataObj. */
  g_object_set_data_full(G_OBJECT(dataObj), "BigDFT_inputs", in,
                         (GDestroyNotify)bigdft_inputs_free);
  if (in->files & BIGDFT_INPUTS_DFT)
    {
      SET_OPT_DBL(fmtWvl, "hx", in->h[0]);
      SET_OPT_DBL(fmtWvl, "hy", in->h[1]);
      SET_OPT_DBL(fmtWvl, "hz", in->h[2]);
      SET_OPT_DBL(fmtWvl, "crmult", in->crmult);
      SET_OPT_DBL(fmtWvl, "frmult", in->frmult);
      SET_OPT_INT(fmtWvl, "ixc", in->ixc);
    }

  /* We have finished here with BigDFT, we set back the current
     working dir. */
  g_chdir(cwd);
  g_free(cwd);
}
static void onNproc(GtkSpinButton *spin, gpointer data _U_)
{
  SET_OPT_INT(fmtWvl, "nproc", gtk_spin_button_get_value(spin));

  update_memory_estimation(visu_ui_panel_getData(VISU_UI_PANEL(panelBigDFT)));
}

static void update_memory_estimation(VisuData *data)
{
  BigDFT_Inputs *in;
  BigDFT_Wf *wf;
  BigDFT_Proj *proj;
  double frmult;
  int out_pipe[2], stdout_fileno_old;

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

  frmult = GET_OPT_DBL(fmtWvl, "frmult");

  proj = bigdft_proj_new(BIGDFT_LOCREG(wf->lzd), BIGDFT_ORBS(wf), frmult);

  stdout_fileno_old = redirect_init(out_pipe);
  bigdft_memory_get_peak(GET_OPT_INT(fmtWvl, "nproc"), BIGDFT_LOCREG(wf->lzd), in,
                         BIGDFT_ORBS(wf), proj);
  redirect_dump(out_pipe, stdout_fileno_old);

  bigdft_proj_free(proj);
}
static int redirect_init(int out_pipe[2])
{
  int stdout_fileno_old;

  /* Flush before redirecting. */
  fflush(stdout);

  /* Make a pipe to redirect stdout. */
  stdout_fileno_old = dup(STDOUT_FILENO);
#ifndef _WIN32
  g_return_val_if_fail((pipe(out_pipe) == 0), stdout_fileno_old);
  dup2(out_pipe[1], STDOUT_FILENO);
#endif

  return stdout_fileno_old;
}

#define MAX_FORTRAN_OUTPUT 4096
static void redirect_dump(int out_pipe[2], int stdout_fileno_old)
{
  gchar foutput[MAX_FORTRAN_OUTPUT];
  ssize_t ncount;
  long flags;
  GtkTextIter startIter;

  /* Flush before reconnecting. */
  fflush(stdout);
  /* Reconnect stdout. */
  dup2(stdout_fileno_old, STDOUT_FILENO);

#ifndef _WIN32
  /* Make the reading pipe non blocking. */
  flags = fcntl(out_pipe[0], F_GETFL);
  flags |= O_NONBLOCK;
  fcntl(out_pipe[0], F_SETFL, flags);

  /* Write Fortran output with prefix... */
  foutput[0] = '\0';
  ncount = read(out_pipe[0], foutput, MAX_FORTRAN_OUTPUT);
  foutput[ncount] = '\0';
  gtk_text_buffer_set_text(bufMemory, "", -1);
  gtk_text_buffer_get_start_iter(bufMemory, &startIter);
  gtk_text_buffer_insert_with_tags_by_name(bufMemory, &startIter, foutput, -1,
                                           "typewriter", NULL);

  /* Close the pipes. */
  close(out_pipe[0]);
  close(out_pipe[1]);
#endif
}

static void exportParameters(GString *data, VisuData *dataObj _U_)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_BIGDFT);
  g_string_append_printf(data, "%s[bigdft]: %d\n\n", FLAG_PARAMETER_BIGDFT,
			 useLoadWithGrid);
}
