/*   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 "toolConfigFile.h"
#include <visu_tools.h>

/**
 * SECTION:toolConfigFile
 * @short_description: Generic read methods of the configuration files
 * are defined here.
 *
 * <para>These methods give generic tools to read common data in the
 * configuration files, such as arrays of float values or one
 * #VisuElement... It also defines a enumeration of detailed errors
 * (see #ToolConfigFileError) when reading a file. All read elements
 * are either from tokens (given g_strsplit()) or strings.</para>
 */

/**
 * tool_config_file_getQuark:
 *
 * Internal routine for error handling.
 *
 * Returns: the #GQuark associated to errors related to configuration
 * files.
 */
GQuark tool_config_file_getQuark()
{
  return g_quark_from_static_string("visu_configFile");
}

/**
 * tool_config_file_readFloatFromTokens:
 * @tokens: array of tokens resulting from a call to g_strsplit() with " " as separator ;
 * @position: IN, the position of the beginning in @tokens ; OUT, one token
 *            after the last read ;
 * @values: allocated area to store read values ;
 * @size: the number of floating point values to be read ;
 * @lineId: the number of the line of the config
 *          file which the @line argument is taken from ;
 * @error: a location to store a possible reading error.
 *
 * Read @size floating point values from @tokens, store them in @values and returns
 * the new head in @tokens.
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readFloatFromTokens(gchar **tokens, int *position, float *values,
					guint size, int lineId, GError **error)
{
  int res;
  guint i, nb;

  g_return_val_if_fail(error && (*error == (GError*)0), FALSE);
  g_return_val_if_fail(values, FALSE);
  g_return_val_if_fail(tokens && position, FALSE);
  
  /* Read @size floating point values from tokens. */
  nb = 0;
  for (i = *position; tokens[i] && nb < size; i++)
    {
      if (tokens[i][0] != '\0')
	{
	  res = sscanf(tokens[i], "%f", values + nb);
	  if (res != 1)
	    {
	      *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_READ,
				   _("Parse error at line %d, %d floating point"
				     " values should appear here.\n"), lineId, size);
	      *position = i;
	      return FALSE;
	    }
	  nb += 1;
	}
    }
  *position = i;

  if (nb != size)
    {
      *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_MISSING,
			   _("Parse error at line %d, %d floating point value(s)"
			     " should appear here but %d has been found.\n"),
			   lineId, size, nb);
      return FALSE;
    }
  return TRUE;
}
/**
 * tool_config_file_readFloat:
 * @line: string where values are read from ;
 * @position: the number of the line of the config
 *            file which the @line argument is taken from ;
 * @values: allocated area to store read values ;
 * @size: the number of floating point values to be read ;
 * @error: a location to store a possible reading error.
 *
 * Read @size floating point values from @line and store them in @values.
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readFloat(gchar *line, int position, float *values,
			      guint size, GError **error)
{
  int id;
  gboolean res;
  gchar **tokens;

  /* Read @size floating point values from @line. */
  tokens = g_strsplit_set(line, " \n", TOOL_MAX_LINE_LENGTH);
  id = 0;
  res = tool_config_file_readFloatFromTokens(tokens, &id, values, size, position, error);
  g_strfreev(tokens);

  return res;
}
/**
 * tool_config_file_readBooleanFromTokens:
 * @tokens: array of tokens resulting from a call to g_strsplit() with " " as separator ;
 * @position: IN, the position of the beginning in @tokens ; OUT, one token
 *            after the last read ;
 * @values: allocated area to store read values ;
 * @size: the number of floating point values to be read ;
 * @lineId: the number of the line of the config
 *          file which the @line argument is taken from ;
 * @error: a location to store a possible reading error.
 *
 * Read @size boolean values from @tokens, store them in @values and returns
 * the new head in @tokens.
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readBooleanFromTokens(gchar **tokens, int *position, gboolean *values,
					  guint size, int lineId, GError **error)
{
  int res;
  guint i, nb;

  g_return_val_if_fail(error && (*error == (GError*)0), FALSE);
  g_return_val_if_fail(values, FALSE);
  g_return_val_if_fail(tokens && position, FALSE);
  
  /* Read @size floating point values from @line. */
  nb = 0;
  for (i = *position; tokens[i] && nb < size; i++)
    {
      if (tokens[i][0] != '\0')
	{
	  res = sscanf(tokens[i], "%d", values + nb);
	  if (res != 1)
	    {
	      *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_READ,
				   _("Parse error at line %d, %d boolean"
				     " values should appear here.\n"), lineId, size);
	      *position = i;
	      return FALSE;
	    }
	  nb += 1;
	}
    }
  *position = i;

  if (nb != size)
    {
      *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_MISSING,
			   _("Parse error at line %d, %d boolean(s)"
			     " values should appear here but %d has been found.\n"),
			   lineId, size, nb);
      return FALSE;
    }
  return TRUE;
}
/**
 * tool_config_file_readBoolean:
 * @line: string where values are read from ;
 * @position: the number of the line of the config
 *            file which the @line argument is taken from ;
 * @values: allocated area to store read values ;
 * @size: the number of boolean values to be read ;
 * @error: a location to store a possible reading error.
 *
 * Read @size boolean values from @line and store them in @values.
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readBoolean(gchar *line, int position, gboolean *values,
				guint size, GError **error)
{
  int id;
  gboolean res;
  gchar **tokens;

  /* Read @size boolean values from @line. */
  tokens = g_strsplit_set(line, " \n", TOOL_MAX_LINE_LENGTH);
  id = 0;
  res = tool_config_file_readBooleanFromTokens(tokens, &id, values, size, position, error);
  g_strfreev(tokens);

  return res;
}
/**
 * tool_config_file_readStringFromTokens:
 * @tokens: array of tokens resulting from a call to g_strsplit() with " " as separator ;
 * @position: IN, the position of the beginning in @tokens ; OUT, one token
 *            after the last read ;
 * @values: a location to point on a gchar** ;
 * @size: the number of floating point values to be read ;
 * @lineId: the number of the line of the config
 *          file which the @line argument is taken from ;
 * @error: a location to store a possible reading error.
 *
 * Read @size strings from @tokens, store them in @values and returns
 * the new head in @tokens.
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readStringFromTokens(gchar **tokens, int *position, gchar ***values,
					  guint size, int lineId, GError **error)
{
  guint i, nb;

  g_return_val_if_fail(error && (*error == (GError*)0), FALSE);
  g_return_val_if_fail(values, FALSE);
  g_return_val_if_fail(tokens && position, FALSE);
  
  /* Read @size floating point values from @line. */
  *values = g_malloc(sizeof(gchar**) * (size + 1));
  nb = 0;
  for (i = *position; tokens[i] && nb < size; i++)
    if (tokens[i][0] != '\0')
      (*values)[nb++] = g_strdup(tokens[i]);
  *position = i;
  (*values)[nb] = (gchar*)0;

  if (nb != size)
    {
      *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_MISSING,
			   _("Parse error at line %d, %d string(s)"
			     " should appear here but %d has been found.\n"),
			   lineId, size, nb);
      g_strfreev(*values);
      *values = (gchar**)0;
      return FALSE;
    }
  return TRUE;
}
/**
 * tool_config_file_readString:
 * @line: string where values are read from ;
 * @position: the number of the line of the config
 *            file which the @line argument is taken from ;
 * @values: a location to point on a gchar** ;
 * @size: the number of strings to be read ;
 * @join: a boolean ;
 * @error: a location to store a possible reading error.
 *
 * Read @size strings from @line and @values points on them. If more
 * strings than @size are available, an error is raised ; except if
 * @join is TRUE. In that case, the method return @size tokens,
 * joining all remaining ones.
 *
 * Returns: TRUE if no error occured, then @values point on an allocated
 *          memory area that is NULL terminated and that must be freed
 *          with g_strfreev().
 */
gboolean tool_config_file_readString(gchar *line, int position, gchar ***values,
			       guint size, gboolean join, GError **error)
{
  guint i, nb;
  gchar *tmp;

  g_return_val_if_fail(error && (*error == (GError*)0), FALSE);
  g_return_val_if_fail(values, FALSE);
  
  /* Read @size floating point values from @line. */
  *values = g_strsplit_set(line, " \n", TOOL_MAX_LINE_LENGTH);
  nb = 0;
  for (i = 0; (*values)[i]; i++)
    if ((*values)[i][0] != '\0')
      {
	tmp = (*values)[i];
	(*values)[i] = (*values)[nb];
	(*values)[nb] = tmp;
	nb += 1;
      }

  if (!join)
    {
      if (nb != size)
	{
	  *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_MISSING,
			       _("Parse error at line %d, %d string(s)"
				 " should appear here but %d has been found.\n"),
			       position, size, nb);
	  g_strfreev(*values);
	  return FALSE;
	}
    }
  else
    {
      tmp = g_strjoinv(" ", (*values) + (size - 1));
      for (i = size - 1; (*values)[i]; i++)
	g_free((*values)[i]);
      (*values)[size - 1] = tmp;
      (*values)[size] = (gchar*)0;
    }
  return TRUE;
}
/**
 * tool_config_file_readBooleanWithLabel:
 * @line: string where values are read from ;
 * @position: the number of the line of the config
 *            file which the @line argument is taken from ;
 * @values: allocated area to store read values ;
 * @size: the number of boolean values to be read ;
 * @label: (allow-none): a location to store a string ;
 * @error: a location to store a possible reading error.
 *
 * Same as tool_config_file_readBoolean() but begins by reading a
 * string label at the begining of the line and store it in @label. If
 * @label is %NULL, only booleans are parsed.
 *
 * Since: 3.8
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readBooleanWithLabel(gchar *line, int position, gboolean *values,
                                               guint size, gchar **label, GError **error)
{
  int id;
  gboolean res;
  gchar **tokens;

  /* Read @size boolean values from @line. */
  tokens = g_strsplit_set(line, " \n", TOOL_MAX_LINE_LENGTH);
  id = 0;
  if (label)
    {
      while (tokens[id] && !tokens[id][0]) id++;
      if (!tokens[id])
        {
          *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_MISSING,
                               _("Parse error at line %d, a label"
                                 " should appear here but none has been found.\n"),
                               position);
          g_strfreev(tokens);
          return FALSE;
        }
      *label = g_strdup(tokens[id]);
      DBG_fprintf(stderr, "Tool ConfigFile: found a label '%s'.\n", *label);
      id += 1;
    }
  res = tool_config_file_readBooleanFromTokens(tokens, &id, values, size, position, error);
  g_strfreev(tokens);

  return res;
}
/**
 * tool_config_file_readFloatWithLabel:
 * @line: string where values are read from ;
 * @position: the number of the line of the config
 *            file which the @line argument is taken from ;
 * @values: allocated area to store read values ;
 * @size: the number of float values to be read ;
 * @label: (allow-none): a location to store a string ;
 * @error: a location to store a possible reading error.
 *
 * Same as tool_config_file_readFloat() but begins by reading a
 * string label at the begining of the line and store it in @label. If
 * @label is %NULL, only floats are parsed.
 *
 * Since: 3.8
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readFloatWithLabel(gchar *line, int position, gfloat *values,
                                             guint size, gchar **label, GError **error)
{
  int id;
  gboolean res;
  gchar **tokens;

  /* Read @size float values from @line. */
  tokens = g_strsplit_set(line, " \n", TOOL_MAX_LINE_LENGTH);
  id = 0;
  if (label)
    {
      while (tokens[id] && !tokens[id][0]) id++;
      if (!tokens[id])
        {
          *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_MISSING,
                               _("Parse error at line %d, a label"
                                 " should appear here but none has been found.\n"),
                               position);
          g_strfreev(tokens);
          return FALSE;
        }
      *label = g_strdup(tokens[id]);
      DBG_fprintf(stderr, "Tool ConfigFile: found a label '%s'.\n", *label);
      id += 1;
    }
  res = tool_config_file_readFloatFromTokens(tokens, &id, values, size, position, error);
  g_strfreev(tokens);

  return res;
}
/* gboolean tool_config_file_readStringWithElement(gchar *line, int position, gchar ***values, */
/* 					  guint size, VisuElement **ele, GError **error) */
/* { */
/*   int id; */
/*   gboolean res; */
/*   gchar **tokens; */

/*   /\* Read @size boolean values from @line. *\/ */
/*   tokens = g_strsplit_set(line, " \n", TOOL_MAX_LINE_LENGTH); */
/*   id = 0; */
/*   res = tool_config_file_readElementFromTokens(tokens, &id, ele, 1, position, error); */
/*   if (!res) */
/*     { */
/*       g_strfreev(tokens); */
/*       return FALSE; */
/*     } */
/*   res = tool_config_file_readStringFromTokens(tokens, &id, values, size, position, error); */
/*   g_strfreev(tokens); */

/*   return res; */
/* } */
/**
 * tool_config_file_readIntegerFromTokens:
 * @tokens: array of tokens resulting from a call to g_strsplit() with " " as separator ;
 * @position: IN, the position of the beginning in @tokens ; OUT, one token
 *            after the last read ;
 * @values: allocated area to store read values ;
 * @size: the number of floating point values to be read ;
 * @lineId: the number of the line of the config
 *          file which the @line argument is taken from ;
 * @error: a location to store a possible reading error.
 *
 * Read @size integer values from @tokens, store them in @values and returns
 * the new head in @tokens.
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readIntegerFromTokens(gchar **tokens, int *position, int *values,
					  guint size, int lineId, GError **error)
{
  int res;
  guint i, nb;

  g_return_val_if_fail(error && (*error == (GError*)0), FALSE);
  g_return_val_if_fail(values, FALSE);
  g_return_val_if_fail(tokens && position, FALSE);
  
  /* Read @size floating point values from @line. */
  nb = 0;
  for (i = *position; tokens[i] && nb < size; i++)
    {
      if (tokens[i][0] != '\0')
	{
	  res = sscanf(tokens[i], "%d", values + nb);
	  if (res != 1)
	    {
	      *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_READ,
				   _("Parse error at line %d, %d integer"
				     " values should appear here.\n"), lineId, size);
	      *position = i;
	      return FALSE;
	    }
	  nb += 1;
	}
    }
  *position = i;

  if (nb != size)
    {
      *error = g_error_new(TOOL_CONFIG_FILE_ERROR, TOOL_CONFIG_FILE_ERROR_MISSING,
			   _("Parse error at line %d, %d integer(s)"
			     " values should appear here but %d has been found.\n"),
			   lineId, size, nb);
      return FALSE;
    }
  return TRUE;
}
/**
 * tool_config_file_readInteger:
 * @line: string where values are read from ;
 * @position: the number of the line of the config
 *            file which the @line argument is taken from ;
 * @values: allocated area to store read values ;
 * @size: the number of floating point values to be read ;
 * @error: a location to store a possible reading error.
 *
 * Read @size integers from @line and store them in @values.
 *
 * Returns: TRUE if no error occured.
 */
gboolean tool_config_file_readInteger(gchar *line, int position, int *values,
				guint size, GError **error)
{
  int id;
  gboolean res;
  gchar **tokens;

  /* Read @size floating point values from @line. */
  tokens = g_strsplit_set(line, " \n", TOOL_MAX_LINE_LENGTH);
  id = 0;
  res = tool_config_file_readIntegerFromTokens(tokens, &id, values, size, position, error);
  g_strfreev(tokens);

  return res;
}
/**
 * tool_config_file_clampFloat:
 * @variable: a pointer to a storage for the value ;
 * @value: the value to put in the storage ;
 * @min: a lower bound ;
 * @max: a upper bound.
 *
 * It puts value in a variable if value is in min and max
 * or put min or max in if not. It return true if value
 * is out of bounds. Min and max are inclusive values.
 * If there is no max bounds then put max at a lower
 * value than min and if there is no min bound, put min
 * at a higher value than max.
 *
 * Returns: TRUE if the value is out of bounds.
 */
gboolean tool_config_file_clampFloat(float *variable, float value, float min, float max)
{
  g_return_val_if_fail(variable, FALSE);

  if (max > min && value > max)
    {
      *variable = max;
      return TRUE;
    }
  if (min < max && value < min)
    {
      *variable = min;
      return TRUE;
    }
  
  *variable = value;
  return FALSE;
}

/**
 * tool_config_file_clampInt:
 * @variable: a pointer to a storage for the value ;
 * @value: the value to put in the storage ;
 * @min: a lower bound ;
 * @max: a upper bound.
 *
 * Integer version o tool_config_file_clampFloat().
 *
 * Since: 3.8
 *
 * Returns: TRUE if the value is out of bounds.
 */
gboolean tool_config_file_clampInt(int *variable, int value, int min, int max)
{
  g_return_val_if_fail(variable, FALSE);

  if (max > min && value > max)
    {
      *variable = max;
      return TRUE;
    }
  if (min < max && value < min)
    {
      *variable = min;
      return TRUE;
    }
  
  *variable = value;
  return FALSE;
}
