/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD, Damien
	CALISTE, Olivier D'Astier, laboratoire L_Sim, (2001-2006)
  
	Adresses mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam 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 and Damien
	CALISTE and Olivier D'Astier, laboratoire L_Sim, (2001-2006)

	E-mail addresses :
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "renderingSpin.h"
#include "renderingAtomic.h" /* To get the position file format
				(and posibly in the future, the read
				method for position) */
#include "renderingAtomic_ascii.h"
#include "renderingAtomic_d3.h"
#include "atomic_xyz.h"
#include <visu_commandLine.h>
#include <visu_configFile.h>
#include <math.h>
#include <assert.h>
#include <coreTools/toolFileFormat.h>
#include <openGLFunctions/objectList.h>
#include <opengl.h>

#define FLAG_RESOURCES_SPIN "spin_resources"
#define DESC_RESOURCES_SPIN "Global or element resource for rendering spin module"

/* An identifier for the different shapes to draw elements. */
enum
  {
    shapeArrow1 = 0,
    shapeArrow2, 
    nb_of_shapes
  };

/* Names as they appear in the gtk combo box when selecting an element shape. */
/* tab[i] element matches the above enum i-th element. */
const char* shapeNameSpin[3] = {"Rounded", 
				"Edged", 
				(const char*)0};


/* The OpenGL identifier to store the glObjectLists that
   describe the atoms. */
int identifierSpheresSpin;

int loadSpin(VisuData *data, GString **errormessage);
gboolean readSpinResources(gchar **lines, int nbLines,
			   int position, GString *errorMessage);
gboolean exportResourcesRenderingSpin(GString *data, int *nbLinesWritten);
void setVariablesForSpinMethod(GObject *visu, gpointer data);

int rspin_get_number_of_shapes()
{
  return nb_of_shapes;
}

int rspin_shape_name_to_number(const char *name)
{
  int i=0;
  if(name == NULL) 
    return -1;

  for(i=0; i<nb_of_shapes; i++)
    if(strcmp(name, shapeNameSpin[i]) == 0)
      return i;

  return -1;
}

const char* rspin_shape_number_to_name(int n)
{
  if(n>=nb_of_shapes && n < 0)
    return NULL;
  
  return shapeNameSpin[n];
}

float returnOneF()
{
  return 1.0;
}

/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/
/* This function is responsible for binding the module with the core. A module NEED */
/* a function of this kind, and it has to be added in the externalRenderingMethods.h file */
/* for the program to know it exists. */ 
RenderingMethod* initSpin()
{
  RenderingMethod *spin = NULL;
  /* Name & desc that appear when selecting the "input method" in the config tab. */
  char *methodName = _("Spin visualisation");
  char *methodDescr = _("It draws arrows at given positions to represent an atom "
			"and its spin.");
  /* The file types this module support and a description for each of these. */
  char *typeSpin[] = {"*.spin", NULL};
  char *descrSpin = _("Spin files");

  FileFormat *fmt = NULL;
  GList *formatListPos = NULL;
  GList *formatListSpin = NULL;

  /* A pointer to store the location of the icon as it is shown in the config menu. */
  gchar *iconPath;

  /* The names & descs of the resources the module supports. */
  /* A resource is something that can be modified according to user prefs. */
  /* For example : drawing atoms as cubes instead of drawing them as spheres. */
  VisuConfigFileEntry *resourceEntry;


  DBG_fprintf(stderr,"Initialising the spin rendering method...\n");

  /* Adding the supported filetype(s) to the list of known file types by the filebrowser. */
  if (!pointerOnRenderingAtomicMethod)
    g_error("Can't initialize the RenderingSpin method,"
	      " renderingAtomic must be initialized first. Aborting...\n");

  formatListPos = renderingMethodGet_fileType(pointerOnRenderingAtomicMethod, 0);
  fmt = fileFormatNew(descrSpin, typeSpin);
  if (!fmt)
    g_error("Can't initialize the rendering spin method, aborting...\n");

  formatListSpin = g_list_append(formatListSpin, (gpointer)fmt);

  /* Calling the constructor to create a new renderingMethod. */
  spin = renderingMethod_new("Spin visualisation", methodName, methodDescr, 2, loadSpin);
  renderingMethodSet_fileType(spin, formatListPos, FILE_KIND_POSITION, _("Position files"));
  renderingMethodSet_fileType(spin, formatListSpin, FILE_KIND_SPIN, _("Spin files"));

  /* Setting up the icon. */
  iconPath = g_build_filename(v_sim_pixmaps_dir, "stock_spin.png", NULL);
  renderingMethodSet_icon(spin, iconPath);
  g_free(iconPath);

  /* Binding this new method with OpenGL drawing funcs. */
  setOpenGLMethods(spin, rspin_createShapeSpin, rspin_placeNodeSpin, returnOneF);

  /* Ok now it's time to create and register this module as a new one using the variables
     we initialised above. */
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_RESOURCE,
					  FLAG_RESOURCES_SPIN,
					  DESC_RESOURCES_SPIN,
					  1, readSpinResources);
  visuConfigFileAdd_exportFunction(VISU_CONFIGFILE_RESOURCE,
				   exportResourcesRenderingSpin);
  

  /* Initialise a GHashTable to store the resources relatively to ONE element.
     Since the key is the name of the element and
     is a pointer to element->name it must not be
     freed when the table is destroyed. */
/*   SpinResources = g_hash_table_new_full(g_direct_hash, g_direct_equal, */
/* 					    NULL, freeSpinStruct); */
/*   if (!SpinResources) */
/*     { */
/*       allocationProblems(); */
/*       renderingMethod_free(spin); */
/*       return (RenderingMethod*)0; */
/*     } */

  /* Initialise the OpenGl part by reserving space in the display list to draw objects. */
  identifierSpheresSpin = openGLObjectList_new(NMAX_TP);
  *(int *)rspin_global_resource_get("hiding_mode") = commandLineGet_spinHidingMode();

  /* We set a general pointer to retrieve this method later. */
  pointerOnRenderingSpinMethod = spin;

/*   /\* In case elements already exists (if the atom module was previously loaded for example), */
/*      it's necessary to create ressources for each known element *\/ */
/*   if(differentVisuElements != NULL) */
/*     { */
/*       DBGspin0("some elements exists in the hash table...\n"); */
/*       DBGspin0("...registering default resources for these elements\n"); */
/*       g_hash_table_foreach(differentVisuElements, init_loaded_elements_resources, NULL); */
/*     } */

  /* Connect to the signal method changed to set or unset some variables
     when spin is current or not. */
  g_signal_connect(G_OBJECT(visu), "renderingChanged",
		   G_CALLBACK(setVariablesForSpinMethod), (gpointer)spin);


  return spin;
}

void setVariablesForSpinMethod(GObject *visu, gpointer data)
{
  if (getRenderingMethodInUse() == (RenderingMethod*)data)
    visuElementSet_updateNodesOnMaterialChange();
  else
    visuElementUnset_updateNodesOnMaterialChange();
}


/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/
/* The following are the methods responsible of dealing with the reading of files. */
/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/

/* This is the method associated to the reading of supported position files. */
static int read_position_file(VisuData *data, const char* fileName, VisuIO *io)
{
  int res;
  GString *tmp;
  FILE *readFrom;

  tmp = g_string_new(io->informations->str);

  DBG_fprintf(stderr,"Rendering Spin : testing '%s' as a spin file.\n", fileName);

  readFrom = fopen(fileName, "r");
  if (!readFrom)
    {
      g_string_append_printf(io->informations, _("WARNING : impossible to open this file.\n"));
      return 0;
    }
  io->flux = &readFrom;

  res = read_d3_file(data, io);
  fclose(*io->flux);
  if (res < 0)
    {
      DBG_fprintf(stderr,"Rendering Atomic : it is not a d3 file, trying as ascii file.\n");
      /* We don't have a D3 file.
	 We test with a ascii one. */
      g_string_free(io->informations, TRUE);
      io->informations = tmp;
      tmp = g_string_new(io->informations->str);

      readFrom = fopen(fileName, "r");
      if (!readFrom)
	{
	  g_string_append_printf(io->informations, _("WARNING : impossible to open this file.\n"));
	  return 0;
	}
      io->flux = &readFrom;
      res = read_ascii_file(data, io);
      fclose(*io->flux);
      if(res < 0)
	{
	  DBG_fprintf(stderr,"Rendering Atomic : it is not an ascii file,"
		      " trying as xyz file.\n");
	  g_string_free(io->informations, TRUE);
	  io->informations = tmp;
	  tmp = g_string_new(io->informations->str);

	  readFrom = fopen(fileName, "r");
	  if (!readFrom)
	    {
	      g_string_append_printf(io->informations,
				     _("WARNING : impossible to open this file.\n"));
	      return 0;
	    }
	  io->flux = &readFrom;
	  res = read_xyz_file(data, io);
	  fclose(*io->flux);
	  if (res)
	    return 0;
	}
      else if (res > 0)
	return 0;
    }
  else if (res > 0)
    return 0;

  g_string_free(tmp, TRUE);
  DBG_fprintf(stderr,"Rendering Spin : loading OK.\n");

  return 1;
}

/* This is the method associated to the reading of supported spin files. */
static int read_spin_file(VisuData *data, const char* fileName, VisuIO *io)
{
  char line[MAX_LINE_LENGTH] = "\0";
  float theta=0, phi=0, ftrash = 0, module = 0;
  int i=0, itrash = 0;
  VisuNode *currentNode = NULL;
  FILE *readFrom = fopen(fileName, "r");

  if (!readFrom)
    {
      g_string_append_printf(io->informations, _("WARNING : impossible to open this file.\n"));
      return 0;
    }
  io->flux = &readFrom;

  /* The first line is a commentry. */
  fgets(line, MAX_LINE_LENGTH, *io->flux);
  if(feof(*io->flux))
    {
      g_string_append_printf(io->informations, _("WARNING : the ASCII file should"
						 " contain more than 3 lines\n"));
      /* The file doesn't conform to ascii norm, we exit with a wrong type flag. */
      return 0;
    }
   
  for(i=1;i<=data->nbOfAllStoredNodes;i++)
    {
      fgets(line, MAX_LINE_LENGTH, *io->flux);
      if(feof(*io->flux)) break;

      currentNode = visuDataGet_nodeFromNumber(data, i);
      if(currentNode == 0) 
	{
	  fprintf(stderr, 
		  _("\nWARNING : information for node number %d can't be retrieved\n\n"),
		  i);
	  continue;
	}
      
      if(sscanf(line, "%d %f %f %f", &itrash, &module, &theta, &phi) != 4)
	{
	  fprintf(stderr,
		  _("\nWARNING : line number #%d invalid..."
		    "Setting node parameters to default ones...\n\n"),
		  i);
	  visuNodeSet_thetaPhi(currentNode, 0, 0);
	  continue;
	}      

      visuNodeSet_thetaPhi(currentNode, theta, phi);
      if(*(int *)rspin_global_resource_get("hiding_mode") == TRUE)
	if(module <= 0.)
	  visuElementSet_rendered(data->fromIntToVisuElement[currentNode->posElement], FALSE);
    }
  return 1;
}

/**
 * loadSpin:
 *
 * @data: a pointer to the current visuData
 * @errormessage: a buffer in case errors happens
 *
 * This is a function binded to the core. It's automatically called
 * whenever a new set of (data+spin) files is loaded.
 */
int loadSpin(VisuData *data, GString **errormessage)
{
  gchar *file, *fileUTF8, *spinFile;
  VisuIO io;

  if (errormessage)
    *errormessage = (GString*)0;

  if (!data)
    return 0;
  
  DBG_fprintf(stderr, "starting loadSpin procedure...\n");
  io.informations = g_string_new("");

  /* Opening gtk selected files */
  file = visuDataGet_file(data, FILE_KIND_POSITION);
  if (!file)
    {
      g_string_append_printf(io.informations, "No file name available.");
      if (errormessage)
	*errormessage = io.informations;
      return 0;
    }

  spinFile = visuDataGet_file(data, FILE_KIND_SPIN);
  if (!spinFile)
    {
      g_string_append_printf(io.informations, "No file name available.");
      if (errormessage)
	*errormessage = io.informations;
      return 0;
    }
 
  fileUTF8 = getStringInUTF8(file);
  g_string_append_printf(io.informations, "Rendering Spin : Loading data file '%s'...\n\n", fileUTF8);
  fileUTF8 = getStringInUTF8(spinFile);
  g_string_append_printf(io.informations, "Rendering Spin : Loading spin file '%s'...\n\n", fileUTF8);
  g_free(fileUTF8);
  DBG_fprintf(stderr, "trying to load these 2 files :\n- %s\n- %s \n", file, spinFile);

  io.nbLine = malloc(sizeof(int));
  *io.nbLine = 0;

  /* Testing each file's validity */
  if (g_file_test(file, G_FILE_TEST_IS_DIR)
      || g_file_test(spinFile, G_FILE_TEST_IS_DIR))
    {
      g_string_append_printf(io.informations, 
			     _("WARNING! The specified file is actually a "
			       "directory... Aborting...\n"));
      if (errormessage)
	*errormessage = io.informations;
      return 0;
    }

  if(!read_position_file(data, (char*)file, &io) 
     || !read_spin_file(data, (char *)spinFile, &io))
    {
      if (errormessage)
	*errormessage = io.informations;
      return 0;
    }
  else
    {
      if (errormessage)
	*errormessage = io.informations;
    }

  

/*   g_hash_table_foreach(differentVisuElements, init_loaded_elements_resources, NULL); */

  DBG_fprintf(stderr, "loading OK.\n");
  return 1;
}


/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/
/* Next comes the functions that deals with resources                        */
/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/

/* See header file for more info */
gpointer rspin_global_resource_get(const char* property)
{
  static GHashTable *rspin_global_resources = NULL;

  if(rspin_global_resources == NULL)
    {
      float *cone_theta_angle = g_malloc(sizeof(float));
      float *cone_phi_angle = g_malloc(sizeof(float));
      float *color_wheel_angle = g_malloc(sizeof(float));
      int *hiding_mode = g_malloc(sizeof(int));
      
      DBG_fprintf(stderr, "Rendering Spin : initializing new hash table to store global resources\n");
      rspin_global_resources = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
      
      DBG_fprintf(stderr, "Rendering Spin : registering new global resources\n");
      g_hash_table_insert(rspin_global_resources, "cone_theta_angle", cone_theta_angle);
      g_hash_table_insert(rspin_global_resources, "cone_phi_angle", cone_phi_angle);
      g_hash_table_insert(rspin_global_resources, "color_wheel_angle", color_wheel_angle);
      g_hash_table_insert(rspin_global_resources, "hiding_mode", hiding_mode);
      
      DBG_fprintf(stderr, "Rendering Spin : setting \"cone_theta_angle\" resource to 0\n");
      DBG_fprintf(stderr, "Rendering Spin : setting \"cone_phi_angle\" resource to 0\n");
      DBG_fprintf(stderr, "Rendering Spin : setting \"color_wheel_angle\" resource to 0\n");
      DBG_fprintf(stderr, "Rendering Spin : setting \"hiding_mode\" resource to 1\n");
      *cone_theta_angle = 0;
      *cone_phi_angle = 0;
      *color_wheel_angle = 0;
      *hiding_mode = 1;
    }
      
  if(property == NULL)
    {
      DBG_fprintf(stderr, "WARNING! 'rspin_global_resource_get' has been called with"
	      " a null data parameter or a null key parameter.\n");
      return NULL;
    }
  
  return g_hash_table_lookup(rspin_global_resources, property);
}

/* See header file for more info */
const void * rspin_get_element_resource(VisuElement *ele, const char* property)
{
  if(visuElementGet_property(ele, "element_shape_number") == NULL)
    {
      int *element_shape_number = g_malloc(sizeof(int));
      float *element_length = g_malloc(sizeof(float));
      float *element_u_length = g_malloc(sizeof(float));
      float *element_height  = g_malloc(sizeof(float));
      float *element_u_height = g_malloc(sizeof(float));
      int *use_element_color = g_malloc(sizeof(int));
      int *use_element_color_hat = g_malloc(sizeof(int));
      
      if(ele == NULL)
	{
	  DBG_fprintf(stderr, "WARNING : trying to initialize resource for (NULL) element\n");
	  return NULL;
	}
      
      DBG_fprintf(stderr, "allocating space for element '%s' resources\n", ele->name);
      visuElementSet_property(ele, "element_shape_number", element_shape_number);
      visuElementSet_property(ele, "element_length", element_length);
      visuElementSet_property(ele, "element_u_length", element_u_length);
      visuElementSet_property(ele, "element_height", element_height);
      visuElementSet_property(ele, "element_u_height", element_u_height);
      visuElementSet_property(ele, "use_element_color", use_element_color);
      visuElementSet_property(ele, "use_element_color_hat", use_element_color_hat);
      
      *element_shape_number = 0;
      *element_length = 0.8;
      *element_u_length = 0.33;
      *element_height = 2.;
      *element_u_height = 0.8;
      *use_element_color = 0;
      *use_element_color_hat = 0;
    }

  return visuElementGet_property(ele, (char *)property);  
}

/* See header file for more info */
void rspin_set_element_resources_f(VisuElement *ele, float new_value, const char* property)
{
  float *old_value = (float *)rspin_get_element_resource(ele, property);
  if(old_value == NULL || ele == NULL || property == NULL)
    return;

  DBG_fprintf(stderr, "Rendering spin : updating %s property for %s element : %f -> %f\n",
	      property, ele->name, *old_value, new_value);
  *old_value = new_value;
}

/* See header file for more info */
void rspin_set_element_resources_d(VisuElement *ele, int new_value, const char* property)
{
  int *old_value = (int *)rspin_get_element_resource(ele, property);
  if(old_value == NULL || ele == NULL || property == NULL)
    return;

  DBG_fprintf(stderr, "updating %s property for %s element...\n", property, ele->name);
  DBG_fprintf(stderr, "...old value : %d, new value : %d\n", *old_value, new_value);
  *old_value = new_value;
}  



/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/
/* Ok now we introduce the functions used to load/save resources of our module. */
/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/

/** 
 * readSpinResources:
 * @lines: an array of strings ;
 * @nbLines: an integer ;
 * @position: an integer ;
 * @errorMessage: a pointer to a GString or NULL.
 *
 * Returns: 1 if an error occured, 0 otherwise.
 *
 * This is the function called by the core when a spin resource flag is found
 * while reading a resource file. It tries to read, extract and set the resources 
 * it reads from the given file.
 */ 
gboolean readSpinResources(gchar **lines, int nbLines,
			   int position, GString *errorMessage)
{
  int boolean_true = 1;
  char line[MAX_LINE_LENGTH];
  char shape[MAX_LINE_LENGTH];
  float length, u_length, height, u_height;
  char *cursor;
  VisuElement* ele = NULL;
  int *temp;
  int shape_number, errorFlag;
  int use_color_element, use_element_color_hat;
  int i;
  char current_global_resource[32] = "\0";
  float global_resource_value = 0, *global_resource_location=NULL;

  cursor = lines[0];

  /* Retreving global resources */
  if(sscanf(lines[0], "%s %f", current_global_resource, &global_resource_value) == 2)
    {
      if(!(strcmp(current_global_resource, "cone_phi_angle") == 0
	   || strcmp(current_global_resource, "cone_theta_angle") == 0
	   || strcmp(current_global_resource, "color_wheel_angle") == 0))
	{
	  if (errorMessage)
	    g_string_append_printf(errorMessage, 
				   _("WARNING! Parse error at line %d : global resource %s "
				     "unknown."),
				   position, current_global_resource);
	  return FALSE;
	}

      global_resource_location = rspin_global_resource_get(current_global_resource);
      if(global_resource_location == NULL) 
	return FALSE;
      *global_resource_location = global_resource_value;
      return TRUE;
    }

  /* Retrieving concerned element */
  if (!(ele = visuElementGet_fromValueLine(&cursor, MAX_LINE_LENGTH, &errorFlag)))
    {
      if (errorMessage)
	visuElementPrint_errorFromValueLine(errorMessage, position, errorFlag);
      return FALSE;
    }
  
  /* Retrieving element's resources */
  if(sscanf(cursor, "%s %f %f %f %f %d %d", shape, 
	     &height, &u_height, &length, &u_length, 
	     &use_color_element, &use_element_color_hat) != 7
     || length <= 0. || u_length <= 0. 
     || height <= 0. || u_height <= 0.)
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, 
			       _("WARNING! Parse error at line %d : 4 valid floating point "
				 "values and a shape name must appear after the %s markup.\n"),
			       position, FLAG_RESOURCES_SPIN);
      return FALSE;
    }


  if((shape_number = rspin_shape_name_to_number(shape)) == -1)
    if (errorMessage)
      g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d," 
					     " the shape '%s' is unknown.\n"), 
			     position, shape); 


  rspin_set_element_resources_d(ele, shape_number, "element_shape_number");
  rspin_set_element_resources_f(ele, height, "element_height");
  rspin_set_element_resources_f(ele, u_height, "element_u_height");
  rspin_set_element_resources_f(ele, length, "element_length");
  rspin_set_element_resources_f(ele, u_length, "element_u_length");
  rspin_set_element_resources_d(ele, use_color_element, "use_element_color");
  rspin_set_element_resources_d(ele, use_element_color_hat, "use_element_color_hat");
  DBG_fprintf(stderr, "resources set successfully for element '%s'\n", ele->name);

  return TRUE;
}

/**
 * write_global_resource:
 * @data: an empty GString to store the export ;
 * @nbLinesWritten: a pointer to store the number of lines written
 * @global_resource_name: the name of the global resource to write
 *
 * Writes the given global resource specified by his @global_resource_name
 * to the @io flux given in parameter.
 */
void write_global_resource(GString *data, int *nbLinesWritten, char *global_resource_name)
{
  float *value = rspin_global_resource_get(global_resource_name);

  if(value == NULL || global_resource_name == NULL)
    return;

  g_string_append_printf(data, "%s:\n", FLAG_RESOURCES_SPIN);

  *nbLinesWritten += 1;

  g_string_append_printf(data, "   %s %f\n", global_resource_name, *value);
 
  *nbLinesWritten += 1;
}

/**
 * write_element_resources:
 * @data: an empty GString to store the export ;
 * @nbLinesWritten: a pointer to store the number of lines written ;
 * @ele: a #VisuElement.
 *
 * Writes the given element resources to the current file given yb the
 * VisuIO element. This function is to be called through the
 * g_hash_table_foreach primitive, so that explains it's weird prototype.
 */
void write_element_resources(GString *data, int *nbLinesWritten, VisuElement* ele)
{
  char *name = ele->name;
  const int *element_shape_number = rspin_get_element_resource(ele, "element_shape_number");
  const float *u_length = rspin_get_element_resource(ele, "element_u_length");
  const float *length = rspin_get_element_resource(ele, "element_length");
  const float *u_height = rspin_get_element_resource(ele, "element_u_height");
  const float *height = rspin_get_element_resource(ele, "element_height");
  const int *use_element_color = rspin_get_element_resource(ele, "use_element_color");
  const int *use_element_color_hat = rspin_get_element_resource(ele, "use_element_color_hat");

/*    assert((key != NULL) && (value != NULL) && (data != NULL));  */
/*    assert(strncmp(ele->name, name, 2) == 0);  */
   DBG_fprintf(stderr, "%s...\n", name); 

  g_string_append_printf(data, "%s:\n", FLAG_RESOURCES_SPIN);

  *nbLinesWritten += 1;     

  g_string_append_printf(data, "   %s %s %f %f %f %f %d %d\n",
			 name,	      
			 rspin_shape_number_to_name(*element_shape_number),
			 *height, *u_height,
			 *length, *u_length,
			 *use_element_color, *use_element_color_hat);
  
  *nbLinesWritten += 1;     
}

/**
 * exportResourcesRenderingSpin:
 * @data: an empty GString to store the export ;
 * @nbLinesWritten: a pointer to store the number of lines written.
 *
 * This function is called by the core whenever it's needed to export
 * resources. It's in charge of exporting ALL the spin module resources.
 * First it writes global resources in the given @io structure and then
 * writes each element resources using @write_element_resources
 *
 * Returns: TRUE if everything goes right, FALSE otherwise.
 */
gboolean exportResourcesRenderingSpin(GString *data, int *nbLinesWritten)
{
  GList *pos;

  DBG_fprintf(stderr, "exporting element resources...\n");

  g_string_append_printf(data, "# %s\n", DESC_RESOURCES_SPIN);
  *nbLinesWritten = 1;

  write_global_resource(data, nbLinesWritten, "cone_phi_angle");
  write_global_resource(data, nbLinesWritten, "cone_theta_angle");
  write_global_resource(data, nbLinesWritten, "color_wheel_angle");
  
  pos = visuElementGet_allElements();
  while(pos)
    {
      write_element_resources(data, nbLinesWritten, (VisuElement*)pos->data);
      pos = g_list_next(pos);
    }
/*   g_hash_table_foreach(differentVisuElements, write_element_resources, io); */

  g_string_append_printf(data, "\n");
  *nbLinesWritten += 1;

  DBG_fprintf(stderr, "element resources succesfully exported\n");
  return TRUE;
}

/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/
/***************/
/* OpenGL part */
/***************/
/*****************************************************************************/
/*                                                                           */
/*****************************************************************************/
static void drawRoundedHat(float hat_length, float hat_height, float radius, int nlat)
{
  gluCylinder(obj, hat_length, 0, hat_height, nlat, 1);
  glRotatef(180, 1, 0, 0);
  gluDisk(obj, 0, radius, nlat, 1);
  glRotatef(180, 1, 0, 0);
}
static void drawRoundedTail(float tail_length, float tail_height, int nlat)
{
  glTranslatef(0, 0, -tail_height);
  glRotatef(180, 1, 0, 0);
  gluDisk(obj, 0, (tail_length)*1.03, nlat, 1);      
  glRotatef(180, 1, 0, 0);
  gluCylinder(obj, tail_length, tail_length, tail_height, nlat, 1);
}
static void drawEdgedHat(float hat_length, float hat_height)
{
  glBegin(GL_TRIANGLE_FAN);
      
  glNormal3f(hat_height, 0, hat_length);
  glVertex3f(0, 0, hat_height); 
  glVertex3f(hat_length,-hat_length,0);
  glVertex3f(hat_length,hat_length,0);

  glNormal3f(0, hat_height, hat_length);
  glVertex3f(-hat_length,hat_length,0);
       
  glNormal3f(-hat_height, 0, hat_length);
  glVertex3f(-hat_length,-hat_length,0);

  glNormal3f(-hat_height, 0, hat_length);
  glVertex3f(hat_length,-hat_length,0);

  glEnd();

  glBegin(GL_QUADS);

  glNormal3f(0., 0., -1.);
  glVertex3f(hat_length ,hat_length ,0 );
  glVertex3f(hat_length ,-hat_length ,0 );
  glVertex3f(-hat_length ,-hat_length ,0 );
  glVertex3f(-hat_length ,hat_length ,0 );

  glEnd();

}
static void drawEdgedTail(float tail_length, float tail_height)
{
  glBegin(GL_QUADS);

  glNormal3f(0., 0., 1.);
  glVertex3f(tail_length , tail_length , 0 );
  glVertex3f(-tail_length , tail_length , 0 );
  glVertex3f(-tail_length , -tail_length , 0 );
  glVertex3f(tail_length , -tail_length , 0 );

  glNormal3f(0., 0., -1.);
  glVertex3f(tail_length ,tail_length ,-tail_height );
  glVertex3f(tail_length ,-tail_length ,-tail_height );
  glVertex3f(-tail_length ,-tail_length ,-tail_height );
  glVertex3f(-tail_length ,tail_length ,-tail_height );

  glNormal3f(1., 0., 0.);
  glVertex3f(tail_length ,tail_length ,0 );
  glVertex3f(tail_length ,-tail_length ,0 );
  glVertex3f(tail_length ,-tail_length ,-tail_height );
  glVertex3f(tail_length ,tail_length ,-tail_height );

  glNormal3f(-1., 0., 0.);
  glVertex3f(-tail_length ,tail_length ,0 );
  glVertex3f(-tail_length ,tail_length ,-tail_height );
  glVertex3f(-tail_length ,-tail_length ,-tail_height );
  glVertex3f(-tail_length ,-tail_length ,0 );

  glNormal3f(0., 1., 0.);
  glVertex3f(-tail_length ,tail_length ,-tail_height );
  glVertex3f(-tail_length ,tail_length ,0 );
  glVertex3f(tail_length ,tail_length ,0 );
  glVertex3f(tail_length ,tail_length ,-tail_height );

  glNormal3f(0., -1., 0.);
  glVertex3f(-tail_length ,-tail_length ,-tail_height );
  glVertex3f(tail_length ,-tail_length ,-tail_height );
  glVertex3f(tail_length ,-tail_length ,0 );
  glVertex3f(-tail_length ,-tail_length ,0 );

  glEnd();
}

void rspin_createShapeSpin(OpenGLView *view, VisuElement* ele)
{
  int nlatl=0, nlatul=0, nlatoh=0;
  int shape_number = *(const int *)rspin_get_element_resource(ele, "element_shape_number");
  int use_element_color_tail = *(const int *)rspin_get_element_resource(ele, "use_element_color");
  int use_element_color_hat = *(const int *)rspin_get_element_resource(ele, "use_element_color_hat");
  float tail_length = *(const float *)rspin_get_element_resource(ele, "element_u_length");
  float hat_length = *(const float *)rspin_get_element_resource(ele, "element_length");
  float tail_height = *(const float *)rspin_get_element_resource(ele, "element_u_height");
  float hat_height = *(const float *)rspin_get_element_resource(ele, "element_height");

  g_return_if_fail(view && ele);
 
  nlatul = OpenGLViewGet_numberOfFacettes(view, tail_length) ;
  nlatl = OpenGLViewGet_numberOfFacettes(view, hat_length);
  nlatoh = OpenGLViewGet_numberOfFacettes(view, hat_height);
  
  DBG_fprintf(stderr, "Rendering Spin : creating arrow for %s,  nlatl = %d, nlatul = %d, nlatoh = %d\n", 
	      ele->name, nlatl, nlatul, nlatoh);

  glNewList(identifierSpheresSpin + ele->typeNumber, GL_COMPILE);
  glTranslatef(0, 0, -(hat_height-tail_height)/2.);
  switch (shape_number)
    {
    case shapeArrow1:
      if(use_element_color_tail && use_element_color_hat)
	{
	  glCallList(visuElementGet_identifierMaterial(ele));
	  drawRoundedHat(hat_length, hat_height, MAX(hat_length, tail_length)*1.03, nlatl);
	  drawRoundedTail(tail_length, tail_height, nlatul);
	}
      else if(use_element_color_tail)
	{
	  drawRoundedHat(hat_length, hat_height, MAX(hat_length, tail_length)*1.03, nlatl);
	  glCallList(visuElementGet_identifierMaterial(ele));
	  drawRoundedTail(tail_length, tail_height, nlatul);
	}
      else if(use_element_color_hat)
	{
	  drawRoundedTail(tail_length, tail_height, nlatul);
	  glTranslatef(0, 0, tail_height);
	  glCallList(visuElementGet_identifierMaterial(ele));
	  drawRoundedHat(hat_length, hat_height, MAX(hat_length, tail_length)*1.03, nlatl);
	}
      else
	{
	  drawRoundedHat(hat_length, hat_height, MAX(hat_length, tail_length)*1.03, nlatl);
	  drawRoundedTail(tail_length, tail_height, nlatul);
	}
      break;
    case shapeArrow2:
      /* This one is a edged arrow. */
      if(use_element_color_tail && use_element_color_hat)
	{
	  glCallList(visuElementGet_identifierMaterial(ele));
	  drawEdgedHat(hat_length, hat_height);
	  drawEdgedTail(tail_length, tail_height);
	}
      else if(use_element_color_tail)
	{
	  drawEdgedHat(hat_length, hat_height);
	  glCallList(visuElementGet_identifierMaterial(ele));
	  drawEdgedTail(tail_length, tail_height);
	}
      else if(use_element_color_hat)
	{
	  drawEdgedTail(tail_length, tail_height);
	  glCallList(visuElementGet_identifierMaterial(ele));
	  drawEdgedHat(hat_length, hat_height);
	}
      else
	{
	  drawEdgedHat(hat_length, hat_height);
	  drawEdgedTail(tail_length, tail_height);
	}
      break;
    default:
      DBG_fprintf(stderr, "RenderingSpin : ERROR while drawing objects... Aborting...");
      exit(1);
      break;
    }
  glEndList();
   
  ele->openGLIdentifier = identifierSpheresSpin + ele->typeNumber;
}

/* This is a method called before the drawing of each node. It's needed to set the
   right positions of each atom but you can apply other treatments if you need.
   Here we also rotate our atom according to the spin theta and phi parameters. */
#ifndef PI
#define PI 3.1415926
#endif

#ifndef DEG2RAD
#define DEG2RAD(x) ((PI/180.)*x)
#endif

void rspin_placeNodeSpin(VisuData *visuData, VisuNode *node, VisuElement* ele)
{
  /* This is the part responsible for the colorisation of
     the atoms according to their spin. */
  float mm[4]; 
  float hsl[3];
  float rgba[4];
  float xyz[3];
  int i;

  float *phi_prime_zero = rspin_global_resource_get("color_wheel_angle");
  float *phi_a = rspin_global_resource_get("cone_phi_angle");
  float *theta_a = rspin_global_resource_get("cone_theta_angle");

  float theta = node->theta;
  float phi = node->phi;
  
  float theta_prime;
  float phi_prime;
  /*début rajout*/
  float spherical[3];
  float matrix_rot_theta[9];
  float matrix_rot_mtheta[9];
  float matrix_rot_phi[9]; 
  float cartesian[3]; 
  float cartesian_prime[3];
  float cartesian_second[3];
  float cartesian_ter[3];
  OpenGLView *view;

  spherical[0] = 1;
  spherical[1] = theta;
  spherical[2] = phi;

  matrix_rot_mtheta[0] = cos(DEG2RAD(-*theta_a));
  matrix_rot_mtheta[1] = 0;
  matrix_rot_mtheta[2] = -sin(DEG2RAD(-*theta_a));
  matrix_rot_mtheta[3] = 0;
  matrix_rot_mtheta[4] = 1;
  matrix_rot_mtheta[5] = 0;
  matrix_rot_mtheta[6] = sin(DEG2RAD(-*theta_a));
  matrix_rot_mtheta[7] = 0;
  matrix_rot_mtheta[8] = cos(DEG2RAD(-*theta_a));

  matrix_rot_theta[0] = cos(DEG2RAD(*theta_a));
  matrix_rot_theta[1] = 0;
  matrix_rot_theta[2] = -sin(DEG2RAD(*theta_a));
  matrix_rot_theta[3] = 0;
  matrix_rot_theta[4] = 1;
  matrix_rot_theta[5] = 0;
  matrix_rot_theta[6] = sin(DEG2RAD(*theta_a));
  matrix_rot_theta[7] = 0;
  matrix_rot_theta[8] = cos(DEG2RAD(*theta_a));

  matrix_rot_phi[0] = cos(DEG2RAD(-*phi_a));
  matrix_rot_phi[1] = -sin(DEG2RAD(-*phi_a));
  matrix_rot_phi[2] = 0;
  matrix_rot_phi[3] = sin(DEG2RAD(-*phi_a));
  matrix_rot_phi[4] = cos(DEG2RAD(-*phi_a));
  matrix_rot_phi[5] = 0;
  matrix_rot_phi[6] = 0;
  matrix_rot_phi[7] = 0;
  matrix_rot_phi[8] = 1; 

  cartesian[0] = sin(DEG2RAD(theta))*cos(DEG2RAD(phi));
  cartesian[1] = sin(DEG2RAD(theta))*sin(DEG2RAD(phi));
  cartesian[2] = cos(DEG2RAD(theta));

/*   fprintf(stderr, "%f %f %f --> ", spherical[0], spherical[1], spherical[2]); */

  square_matrix_product(matrix_rot_phi, cartesian, cartesian_prime, 3);
  square_matrix_product(matrix_rot_theta, cartesian_prime, cartesian_second, 3);
/*   square_matrix_product(matrix_rot_phi, cartesian_second, cartesian_ter, 3); */

  cartesian_to_spherical(spherical, cartesian_second);

/*   fprintf(stderr, "%f %f %f \n", spherical[0], spherical[1], spherical[2]); */

/*   theta_prime = spherical[1] - *theta_a; */
/*   phi_prime = spherical[2]; */

/*   /\* fin modifs *\/ */
/*   theta_prime = fModulo(theta_prime, 360); */

/*   if(theta_prime > 180) */
/*     { */
/*       theta_prime = 360 - theta_prime; */
/*       phi_prime += 180; */
/*       phi_prime = fModulo(phi_prime, 360); */
/*     } */

/*   phi_prime -= *phi_a; */
/*   phi_prime = fModulo(phi_prime, 360);  /\* Probably useless *\/ */

  theta_prime = spherical[1];
  phi_prime = spherical[2];

  hsl[2] = 1 - theta_prime/180.;

  hsl[0] = fModulo(phi_prime - *phi_prime_zero, 360) / 360.;

  /* FIN ALGO COLORISATION */

  hsl[1] = 1;
  
  HSLtoRGB(mm, hsl);

  mm[3] = ele->rgb[3];
  
  setOpenGlMaterial(ele->material, mm);

  /* The following is the part responsible for the rotation 
     of the atoms according to their spins. */

  visuDataGet_nodePosition(visuData, node, xyz);
  view = visuDataGet_openGLView(visuData);
  g_return_if_fail(view);

  glPushMatrix();
  
  glTranslated(xyz[0] - view->box->dxxs2,
	       xyz[1] - view->box->dyys2,
	       xyz[2] - view->box->dzzs2);

  glRotated(node->phi, 0, 0, 1);  
  glRotated(node->theta, 0, 1, 0); 

  if (visuData->setColor)
    {
      visuData->setColor(visuData, rgba, ele, node);
      setOpenGlMaterial(ele->material, rgba);
    }
  glCallList(ele->openGLIdentifier);

  glPopMatrix();
}
