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

	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 "iface_wire.h"

#include <visu_configFile.h>
#include <visu_pairset.h>

/**
 * SECTION:iface_wire
 * @short_description: An interface defining all the properties
 * required to draw a #VisuPair as a wire.
 *
 * <para>#VisuPairWire interface introduces the "width" property of a
 * wire rendering, wether or not it should use a solid color or a
 * #ToolShade depending on its length...</para>
 */

#define FLAG_PARAMETER_WIRE   "wire_nonLinear"
#define DESC_PARAMETER_WIRE   "If the color of the pairs are corrected by their length ; boolean 0 or 1"
#define FLAG_PARAMETER_SHADE  "wire_shade"
#define DESC_PARAMETER_SHADE  "If positive, use a shade to colourise pairs depending on length ; -1 or a positive shade id"
#define FLAG_RESOURCES_SHADE  "pairWire_linkShade"
#define DESC_RESOURCES_SHADE  "For each given pair, if positive, use a shade to colourise pairs depending on length ; \"ele1\" \"ele2\" -1 or a positive shade id"
static int _linkShade;

#define FLAG_RESOURCES_PAIR_WIDTH "pairWire_pairWidth"
#define DESC_RESOURCES_PAIR_WIDTH "Widths detail for each pair drawn ; \"ele1\" \"ele2\" 0 < integer < 10"
#define FLAG_RESOURCES_WIRE_WIDTH "pairWire_width"
#define DESC_RESOURCES_WIRE_WIDTH "This value is the width for all pairs drawn ; \"ele1\" \"ele2\" 0 < integer < 10"
#define _WIDTH_MAX 10
#define _WIDTH_DEFAULT 2
static guint wireWidth = _WIDTH_DEFAULT;

#define FLAG_RESOURCES_LINK_WIDTH "pairWire_linkWidth"
#define DESC_RESOURCES_LINK_WIDTH "Widths detail for each drawn link ; \"ele1\" \"ele2\" 0 < integer < 10"
static guint _linkWidth;

#define FLAG_RESOURCES_LINK_STIPPLE "pairWire_linkStipple"
#define DESC_RESOURCES_LINK_STIPPLE "Dot scheme detail for each drawn link ; \"ele1\" \"ele2\" 0 < integer < 2^16"
static guint16 _linkStipple;

static void onEntryShade(VisuConfigFile *obj, VisuConfigFileEntry *entry, gpointer data);
static void onEntryStipple(VisuConfigFile *obj, VisuConfigFileEntry *entry, gpointer data);
static void onEntryWidth(VisuConfigFile *obj, VisuConfigFileEntry *entry, gpointer data);

/* This function save the resources. */
static void exportResourcesWire(GString *data, VisuData *dataObj);

enum {
  PROP_0,
  PROP_WIDTH,
  PROP_SHADE,
  PROP_STIPPLE,
  N_PROPS
};
static GParamSpec *_properties[N_PROPS];

/* Boxed interface. */
G_DEFINE_INTERFACE(VisuPairWire, visu_pair_wire, G_TYPE_OBJECT)

static void visu_pair_wire_default_init(VisuPairWireInterface *iface)
{
  VisuConfigFileEntry *resourceEntry, *oldEntry, *olderEntry;
  int rgWidth[2] = {0, _WIDTH_MAX};
  int rgShade[2] = {-1, G_MAXINT};

  /**
   * VisuPairWire::width:
   *
   * The wire width.
   *
   * Since: 3.8
   */
  _properties[PROP_WIDTH] =
    g_param_spec_uint("width", "Width",
                      "wire width", 1, _WIDTH_MAX, _WIDTH_DEFAULT, G_PARAM_READWRITE);
  g_object_interface_install_property(iface, _properties[PROP_WIDTH]);
  /**
   * VisuPairWire::shade:
   *
   * If set, the wire is coloured with a shade depending on its length.
   *
   * Since: 3.8
   */
  _properties[PROP_SHADE] =
    g_param_spec_boxed("shade", "Shade",
                       "wire shade", TOOL_TYPE_SHADE, G_PARAM_READWRITE);
  g_object_interface_install_property(iface, _properties[PROP_SHADE]);
  /**
   * VisuPairWire::stipple:
   *
   * The wire stipple.
   *
   * Since: 3.8
   */
  _properties[PROP_STIPPLE] =
    g_param_spec_uint("stipple", "Stipple",
                      "wire stipple", 1, 65535, 65535, G_PARAM_READWRITE);
  g_object_interface_install_property(iface, _properties[PROP_STIPPLE]);

  resourceEntry = visu_config_file_addIntegerArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                        FLAG_RESOURCES_WIRE_WIDTH,
                                                        DESC_RESOURCES_WIRE_WIDTH,
                                                        1, (int*)&wireWidth, rgWidth, FALSE);
  oldEntry = visu_config_file_addEntry(VISU_CONFIG_FILE_RESOURCE,
				     FLAG_RESOURCES_PAIR_WIDTH,
				     DESC_RESOURCES_PAIR_WIDTH,
				     1, NULL);
  visu_config_file_entry_setVersion(resourceEntry, 3.1f);
  resourceEntry = visu_config_file_addIntegerArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                        FLAG_RESOURCES_LINK_WIDTH,
                                                        DESC_RESOURCES_LINK_WIDTH,
                                                        1, (int*)&_linkWidth, rgWidth, TRUE);
  visu_config_file_entry_setVersion(resourceEntry, 3.4f);
  visu_config_file_entry_setReplace(resourceEntry, oldEntry);
  g_signal_connect(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCES_LINK_WIDTH,
                   G_CALLBACK(onEntryWidth), (gpointer)0);
  resourceEntry = visu_config_file_addStippleArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                        FLAG_RESOURCES_LINK_STIPPLE,
                                                        DESC_RESOURCES_LINK_STIPPLE,
                                                        1, &_linkStipple);
  visu_config_file_entry_setVersion(resourceEntry, 3.4f);
  g_signal_connect(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCES_LINK_STIPPLE,
                   G_CALLBACK(onEntryStipple), (gpointer)0);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE,
                                     exportResourcesWire);
  olderEntry = visu_config_file_addEntry(VISU_CONFIG_FILE_PARAMETER,
                                         FLAG_PARAMETER_WIRE,
                                         DESC_PARAMETER_WIRE,
                                         1, NULL);
  oldEntry = visu_config_file_addEntry(VISU_CONFIG_FILE_PARAMETER,
                                       FLAG_PARAMETER_SHADE,
                                       DESC_PARAMETER_SHADE,
                                       1, NULL);
  visu_config_file_entry_setVersion(oldEntry, 3.6f);
  visu_config_file_entry_setReplace(oldEntry, olderEntry);
  resourceEntry = visu_config_file_addIntegerArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                        FLAG_RESOURCES_SHADE,
                                                        DESC_RESOURCES_SHADE,
                                                        1, &_linkShade, rgShade, TRUE);
  visu_config_file_entry_setVersion(resourceEntry, 3.7f);
  visu_config_file_entry_setReplace(resourceEntry, oldEntry);
  g_signal_connect(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCES_SHADE,
                   G_CALLBACK(onEntryShade), (gpointer)0);
}

/*****************************************/
/* Dealing with parameters and resources */
/*****************************************/
static void onEntryShade(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry, gpointer data _U_)
{
  VisuPairLink *link;
  gchar *errMess;
  GList *list;
  
  if (!visu_pair_pool_readLinkFromLabel(visu_config_file_entry_getLabel(entry), &link, &errMess))
    {
      visu_config_file_entry_setErrorMessage(entry, errMess);
      g_free(errMess);
      return;
    }
  
  list = tool_pool_asList(tool_shade_getStorage());
  if (_linkShade >= (int)g_list_length(list))
    {
      visu_config_file_entry_setErrorMessage(entry, _("shade id must be in [%d;%d]"),
                                             0, g_list_length(list) - 1);
      return;
    }
  else
    visu_pair_wire_setShade(VISU_PAIR_WIRE(link),
                            (ToolShade*)g_list_nth_data(list, _linkShade));
}
static void onEntryStipple(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry, gpointer data _U_)
{
  VisuPairLink *link;
  gchar *errMess;
  
  if (!visu_pair_pool_readLinkFromLabel(visu_config_file_entry_getLabel(entry), &link, &errMess))
    {
      visu_config_file_entry_setErrorMessage(entry, errMess);
      g_free(errMess);
      return;
    }
  visu_pair_wire_setStipple(VISU_PAIR_WIRE(link), _linkStipple);
}
static void onEntryWidth(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry, gpointer data _U_)
{
  VisuPairLink *link;
  gchar *errMess;
  
  if (!visu_pair_pool_readLinkFromLabel(visu_config_file_entry_getLabel(entry), &link, &errMess))
    {
      visu_config_file_entry_setErrorMessage(entry, errMess);
      g_free(errMess);
      return;
    }
  visu_pair_wire_setWidth(VISU_PAIR_WIRE(link), _linkWidth);
}
static void exportWidth(VisuPair *pair, VisuPairLink *data, gpointer userData)
{
  struct _VisuConfigFileForeachFuncExport *str;
  VisuElement *ele1, *ele2;
  guint16 stipple;
  ToolShade *shade;
  gchar *buf;

  str = ( struct _VisuConfigFileForeachFuncExport*)userData;
  visu_pair_getElements(pair, &ele1, &ele2);
  if (str->dataObj &&
      (!visu_node_array_containsElement(VISU_NODE_ARRAY(str->dataObj), ele1) ||
       !visu_node_array_containsElement(VISU_NODE_ARRAY(str->dataObj), ele2)))
    return;
  if (visu_pair_link_getDistance(data, VISU_DISTANCE_MIN) == 0.f &&
      visu_pair_link_getDistance(data, VISU_DISTANCE_MAX) == 0.f)
    return;

  buf = g_strdup_printf("%s %s  %4.3f %4.3f", ele1->name, ele2->name,
                        visu_pair_link_getDistance(data, VISU_DISTANCE_MIN),
                        visu_pair_link_getDistance(data, VISU_DISTANCE_MAX));

  if (visu_pair_wire_getWidth(VISU_PAIR_WIRE(data)) !=
      visu_pair_wire_getDefaultWidth())
    visu_config_file_exportEntry(str->data, FLAG_RESOURCES_LINK_WIDTH, buf,
                                 "%d", visu_pair_wire_getWidth(VISU_PAIR_WIRE(data)));

  stipple = visu_pair_wire_getStipple(VISU_PAIR_WIRE(data));
  if (stipple != 65535)
    visu_config_file_exportEntry(str->data, FLAG_RESOURCES_LINK_STIPPLE, buf,
                                 "%d", stipple);

  shade = visu_pair_wire_getShade(VISU_PAIR_WIRE(data));
  if (shade)
    visu_config_file_exportEntry(str->data, FLAG_RESOURCES_LINK_STIPPLE, buf,
                                 "%d", tool_pool_index(tool_shade_getStorage(), shade));

  g_free(buf);
}
static void exportPair(VisuPair *pair, gpointer data)
{
  visu_pair_foreach(pair, exportWidth, data);
}
static void exportResourcesWire(GString *data, VisuData *dataObj)
{
  struct _VisuConfigFileForeachFuncExport str;

  visu_config_file_exportComment(data, DESC_RESOURCES_WIRE_WIDTH);
  visu_config_file_exportEntry(data, FLAG_RESOURCES_WIRE_WIDTH, NULL,
                               "%d", wireWidth);
  str.data           = data;
  str.dataObj        = dataObj;
  visu_config_file_exportComment(data, DESC_RESOURCES_LINK_WIDTH);
  visu_config_file_exportComment(data, DESC_RESOURCES_LINK_STIPPLE);
  visu_config_file_exportComment(data, DESC_RESOURCES_SHADE);
  visu_pair_pool_foreach(exportPair, &str);
  visu_config_file_exportComment(data, "");
}

/**
 * visu_pair_wire_setStipple:
 * @data: a #VisuPairWire object ;
 * @stipple: a pattern.
 *
 * Change the line pattern of @data.
 *
 * Returns: TRUE if the value is different from previous.
 */
gboolean visu_pair_wire_setStipple(VisuPairWire *data, guint16 stipple)
{
  gboolean res;

  res = VISU_PAIR_WIRE_GET_INTERFACE(data)->set_stipple(data, stipple);
  if (res)
    g_object_notify_by_pspec(G_OBJECT(data), _properties[PROP_STIPPLE]);
  return res;
}
/**
 * visu_pair_wire_getStipple:
 * @data: a #VisuPairWire object.
 *
 * Get the line pattern of @data.
 *
 * Returns: a line pattern (default is 65535).
 */
guint16 visu_pair_wire_getStipple(VisuPairWire *data)
{
  return VISU_PAIR_WIRE_GET_INTERFACE(data)->get_stipple(data);
}
/**
 * visu_pair_wire_setWidth:
 * @data: a #VisuPairWire object ;
 * @val: a positive integer.
 *
 * This method allows to change the width of line for a specific pair.
 * When a pair is rendered via with a line, it first checks if that pairs has
 * a specific width value set by this method. If not, it uses the default value.
 *
 * Returns: TRUE if the value is different from previous.
 */
gboolean visu_pair_wire_setWidth(VisuPairWire *data, guint val)
{
  gboolean res;

  res = VISU_PAIR_WIRE_GET_INTERFACE(data)->set_width(data, MIN(_WIDTH_MAX, val));
  if (res)
    g_object_notify_by_pspec(G_OBJECT(data), _properties[PROP_WIDTH]);
  return res;
}
/**
 * visu_pair_wire_getWidth:
 * @data: a #VisuPairWire object.
 *
 * Get the width of the given pair @data. If the given pair has no
 * specific width, the defaul value is returned.
 *
 * Returns: the width of the given pair.
 */
guint visu_pair_wire_getWidth(VisuPairWire *data)
{
  return VISU_PAIR_WIRE_GET_INTERFACE(data)->get_width(data);
}
/**
 * visu_pair_wire_setShade:
 * @data: a #VisuPairWire object.
 * @shade: (allow-none): a #ToolShade object (can be NULL).
 *
 * If @shade is not NULL, make the colour of each pair varies with its
 * length according to @shade colour scheme.
 *
 * Since: 3.6
 *
 * Returns: TRUE if shade is changed.
 */
gboolean visu_pair_wire_setShade(VisuPairWire *data, ToolShade *shade)
{
  gboolean res;

  res = VISU_PAIR_WIRE_GET_INTERFACE(data)->set_shade(data, shade);
  if (res)
    g_object_notify_by_pspec(G_OBJECT(data), _properties[PROP_SHADE]);
  return res;
}
/**
 * visu_pair_wire_getShade:
 * @data: a #VisuPairWire object.
 *
 * Colour of wires can depend on length, following a #ToolShade scheme.
 *
 * Since: 3.6
 *
 * Returns: (transfer none): the #ToolShade scheme if used, or NULL.
 */
ToolShade* visu_pair_wire_getShade(VisuPairWire *data)
{
  return VISU_PAIR_WIRE_GET_INTERFACE(data)->get_shade(data);
}

/**
 * visu_pair_wire_getDefaultWidth:
 *
 * Retrieves the default width.
 *
 * Since: 3.8
 *
 * Returns: the default width.
 **/
guint visu_pair_wire_getDefaultWidth()
{
  return wireWidth;
}
/**
 * visu_pair_wire_getDefaultStipple:
 *
 * Retrieves the default stipple pattern.
 *
 * Since: 3.8
 *
 * Returns: the default stipple pattern.
 **/
guint16 visu_pair_wire_getDefaultStipple()
{
  return 65535;
}
