/*   EXTRAITS DE LA LICENCE
     Copyright CEA, contributeurs : Luc BILLARD et Damien
     CALISTE, laboratoire L_Sim, (2001-2005)
  
     Adresse mèl :
     BILLARD, non joignable par mèl ;
     CALISTE, damien P caliste AT cea P fr.

     Ce logiciel est un programme informatique servant à visualiser des
     structures atomiques dans un rendu pseudo-3D. 

     Ce logiciel est régi par la licence CeCILL soumise au droit français et
     respectant les principes de diffusion des logiciels libres. Vous pouvez
     utiliser, modifier et/ou redistribuer ce programme sous les conditions
     de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
     sur le site "http://www.cecill.info".

     Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
     pris connaissance de la licence CeCILL, et que vous en avez accepté les
     termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
     Copyright CEA, contributors : Luc BILLARD et Damien
     CALISTE, laboratoire L_Sim, (2001-2005)

     E-mail address:
     BILLARD, not reachable any more ;
     CALISTE, damien P caliste AT cea P fr.

     This software is a computer program whose purpose is to visualize atomic
     configurations in 3D.

     This software is governed by the CeCILL  license under French law and
     abiding by the rules of distribution of free software.  You can  use, 
     modify and/ or redistribute the software under the terms of the CeCILL
     license as circulated by CEA, CNRS and INRIA at the following URL
     "http://www.cecill.info". 

     The fact that you are presently reading this means that you have had
     knowledge of the CeCILL license and that you accept its terms. You can
     find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/

#include <glib.h>

#include "abinit.h"

#include <string.h>

#include <config.h>
#include <visu_tools.h>
#include <visu_basic.h>
#include <gtk_interactive.h>
#include <openGLFunctions/interactive.h>
#include <coreTools/toolMatrix.h>
#include <coreTools/toolPhysic.h>

#include "ab_symmetry.h"

/* For compatibility with older version. */
#ifndef AB6_INVARS_STR
#define AB6_INVARS_STR(A) #A
#endif

#ifndef ab6_error_string_from_id
#define ab6_error_string_from_id(A) #A
#endif

#define ABINIT_DT          "ABINIT_datasets"

#define ABINIT_DESCRIPTION _("<span size=\"smaller\">"			\
			     "This plug-in introduces support for\n"	\
			     "crystallographic structures in\n"		\
			     "<b>ABINIT</b> input files.</span>")
#define ABINIT_AUTHORS     "Caliste Damien"

/* Local methods */
#ifdef HAVE_ABINIT_PARSER
static void abStructuralInit(VisuRendering *method);
static void abSpinInit(VisuRendering *method);
static gboolean loadAbinitIn(VisuData *data, const gchar* filename,
			     ToolFileFormat *format, int nSet,
                             GCancellable *cancel, GError **error);
static gboolean loadAbinitSpin(VisuData *data, const gchar* filename,
			       ToolFileFormat *format, int nSet,
                               GCancellable *cancel, GError **error);
static gpointer parseAbinitThread(gpointer data);
static GError* loadAbinit(VisuData *data, AbInvars *dt, int nSet);
static gboolean waitDt(gpointer data);
static void freeDt(gpointer dt);
static gchar** readPseudoFiles(AbinitData *dt);
#endif

/* Local variables */
static gchar *iconPath;

/* Required methods for a loadable module. */
gboolean abinitInit()
{
  DBG_fprintf(stderr, "Abinit: loading plug-in 'abinit'...\n");
  iconPath = g_build_filename(V_SIM_PIXMAPS_DIR, "abinit.png", NULL);

#ifdef HAVE_ABINIT_PARSER
  DBG_fprintf(stderr, "Abinit: declare a new rendering load method.\n");
  abStructuralInit(visu_rendering_getByName(VISU_RENDERING_ATOMIC_NAME));

  DBG_fprintf(stderr, "Abinit: declare a new spin load method.\n");
  abSpinInit(visu_rendering_getByName(VISU_RENDERING_SPIN_NAME));

  ab_mpi_init();
#endif

  return TRUE;
}

gboolean abinitInitGtk()
{
  DBG_fprintf(stderr, "Abinit: declare a new interactive tab.\n");
  visu_ui_interactive_addAction(buildTab, startSelect, stopSelect);

  return TRUE;
}

const char* abinitGet_description()
{
  return ABINIT_DESCRIPTION;
}

const char* abinitGet_authors()
{
  return ABINIT_AUTHORS;
}

const char* abinitGet_icon()
{
  return iconPath;
}

#ifdef HAVE_ABINIT_PARSER
static void abStructuralInit(VisuRendering *method)
{
#ifdef HAVE_NEW_WITH_PSEUDO
  const gchar *type[] = {"*.in", "*.files", (char*)0};
#else
  const gchar *type[] = {"*.in", (char*)0};
#endif
  
  visu_rendering_addFileFormat(method, 0,
                               tool_file_format_new(_("ABINIT input file format"),
                                                    type),
                               95, loadAbinitIn);
}
#endif

static GMutex *dt_mutex = (GMutex*)0;
void abinit_mutexInit()
{
  if (!dt_mutex)
    {
#if GLIB_MINOR_VERSION < 32
      dt_mutex = g_mutex_new();
#else
      dt_mutex = g_malloc(sizeof(GMutex));
      g_mutex_init(dt_mutex);
#endif
    }
}
void abinit_mutexRelease()
{
  if (dt_mutex)
    {
#if GLIB_MINOR_VERSION < 32
      g_mutex_free(dt_mutex);
#else
      g_mutex_clear(dt_mutex);
#endif
      dt_mutex = (GMutex*)0;
    }
}
void abinit_mutexUnlock()
{
  if (dt_mutex)
    g_mutex_unlock(dt_mutex);
}
static AbinitData ABINIT_DATA;
AbinitData* abinit_getDt()
{
  if (!g_mutex_trylock(dt_mutex))
    return (AbinitData*)0;
  else
    return &ABINIT_DATA;
}
AbinitData* abinit_getDirectDt()
{
  return &ABINIT_DATA;
}

#ifdef HAVE_ABINIT_PARSER
static gboolean loadAbinitIn(VisuData *data, const gchar* filename,
			     ToolFileFormat *format _U_, int nSet,
                             GCancellable *cancel, GError **error)
{
#ifdef G_THREADS_ENABLED
  GThread *ld_thread;
#endif
  AbinitData *dt;
  AbInvars *dataset;

  g_return_val_if_fail(error && !*error, FALSE);

  /* Try to see if data has already read this dataset. */
  dataset = (AbInvars*)g_object_get_data(G_OBJECT(data), ABINIT_DT);
  if (dataset)
    {
      *error = loadAbinit(data, dataset, nSet);
      return TRUE;
    }
  
  /* Read the file, since it's the first time. */
  dt = abinit_getDirectDt();
  dt->dt          = (AbInvars*)0;
  dt->filename    = g_strdup(filename);
  dt->error       = (GError*)0;
  dt->isABFormat  = FALSE;
  dt->data        = data;
  dt->nSet        = nSet;
  dt->getMessages = TRUE;
  dt->messages    = g_async_queue_new();
  dt->useSignals  = TRUE;
  dt->signals     = g_async_queue_new();
  dt->cancel      = cancel;
#ifdef G_THREADS_ENABLED
  abinit_mutexInit();
  ld_thread = g_thread_create(parseAbinitThread, (gpointer)0, FALSE, error);
  DBG_fprintf(stderr, "AB structure: run ABINIT parsing into a thread (%p) from me (%p).\n",
              (gpointer)ld_thread, (gpointer)g_thread_self());
  if (ld_thread)
    {
      g_async_queue_pop(dt->signals);
      DBG_fprintf(stderr, "AB structure: start waiting.\n");
      g_idle_add_full(G_PRIORITY_LOW, waitDt, (gpointer)0, (GDestroyNotify)0);
    }
  else
    g_warning("Can't run thread for ABINIT parsing.");
#else
  DBG_fprintf(stderr, "AB structure: run ABINIT parsing directly.\n");
  *error = (GError*)parseAbinitThread((gpointer)(&dt));
#endif

  while(g_main_context_pending(NULL))
    g_main_context_dispatch(g_main_context_default());
  
  if (dt->error)
    *error = dt->error;

  g_async_queue_unref(dt->signals);
  g_async_queue_unref(dt->messages);
  abinit_mutexRelease();

  g_free(dt->filename);

  DBG_fprintf(stderr, "AB structure: file parsed, returns %d.\n", dt->isABFormat);
  return dt->isABFormat;
}

static gpointer parseAbinitThread(gpointer data _U_)
{
  AbinitData *dt;
  AbInvars *dt_;
  gchar **pseudos;

  DBG_fprintf(stderr, "AB(%p) main: get the dt structure.\n",
              (gpointer)g_thread_self());
  dt = abinit_getDt();
  g_return_val_if_fail(dt, (gpointer)0);

  /* Send a signal to V_Sim to say that parsing will start. */
  g_async_queue_push(dt->signals, GINT_TO_POINTER(TRUE));

  /* Try to see if the provided file is a *.files with pseudo
     information. */
  pseudos = readPseudoFiles(dt);
  if (!dt->error)
    {
      /* Read the file and store its contain as a string. */
      DBG_fprintf(stderr, "AB(%p) main: read and store input file '%s'.\n",
                  (gpointer)g_thread_self(), dt->filename);
#ifdef HAVE_NEW_WITH_PSEUDO
      DBG_fprintf(stderr, "AB(%p) main: pseudo list %p.\n",
                  (gpointer)g_thread_self(), (gpointer)pseudos);
      dt_ = ab_invars_new_from_file_with_pseudo(dt->filename, (const char**)pseudos);
#else
      dt_ = ab_invars_new_from_file(dt->filename);
      if (pseudos)
        g_warning("Unsupported ABINIT input file with pseudo-potential information.");
#endif
      if (pseudos)
        g_strfreev(pseudos);

      /* If we reach here, it means that no error happened. */
      dt->dt = dt_;
    }

  DBG_fprintf(stderr, "AB(%p) main: unlock mutex, other thread can access dt.\n",
              (gpointer)g_thread_self());
  abinit_mutexUnlock();

  return (gpointer)0;
}

static gboolean waitDt(gpointer data _U_)
{
  AbinitData *dt;
  gpointer mess;

  dt = abinit_getDirectDt();

  /* We stop on cancel, without error. */
  if (g_cancellable_is_cancelled(dt->cancel))
    {
      /* We push a message in the queue to be tested by ABINIT. */
      g_async_queue_push(dt->signals, GINT_TO_POINTER(TRUE));
      return TRUE;
    }

  mess = g_async_queue_try_pop(dt->messages);
  if (mess)
    {
      visu_object_setLoadMessage(VISU_OBJECT_INSTANCE, (const gchar*)mess);
      g_free(mess);
    }

  /* DBG_fprintf(stderr, "AB structure: waiting...\n"); */
  dt = abinit_getDt();
  if (!dt)
    return TRUE;

  /* We empty the message queue, in case. */
  DBG_fprintf(stderr, "AB structure: empty the queue.\n");
  while (g_async_queue_length(dt->messages) > 0)
    {
      DBG_fprintf(stderr, " | emptying.\n");
      g_free(g_async_queue_pop(dt->messages));
    }

  DBG_fprintf(stderr, "AB structure: stop waiting, analyse.\n");
  dt->isABFormat = TRUE;
  if (dt->error && dt->error->code == TOOL_FILE_FORMAT_ERROR_FILE)
    {
      dt->isABFormat = FALSE;
      g_error_free(dt->error);
      dt->error = (GError*)0;
      abinit_mutexUnlock();
      return FALSE;
    }
  else if (dt->error)
    {
      abinit_mutexUnlock();
      return FALSE;
    }
  else
    {
      dt->error = loadAbinit(dt->data, dt->dt, dt->nSet);
      /* We attach this Abinit dataset to this VisuData. */
      DBG_fprintf(stderr, "AB structure: add '%s' to VisuData %p.\n",
                  ABINIT_DT, (gpointer)dt->data);
      g_object_set_data_full(G_OBJECT(dt->data), ABINIT_DT, (gpointer)dt->dt, freeDt);
    }

  abinit_mutexUnlock();

  return FALSE;
}

static void freeDt(gpointer dt)
{
  DBG_fprintf(stderr, "AB structure: release ABINIT resources.\n");
  ab_invars_free((AbInvars*)dt);
}

static gchar** readPseudoFiles(AbinitData *dt)
{
  GIOChannel *ioFile;
  GString *line= (GString*)0;
  gchar *inFile;
  GList *lst, *tmpLst;
  gchar **out;
  int i;

  DBG_fprintf(stderr, "AB(%p) main: read '%s' to find pseudo files.\n",
              (gpointer)g_thread_self(), dt->filename);
  
  ioFile = g_io_channel_new_file(dt->filename, "r", NULL);
  if (!ioFile)
    return (gchar**)0;

  line = g_string_new("");
  if (g_io_channel_read_line_string(ioFile, line, NULL, NULL) != G_IO_STATUS_NORMAL)
    {
      g_io_channel_shutdown(ioFile, FALSE, NULL);
      g_io_channel_unref(ioFile);
      g_string_free(line, TRUE);

      return (gchar**)0;
    }
  inFile = g_strstrip(g_strdup(line->str));
  /* We test inFile in case. */
  DBG_fprintf(stderr, "AB(%p) main: testing '%s' as input file.\n",
              (gpointer)g_thread_self(), inFile);
  if (!g_file_test(inFile, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
    {
      g_io_channel_shutdown(ioFile, FALSE, NULL);
      g_io_channel_unref(ioFile);
      g_string_free(line, TRUE);
      g_free(inFile);

      return (gchar**)0;
    }

  /* From now on, we consider that we have a valid *.files file. */
  for (i = 0; i < 4; i++)
    if (g_io_channel_read_line_string(ioFile, line, NULL, &dt->error) != G_IO_STATUS_NORMAL)
      {
        g_io_channel_shutdown(ioFile, FALSE, NULL);
        g_io_channel_unref(ioFile);
        g_string_free(line, TRUE);
        g_free(inFile);

        return (gchar**)0;
      }

  lst = (GList*)0;
  while (g_io_channel_read_line_string(ioFile, line, NULL, &dt->error) == G_IO_STATUS_NORMAL)
    {
      lst = g_list_append(lst, (gpointer)g_strstrip(g_strdup(line->str)));
      DBG_fprintf(stderr, "AB(%p) main: testing '%s' as a pseudo.\n",
                  (gpointer)g_thread_self(), (gchar*)lst->data);
      if (!g_file_test((const gchar*)lst->data, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
        {
          dt->error = g_error_new(VISU_ERROR_RENDERING, TOOL_FILE_FORMAT_ERROR_FILE,
                                  "Invalid psedo-potential file '%s'.",
                                  (const gchar*)lst->data);
          g_io_channel_shutdown(ioFile, FALSE, NULL);
          g_io_channel_unref(ioFile);
          g_string_free(line, TRUE);
          g_free(inFile);
          for (tmpLst = lst; tmpLst; tmpLst = g_list_next(tmpLst))
            g_free(tmpLst->data);
          g_list_free(lst);

          return (gchar**)0;
        }
    }
  g_string_free(line, TRUE);

  out = g_malloc(sizeof(gchar*) * (g_list_length(lst) + 1));
  for (i = 0, tmpLst = lst; tmpLst; i++, tmpLst = g_list_next(tmpLst))
    out[i] = tmpLst->data;
  out[i] = (gchar*)0;
  g_list_free(lst);
  g_free(dt->filename);
  dt->filename = inFile;

  return out;
}
#endif

void FC_FUNC(wrtout, WRTOUT)(int *unit,char message[500], char mode_paral[4])
{
  gchar *buf, *ptError, *ptInvars0, *ptInstrng, *ptSize, *ptLen, *ptMinus;
  AbinitData *dt;

  dt = abinit_getDirectDt();
  g_return_if_fail(dt);

#ifdef G_THREADS_ENABLED
  if (dt->useSignals && g_async_queue_try_pop(dt->signals))
    {
      dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_CANCEL,
                              "Loading process cancelled.");
      abinit_mutexUnlock();
      g_thread_exit((gpointer)0);
    }
#endif

  buf = g_strndup(message, 500);
  g_strstrip(buf);
  DBG_fprintf(stderr, "AB(%p) main: wrtout(%d %c%c%c%c) %s\n", (gpointer)g_thread_self(),
	      *unit, mode_paral[0], mode_paral[1], mode_paral[2], mode_paral[3], buf);
  /* We analyse buf. If, it contains an error, we test if it is
     about natom in inarvs0. If so, the file is not a valid ABINIT
     file. On the contrary, we get the message and raise an error. */
  ptError = strstr(buf, "ERROR");
  if (!ptError)
    ptError = strstr(buf, "BUG");
  if (!ptError)
    ptError = strstr(buf, "Error");
  ptInvars0 = strstr(buf, "Input natom must be defined");
  ptInstrng = strstr(buf, "The occurence of a tab");
  ptSize    = strstr(buf, "The size of your input file");
  ptLen     = strstr(buf, "The number of lines already read from input file=");
  ptMinus   = strstr(buf, "the occurence of a minus sign followed");
  if (ptError && ptInvars0)
    dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FILE,
                            "Not an ABINIT file (no 'natom' keyword found).");
  else if (ptError && ptInstrng)
    dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FILE,
                            "Not an ABINIT file (tab characters found in the file).");
  else if (ptError && ptSize)
    dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FILE,
                            "Not an ABINIT file (input file too long).");
  else if (ptError && ptLen)
    dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FILE,
                            "Not an ABINIT file (too many lines).");
  else if (ptError && ptMinus)
    dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FILE,
                            "Not an ABINIT file (minus space error).");
  else if (ptError)
    dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FORMAT, "%s", buf);
  else
    dt->error = (GError*)0;

  if (dt->getMessages && strstr(buf, "getkgrid"))
    g_async_queue_push(dt->messages, buf);
  else
    g_free(buf);

  if (dt->error)
    {
      DBG_fprintf(stderr, "AB(%p) main: an error occured in ABINIT.\n",
                  (gpointer)g_thread_self());
#ifdef G_THREADS_ENABLED
      abinit_mutexUnlock();
      g_thread_exit((gpointer)0);
#else
      g_error("ABINIT plug-in requires threads.");
#endif
    }
}
void FC_FUNC(leave_new, LEAVE_NEW)(char mode_paral[4])
{
  AbinitData *dt;

  dt = abinit_getDirectDt();
  g_return_if_fail(dt);

  DBG_fprintf(stderr, "AB(%p) main: leave_new(%c%c%c%c)\n", (gpointer)g_thread_self(),
	      mode_paral[0], mode_paral[1], mode_paral[2], mode_paral[3]);
  dt->error = g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FILE,
                          "Not an ABINIT file");
  DBG_fprintf(stderr, "AB(%p) main: return error (%p).\n",
              (gpointer)g_thread_self(), (gpointer)dt->error);
#ifdef G_THREADS_ENABLED
  abinit_mutexUnlock();
  g_thread_exit((gpointer)0);
#else
  g_error("ABINIT plug-in requires threads.");
#endif
}
void FC_FUNC(timab, TIMAB)()
{
}
void FC_FUNC(psp_from_data, PSP_FROM_DATA)()
{
}

#ifdef HAVE_ABINIT_PARSER
static GError* loadAbinit(VisuData *data, AbInvars *dt, int nSet)
{
  int ndtset, i;
  int ntypat, natom, nzero, *index;
  int *typat;
  double *znucl, rprimd[3][3], *coord, *spinat;
  VisuElement **ntypes;
  VisuElement *ele;
  float rcov, red[3];
  char *ptChar, *name;
  GArray *nattyp, *types;
  AbError error;
  gboolean newEle;
  VisuBox *boxObj;

  g_return_val_if_fail(dt, (GError*)0);

  DBG_fprintf(stderr, "AB structure: transfer data.\n");
  /* Ok, try to find the required keywords. */
  error = ab_invars_get_ndtset(dt, &ndtset);
  if (error != AB_NO_ERROR)
    return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
		       "Abinit loader report error %d while getting n datasets.",
		       (int)error);
  DBG_fprintf(stderr, "AB structure: found %d dtsets.\n", ndtset);
  /* Store the number of datasets. */
  visu_data_setNSubset(data, ndtset);

  g_return_val_if_fail(nSet >= 0 && nSet < ndtset,
		       g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
				   "Can't load dataset %d.", nSet));

  error = ab_invars_get_integer(dt, AB_INVARS_NTYPAT, nSet + 1, &ntypat);
  if (error != AB_NO_ERROR)
    return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
		       "Abinit loader report error:\n %s\nwhile getting attribute '%s'.",
		       ab6_error_string_from_id(error),
		       AB_INVARS_STR(AB_INVARS_NTYPAT));
  error = ab_invars_get_integer(dt, AB_INVARS_NATOM,  nSet + 1, &natom);
  if (error != AB_NO_ERROR)
    return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
		       "Abinit loader report error:\n %s\nwhile getting attribute '%s'.",
		       ab6_error_string_from_id(error),
		       AB_INVARS_STR(AB_INVARS_NATOM));
  DBG_fprintf(stderr, "AB structure: with %d atoms and %d types.\n",
	      natom, ntypat);
  
  typat = g_malloc(sizeof(int) * natom);
  error = ab_invars_get_integer_array(dt, typat, natom,
				       AB_INVARS_TYPAT, nSet + 1);
  if (error != AB_NO_ERROR)
    {
      g_free(typat);
      return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
			 "Abinit loader report error:\n %s\nwhile getting attribute '%s'.",
			 ab6_error_string_from_id(error),
			 AB_INVARS_STR(AB_INVARS_TYPAT));
    }

  znucl = g_malloc(sizeof(double) * ntypat);
  error = ab_invars_get_real_array(dt, znucl, ntypat,
				    AB_INVARS_ZNUCL, nSet + 1);
  if (error != AB_NO_ERROR)
    {
      g_free(typat); g_free(znucl);
      return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
			 "Abinit loader report error:\n %s\nwhile getting attribute '%s'.",
			 ab6_error_string_from_id(error),
			 AB_INVARS_STR(AB_INVARS_ZNUCL));
    }
  DBG_fprintf(stderr, "AB structure: read znucl OK.\n");
  types = g_array_sized_new(FALSE, FALSE, sizeof(VisuElement*), ntypat);
  for (i = 0; i < ntypat; i++)
    {
      /* Try to find a name instead of a z number. */
      tool_physic_getSymbolFromZ(&ptChar, &rcov, znucl[i]);
      name = g_strdup(ptChar);
      /* adding name to the hashtable */
      ele = visu_element_retrieveFromName(name, &newEle);
      g_array_insert_val(types, i, ele);
      if (newEle)
        visu_rendering_atomic_setRadius(ele, rcov);
      g_free(name);
    }
  g_free(znucl);
  DBG_fprintf(stderr, "AB structure: all new elements created.\n");
  nattyp = g_array_sized_new(FALSE, TRUE, sizeof(guint), ntypat);
  g_array_set_size(nattyp, ntypat);
  ntypes = g_malloc(sizeof(VisuElement*) * natom);
  for (i = 0; i < natom; i++)
    {
      g_array_index(nattyp, guint, typat[i] - 1) += 1;
      ntypes[i] = g_array_index(types, VisuElement*, typat[i] - 1);
    }
  /* Reduce the arrays when nattyp is 0. */
  nzero = 0;
  index = g_malloc(sizeof(int) * ntypat);
  for (i = 0; i < ntypat; i++)
    {
      if (i > nzero)
	{
	  g_array_index(nattyp, guint, nzero)       = g_array_index(nattyp, guint, i);
	  g_array_index(types, VisuElement*, nzero) = g_array_index(types, VisuElement*, i);
	}
      index[i] = nzero;
      if (g_array_index(nattyp, guint, i) > 0)
	nzero += 1;
    }
  DBG_fprintf(stderr, "AB structure: removing null types.\n");
  for (i = 0; i < natom; i++)
    {
      DBG_fprintf(stderr, "AB structure: atom %d (%d)", i, typat[i] - 1);
      DBG_fprintf(stderr, " -> %d.\n", index[typat[i] - 1]);
      ntypes[i] = g_array_index(types, VisuElement*, index[typat[i] - 1]);
    }
  g_free(typat);
  g_free(index);
  ntypat = nzero;
  g_array_set_size(types, ntypat);
  g_array_set_size(nattyp, ntypat);

  DBG_fprintf(stderr, "AB structure: there are %d types in this file.\n", ntypat);
  if (DEBUG)
    for (i = 0; i < ntypat; i++)
      fprintf(stderr, " | %d atom(s) for type %d.\n",
              g_array_index(nattyp, guint, i), i);
  if (DEBUG)
    for (i = 0; i < natom; i++)
      fprintf(stderr, " | atom %d of type %p.\n", i, (gpointer)ntypes[i]);
  visu_node_array_allocate(VISU_NODE_ARRAY(data), types, nattyp);
  g_array_free(nattyp, TRUE);
  g_array_free(types, TRUE);

  error = ab_invars_get_real_array(dt, (double*)rprimd, 9,
				    AB_INVARS_RPRIMD_ORIG, nSet + 1);
  if (error != AB_NO_ERROR)
    {
      g_free(ntypes);
      return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
			 "Abinit loader report error:\n %s\nwhile getting attribute '%s'.",
			 ab_error_string_from_id(error),
			 AB_INVARS_STR(AB_INVARS_RPRIMD_ORIG));
    }
  DBG_fprintf(stderr, " | box definition : ( %f %f %f )\n",
	      rprimd[0][0], rprimd[0][1], rprimd[0][2]);
  DBG_fprintf(stderr, " |                  ( %f %f %f )\n",
	      rprimd[1][0], rprimd[1][1], rprimd[1][2]);
  DBG_fprintf(stderr, " |                  ( %f %f %f )\n",
	      rprimd[2][0], rprimd[2][1], rprimd[2][2]);
  boxObj = visu_box_new_full(rprimd, VISU_BOX_PERIODIC);
  if (visu_box_getGeometry(boxObj, VISU_BOX_DXX) == G_MAXFLOAT)
    {
      g_free(ntypes);
      return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_FORMAT,
                         _("Abinit loader report error:\n basis-set is degenerated.\n"));
    }

  visu_boxed_setBox(VISU_BOXED(data), VISU_BOXED(boxObj));
  g_object_unref(boxObj);
  
  coord = g_malloc(sizeof(double) * 3 * natom);
  error = ab_invars_get_real_array(dt, coord, 3 * natom,
				    AB_INVARS_XRED_ORIG, nSet + 1);
  if (error != AB_NO_ERROR)
    {
      g_free(ntypes);
      g_free(coord);
      return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
			 "Abinit loader report error:\n %s\nwhile getting attribute '%s'.",
			 ab6_error_string_from_id(error),
			 AB_INVARS_STR(AB_INVARS_XRED_ORIG));
    }
  for (i = 0; i < natom; i++)
    {
      red[0] = (float)*(coord + 3 * i + 0);
      red[1] = (float)*(coord + 3 * i + 1);
      red[2] = (float)*(coord + 3 * i + 2);
      DBG_fprintf(stderr, " |                  ( %f %f %f )\n",
		  red[0], red[1], red[2]);
      visu_data_addNodeFromElement(data, ntypes[i], red, TRUE, FALSE);
    }
  /* We reset the box size after we set all the coordinates to get the
     OpenGL box right. */
  visu_box_setMargin(boxObj, visu_node_array_getMaxElementSize(VISU_NODE_ARRAY(data)) +
                     visu_data_getAllNodeExtens(data, boxObj), TRUE);
  g_free(ntypes);
  g_free(coord);

  /* We set the units. */
  visu_box_setUnit(boxObj, TOOL_UNITS_BOHR);

  /* We store the spinat array as a property to be used later by the spin
     loading method. */
  spinat = g_malloc(sizeof(double) * 3 * natom);
  g_object_set_data_full(G_OBJECT(data), "ABINIT_spinat", (gpointer)spinat, g_free);
  error = ab_invars_get_real_array(dt, spinat, 3 * natom,
				    AB_INVARS_SPINAT, nSet + 1);
  if (error != AB_NO_ERROR)
    {
      g_free(spinat);
      return g_error_new(TOOL_FILE_FORMAT_ERROR, TOOL_FILE_FORMAT_ERROR_METHOD,
			 "Abinit loader report error:\n %s\nwhile getting attribute '%s'.",
			 ab6_error_string_from_id(error),
			 AB_INVARS_STR(AB_INVARS_SPINAT));
    }

  return (GError*)0;
}

static void abSpinInit(VisuRendering *method)
{
  const gchar *type[] = {"*.in", (char*)0};
  
  visu_rendering_addFileFormat(method, FILE_KIND_SPIN,
                               tool_file_format_new(_("ABINIT input file format"),
                                                    type),
                               95, loadAbinitSpin);
}

static void freeSpin(gpointer obj, gpointer data _U_)
{
#if GLIB_MINOR_VERSION > 9
  g_slice_free1(sizeof(float) * 3, obj);
#else
  g_free(obj);
#endif
}
static gpointer newOrCopySpin(gconstpointer obj, gpointer data _U_)
{
  float *spinData;

#if GLIB_MINOR_VERSION > 9
  spinData = g_slice_alloc(sizeof(float) * 3);
#else
  spinData = g_malloc(sizeof(float) * 3);
#endif
  if (obj)
    memcpy(spinData, obj, sizeof(float) * 3);
  else
    memset(spinData, 0, sizeof(float) * 3);
    
  return (gpointer)spinData;
}

static void initMaxModulus(VisuElement *ele _U_, GValue *val)
{
  DBG_fprintf(stderr, " | init max modulus of val %p.\n", (gpointer)val);
  g_value_init(val, G_TYPE_FLOAT);
  g_value_set_float(val, -G_MAXFLOAT);
}

static gboolean loadAbinitSpin(VisuData *data, const gchar* filename,
			       ToolFileFormat *format _U_, int nSet _U_,
                               GCancellable *cancel _U_, GError **error)
{
  double *spinat;
  float spins[3];
  float *svgSpinValues;
  float sph[3], vals[3];
  VisuNodeProperty *spin;
  VisuNodeArrayIter iter;
  GValue spinValue = {0, {{0}, {0}}};
  GValueArray *svgMaxSpinModulus;
  GValue *val;

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

  spinat = (double*)g_object_get_data(G_OBJECT(data), "ABINIT_spinat");
  if (!spinat)
    return FALSE;

  /* We check that spin and position are the same. */
  /* TODO... */

  /* Create a storage for max values of spin modulus for each element. */
  svgMaxSpinModulus = visu_node_array_setElementProperty(VISU_NODE_ARRAY(data),
                                                         VISU_RENDERING_SPIN_MAX_MODULUS_ID,
                                                         initMaxModulus);
  spin = visu_node_array_property_newPointer(VISU_NODE_ARRAY(data), VISU_RENDERING_SPIN_VALUES_ID,
                                       freeSpin, newOrCopySpin, (gpointer)0);

  g_value_init(&spinValue, G_TYPE_POINTER);
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for(visu_node_array_iterStartNumber(VISU_NODE_ARRAY(data), &iter); iter.node;
      visu_node_array_iterNextNodeNumber(VISU_NODE_ARRAY(data), &iter))
    {
      spins[0] = (float)(spinat + iter.node->number * 3)[0];
      spins[1] = (float)(spinat + iter.node->number * 3)[1];
      spins[2] = (float)(spinat + iter.node->number * 3)[2];
      tool_matrix_cartesianToSpherical(sph, spins);
      vals[TOOL_MATRIX_SPHERICAL_MODULUS] = sph[0];
      vals[TOOL_MATRIX_SPHERICAL_THETA]   = sph[1];
      vals[TOOL_MATRIX_SPHERICAL_PHI]     = sph[2];
      svgSpinValues = newOrCopySpin(vals, (gpointer)0);
      g_value_set_pointer(&spinValue, svgSpinValues);
      visu_node_property_setValue(spin, iter.node, &spinValue);
      val = g_value_array_get_nth(svgMaxSpinModulus, iter.iElement);
      g_value_set_float(val, MAX(vals[TOOL_MATRIX_SPHERICAL_MODULUS],
                                 g_value_get_float(val)));
    }

  /* We kill the temporary spinat property. */
  g_free(g_object_steal_data(G_OBJECT(data), "ABINIT_spinat"));

  /* Everything is OK. */
  *error = (GError*)0;
  return TRUE;
}
#endif
