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

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

#include <GL/gl.h>
#include <GL/glu.h> 

#include <math.h>
#include <string.h>

#include <opengl.h>
#include <visu_tools.h>
#include <visu_configFile.h>
#include <openGLFunctions/objectList.h>
#include <openGLFunctions/text.h>
#include <coreTools/toolMatrix.h>
#include <coreTools/toolColor.h>

/**
 * SECTION:box
 * @short_description: Draw a bounding box around nodes.
 *
 * <para>This extension allows V_Sim to draw a box around the
 * nodes. The box is defined in the #VisuBox structure and can be
 * retrieved with visu_box_getGeometry(). This box is not necessary
 * orthogonal.</para>
 * <para>It has several properties, namely, its colour, its line width
 * and its line pattern. It is represented in OpenGL with simple lines
 * and is affected by the antialiasing property. Defined resources:</para>
 * <itemizedlist>
 *  <listitem>
 *   <para><emphasis>box_is_on</emphasis> (boolean): controls if a box
 *   is drawn around the rendering area (since 3.0).</para>
 *  </listitem>
 *  <listitem>
 *   <para><emphasis>box_color</emphasis> (RGB in [0;1]): defines the
 *   color of the box(since 3.0).</para>
 *  </listitem>
 *  <listitem>
 *   <para><emphasis>box_line_width</emphasis> (integer in [1;10]):
 *   defines the width of the lines of the box (since 3.0).</para>
 *  </listitem>
 *  <listitem>
 *   <para><emphasis>box_line_stipple</emphasis> (2 integers in
 *   ]0;65535]): dot scheme detail for the lines of the box. The first
 *   value is the pattern for the line of the main box and the second
 *   is the pattern for the lines of the expanded areas (since 3.4).</para>
 *  </listitem>
 * </itemizedlist>
 */

/* Parameters & resources*/
/* This is a boolean to control is the box is render or not. */
#define FLAG_RESOURCE_BOX_USED   "box_is_on"
#define DESC_RESOURCE_BOX_USED   "Control if a box is drawn around the rendering area ; boolean (0 or 1)"
static gboolean RESOURCE_BOX_USED_DEFAULT = FALSE;
/* A resource to control the color used to render the lines of the box. */
#define FLAG_RESOURCE_BOX_COLOR   "box_color"
#define DESC_RESOURCE_BOX_COLOR   "Define the color of the box ; three floating point values (0. <= v <= 1.)"
static float rgbDefault[4] = {1.0, 0.5, 0.1, 1.};
/* A resource to control the width to render the lines of the box. */
#define FLAG_RESOURCE_BOX_LINE   "box_line_width"
#define DESC_RESOURCE_BOX_LINE   "Define the width of the lines of the box ; one integer (1. <= v <= 10.)"
static float LINE_WIDTH_DEFAULT = 1.;
/* A resource to control the stipple to render the lines of the box. */
#define FLAG_RESOURCE_BOX_STIPPLE   "box_line_stipple"
#define DESC_RESOURCE_BOX_STIPPLE   "Dot scheme detail for the lines of the box (main and expanded) ; 0 < 2 integers < 2^16"
static guint16 stippleDefault[2] = {65535, 65280};

#define FLAG_RESOURCE_BOX_SIDE   "box_side_color"
#define DESC_RESOURCE_BOX_SIDE   "RGBA color used to draw the pristine box sides when expanded ; four floating point values (0. <= v <= 1.)"
static float sideRGBDefault[4] = {0.f, 0.f, 0.f, 0.3333f};

#define RESOURCE_WITH_BASIS_DEFAULT FALSE
/* static gboolean withBasis = FALSE; */
static float basisLength = 2.5f;

/* Export function that is called by visu_module to write the
   values of resources to a file. */
static void exportResourcesBox(GString *data, VisuData *dataObj);

struct _VisuGlExtBoxPrivate
{
  gboolean dispose_has_run;

  /* Box definition. */
  VisuBox *box;
  gulong box_signal;
  /* Matrix definition (to be merge later within box. */
  float matrix[3][3];

  /* Rendenring parameters. */
  float rgb[4], sideRGB[4];
  float lineWidth;
  guint16 lineStipple[2];
};
static VisuGlExtBox* defaultBox;

enum
  {
    PROP_0,
    COLOR_PROP,
    SIDE_COLOR_PROP,
    WIDTH_PROP,
    STIPPLE_PROP,
    EXT_STIPPLE_PROP,
    BOX_PROP,
    N_PROP
  };
static GParamSpec *properties[N_PROP];

static void visu_gl_ext_lined_interface_init(VisuGlExtLinedInterface *iface);
static void visu_gl_ext_box_dispose(GObject* obj);
static void visu_gl_ext_box_get_property(GObject* obj, guint property_id,
                                         GValue *value, GParamSpec *pspec);
static void visu_gl_ext_box_set_property(GObject* obj, guint property_id,
                                         const GValue *value, GParamSpec *pspec);
static void visu_gl_ext_box_rebuild(VisuGlExt *ext);
static void visu_gl_ext_box_draw(VisuGlExt *ext);

static gboolean _setRGB(VisuGlExtLined *box, float rgb[4], int mask);
static gboolean _setLineWidth(VisuGlExtLined *box, float width);
static gboolean _setLineStipple(VisuGlExtLined *box, guint16 stipple);
static float*   _getRGB(const VisuGlExtLined *box);
static float    _getLineWidth(const VisuGlExtLined *box);
static guint16  _getLineStipple(const VisuGlExtLined *box);

/* Callbacks. */
static void onSizeChanged(VisuBox *box, gfloat extens, gpointer user_data);
static void onEntryUsed(VisuGlExtBox *box, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryColor(VisuGlExtBox *box, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryWidth(VisuGlExtBox *box, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryStipple(VisuGlExtBox *box, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntrySide(VisuGlExtBox *box, VisuConfigFileEntry *entry, VisuConfigFile *obj);

G_DEFINE_TYPE_WITH_CODE(VisuGlExtBox, visu_gl_ext_box, VISU_TYPE_GL_EXT,
                        G_ADD_PRIVATE(VisuGlExtBox)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_GL_EXT_LINED,
                                              visu_gl_ext_lined_interface_init))

static void visu_gl_ext_lined_interface_init(VisuGlExtLinedInterface *iface)
{
  iface->get_width   = _getLineWidth;
  iface->set_width   = _setLineWidth;
  iface->get_stipple = _getLineStipple;
  iface->set_stipple = _setLineStipple;
  iface->get_rgba    = _getRGB;
  iface->set_rgba    = _setRGB;
}
static void visu_gl_ext_box_class_init(VisuGlExtBoxClass *klass)
{
  float rgColor[2] = {0.f, 1.f};
  float rgWidth[2] = {0.f, 10.f};
  VisuConfigFileEntry *resourceEntry;

  DBG_fprintf(stderr, "Extension Box: creating the class of the object.\n");
  /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */

  DBG_fprintf(stderr, "                - adding new resources ;\n");
  resourceEntry = visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_RESOURCE,
                                                   FLAG_RESOURCE_BOX_USED,
                                                   DESC_RESOURCE_BOX_USED,
                                                   &RESOURCE_BOX_USED_DEFAULT, FALSE);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_BOX_COLOR,
                                                      DESC_RESOURCE_BOX_COLOR,
                                                      3, rgbDefault, rgColor, FALSE);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_BOX_LINE,
                                                      DESC_RESOURCE_BOX_LINE,
                                                      1, &LINE_WIDTH_DEFAULT, rgWidth, FALSE);
  resourceEntry = visu_config_file_addStippleArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                        FLAG_RESOURCE_BOX_STIPPLE,
                                                        DESC_RESOURCE_BOX_STIPPLE,
                                                        2, stippleDefault);
  visu_config_file_entry_setVersion(resourceEntry, 3.4f);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_BOX_SIDE,
                                                      DESC_RESOURCE_BOX_SIDE,
                                                      4, sideRGBDefault, rgColor, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.8f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE,
                                     exportResourcesBox);

  defaultBox = (VisuGlExtBox*)0;

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_box_dispose;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_ext_box_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_ext_box_get_property;
  VISU_GL_EXT_CLASS(klass)->rebuild = visu_gl_ext_box_rebuild;
  VISU_GL_EXT_CLASS(klass)->draw = visu_gl_ext_box_draw;

  /**
   * VisuGlExtBox::color:
   *
   * Store the color of the box.
   *
   * Since: 3.8
   */
  g_object_class_override_property(G_OBJECT_CLASS(klass), COLOR_PROP, "color");
  /**
   * VisuGlExtBox::side-color:
   *
   * Store the color of the sides drawn on the primary cell when the
   * box is expanded.
   *
   * Since: 3.8
   */
  properties[SIDE_COLOR_PROP] = g_param_spec_boxed("side-color", "side color",
                                                   "color of the primary cell sides",
                                                   TOOL_TYPE_COLOR, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), SIDE_COLOR_PROP,
				  properties[SIDE_COLOR_PROP]);
  /**
   * VisuGlExtBox::width:
   *
   * Store the line width of the box.
   *
   * Since: 3.8
   */
  g_object_class_override_property(G_OBJECT_CLASS(klass), WIDTH_PROP, "width");
  /**
   * VisuGlExtBox::stipple:
   *
   * Store the line stipple pattern of the box.
   *
   * Since: 3.8
   */
  g_object_class_override_property(G_OBJECT_CLASS(klass), STIPPLE_PROP, "stipple");
  /**
   * VisuGlExtBox::expand-stipple:
   *
   * Store the line stipple pattern of the extension lines of the box.
   *
   * Since: 3.8
   */
  properties[EXT_STIPPLE_PROP] = g_param_spec_uint("expand-stipple", "extension line stipple",
                                                   "rendering line stipple pattern of extension",
                                                   0, 65535, stippleDefault[1],
                                                   G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), EXT_STIPPLE_PROP,
				  properties[EXT_STIPPLE_PROP]);
  /**
   * VisuGlExtBox::basis:
   *
   * Store the #VisuBoxed object that defines the box. If %NULL, a
   * cartesian basis-set is assumed.
   *
   * Since: 3.8
   */
  properties[BOX_PROP] = g_param_spec_object("basis", "basis-set",
                                             "provides the basis-set to draw the box",
                                             VISU_TYPE_BOX, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), BOX_PROP,
				  properties[BOX_PROP]);
}

static void visu_gl_ext_box_init(VisuGlExtBox *obj)
{
  DBG_fprintf(stderr, "Extension Box: initializing a new object (%p).\n",
	      (gpointer)obj);
  
  obj->priv = visu_gl_ext_box_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  /* Private data. */
  tool_matrix_setIdentity(obj->priv->matrix);
  obj->priv->rgb[0]      = rgbDefault[0];
  obj->priv->rgb[1]      = rgbDefault[1];
  obj->priv->rgb[2]      = rgbDefault[2];
  obj->priv->rgb[3]      = 1.f;
  obj->priv->sideRGB[0]  = sideRGBDefault[0];
  obj->priv->sideRGB[1]  = sideRGBDefault[1];
  obj->priv->sideRGB[2]  = sideRGBDefault[2];
  obj->priv->sideRGB[3]  = sideRGBDefault[3];
  obj->priv->lineWidth   = LINE_WIDTH_DEFAULT;
  obj->priv->lineStipple[0] = stippleDefault[0];
  obj->priv->lineStipple[1] = stippleDefault[1];
  obj->priv->box          = (VisuBox*)0;
  obj->priv->box_signal   = 0;

  /* withBasis          = RESOURCE_WITH_BASIS_DEFAULT; */
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_BOX_USED,
                          G_CALLBACK(onEntryUsed), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_BOX_COLOR,
                          G_CALLBACK(onEntryColor), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_BOX_LINE,
                          G_CALLBACK(onEntryWidth), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_BOX_STIPPLE,
                          G_CALLBACK(onEntryStipple), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_BOX_SIDE,
                          G_CALLBACK(onEntrySide), (gpointer)obj, G_CONNECT_SWAPPED);

  if (!defaultBox)
    defaultBox = obj;
}

/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_gl_ext_box_dispose(GObject* obj)
{
  VisuGlExtBox *box;

  DBG_fprintf(stderr, "Extension Box: dispose object %p.\n", (gpointer)obj);

  box = VISU_GL_EXT_BOX(obj);
  if (box->priv->dispose_has_run)
    return;
  box->priv->dispose_has_run = TRUE;

  /* Disconnect signals. */
  visu_gl_ext_box_setBox(box, (VisuBox*)0);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_gl_ext_box_parent_class)->dispose(obj);
}
static void visu_gl_ext_box_get_property(GObject* obj, guint property_id,
                                         GValue *value, GParamSpec *pspec)
{
  VisuGlExtBox *self = VISU_GL_EXT_BOX(obj);

  DBG_fprintf(stderr, "Extension Box: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case COLOR_PROP:
      g_value_take_boxed(value, tool_color_new(self->priv->rgb));
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->rgb[0], self->priv->rgb[1], self->priv->rgb[2]);
      break;
    case SIDE_COLOR_PROP:
      g_value_take_boxed(value, tool_color_new(self->priv->sideRGB));
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->rgb[0], self->priv->rgb[1], self->priv->rgb[2]);
      break;
    case WIDTH_PROP:
      g_value_set_float(value, self->priv->lineWidth);
      DBG_fprintf(stderr, "%g.\n", self->priv->lineWidth);
      break;
    case STIPPLE_PROP:
      g_value_set_uint(value, (guint)self->priv->lineStipple[0]);
      DBG_fprintf(stderr, "%d.\n", (guint)self->priv->lineStipple[0]);
      break;
    case EXT_STIPPLE_PROP:
      g_value_set_uint(value, (guint)self->priv->lineStipple[1]);
      DBG_fprintf(stderr, "%d.\n", (guint)self->priv->lineStipple[1]);
      break;
    case BOX_PROP:
      g_value_set_object(value, self->priv->box);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->box);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_gl_ext_box_set_property(GObject* obj, guint property_id,
                                          const GValue *value, GParamSpec *pspec)
{
  ToolColor *color;
  VisuGlExtBox *self = VISU_GL_EXT_BOX(obj);

  DBG_fprintf(stderr, "Extension Box: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case COLOR_PROP:
      color = (ToolColor*)g_value_get_boxed(value);
      _setRGB((VisuGlExtLined*)self, color->rgba, TOOL_COLOR_MASK_RGBA);
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->rgb[0], self->priv->rgb[1], self->priv->rgb[2]);
      break;
    case SIDE_COLOR_PROP:
      color = (ToolColor*)g_value_get_boxed(value);
      visu_gl_ext_box_setSideRGB(self, color->rgba, TOOL_COLOR_MASK_RGBA);
      DBG_fprintf(stderr, "%gx%gx%g.\n", self->priv->rgb[0], self->priv->rgb[1], self->priv->rgb[2]);
      break;
    case WIDTH_PROP:
      _setLineWidth((VisuGlExtLined*)self, g_value_get_float(value));
      DBG_fprintf(stderr, "%g.\n", self->priv->lineWidth);
      break;
    case STIPPLE_PROP:
      _setLineStipple((VisuGlExtLined*)self, (guint16)g_value_get_uint(value));
      DBG_fprintf(stderr, "%d.\n", (guint)self->priv->lineStipple[0]);
      break;
    case EXT_STIPPLE_PROP:
      visu_gl_ext_box_setExpandStipple(self, (guint16)g_value_get_uint(value));
      DBG_fprintf(stderr, "%d.\n", (guint)self->priv->lineStipple[1]);
      break;
    case BOX_PROP:
      visu_gl_ext_box_setBox(self, VISU_BOX(g_value_get_object(value)));
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_gl_ext_box_new:
 * @name: (allow-none): the name to give to the extension (default is #VISU_GL_EXT_BOX_ID).
 *
 * Creates a new #VisuGlExt to draw a box.
 *
 * Since: 3.7
 *
 * Returns: a pointer to the #VisuGlExt it created or
 * NULL otherwise.
 */
VisuGlExtBox* visu_gl_ext_box_new(const gchar *name)
{
  char *name_ = VISU_GL_EXT_BOX_ID;
  char *description = _("Draw a box representing the limit of the area.");

  DBG_fprintf(stderr,"Extension Box: new object.\n");
  
  return g_object_new(VISU_TYPE_GL_EXT_BOX,
                      "name", (name)?name:name_, "label", _(name),
                      "description", description, "nGlObj", 1,
                      "priority", VISU_GL_EXT_PRIORITY_LOW, NULL);
}
/**
 * visu_gl_ext_box_setBox:
 * @box: the #VisuGlExtBox object to attach to.
 * @boxObj: the box to get the definition of.
 *
 * Attach the #VisuBox to draw the frame of.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the #VisuBox model is actually changed.
 **/
gboolean visu_gl_ext_box_setBox(VisuGlExtBox *box, VisuBox *boxObj)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), FALSE);

  DBG_fprintf(stderr, "Extension Box: set box %p.\n", (gpointer)boxObj);
  if (box->priv->box)
    {
      g_signal_handler_disconnect(G_OBJECT(box->priv->box), box->priv->box_signal);
      g_object_unref(box->priv->box);
    }
  if (boxObj)
    {
      g_object_ref(boxObj);
      box->priv->box_signal =
        g_signal_connect(G_OBJECT(boxObj), "SizeChanged",
                         G_CALLBACK(onSizeChanged), (gpointer)box);
    }
  else
    box->priv->box_signal = 0;
  box->priv->box = boxObj;

  visu_gl_ext_setDirty(VISU_GL_EXT(box), TRUE);
  g_object_notify_by_pspec(G_OBJECT(box), properties[BOX_PROP]);
  return TRUE;
}
/**
 * visu_gl_ext_box_setBasis:
 * @box: the #VisuGlExtBox object to attach to.
 * @orig: (array fixed-size=3): the origin.
 * @mat: (array fixed-size=9): the basis-set.
 *
 * Define the box to draw with a simple matrix basis-set and an origin.
 *
 * Since: 3.7
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_gl_ext_box_setBasis(VisuGlExtBox *box, float orig[3], float mat[3][3])
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), FALSE);

  visu_gl_ext_box_setBox(box, (VisuBox*)0);
  visu_gl_ext_setTranslation(VISU_GL_EXT(box), orig);
  memcpy(box->priv->matrix, mat, sizeof(float) * 9);

  visu_gl_ext_setDirty(VISU_GL_EXT(box), TRUE);
  return TRUE;
}

static gboolean _setRGB(VisuGlExtLined *box, float rgb[3], int mask)
{
  VisuGlExtBoxPrivate *self;
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), FALSE);
  self = VISU_GL_EXT_BOX(box)->priv;

  if (mask & TOOL_COLOR_MASK_R)
    self->rgb[0] = rgb[0];
  if (mask & TOOL_COLOR_MASK_G)
    self->rgb[1] = rgb[1];
  if (mask & TOOL_COLOR_MASK_B)
    self->rgb[2] = rgb[2];

  visu_gl_ext_setDirty(VISU_GL_EXT(box), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_box_setSideRGB:
 * @box: the #VisuGlExtBox to update.
 * @rgba: (array fixed-size=4): a four floats array with values (0 <= values <= 1) for the
 * red, the green, the blue color and the alpha channel. Only values
 * specified by the mask are really relevant.
 * @mask: use #TOOL_COLOR_MASK_R, #TOOL_COLOR_MASK_G,
 * #TOOL_COLOR_MASK_B, #TOOL_COLOR_MASK_A or a combinaison to indicate
 * what values in the @rgba array must be taken into account.
 *
 * Change the colour to represent the side of the super-cell. A
 * channel alpha of zero, means that the box is rendered as wire-frame
 * only. The sides are indeed drawn only if the box has expansion.
 *
 * Since: 3.7
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_ext_box_setSideRGB(VisuGlExtBox *box, float rgba[4], int mask)
{
  gboolean diff = FALSE;

  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), FALSE);
    
  if (mask & TOOL_COLOR_MASK_R && box->priv->sideRGB[0] != rgba[0])
    {
      box->priv->sideRGB[0] = rgba[0];
      diff = TRUE;
    }
  if (mask & TOOL_COLOR_MASK_G && box->priv->sideRGB[1] != rgba[1])
    {
      box->priv->sideRGB[1] = rgba[1];
      diff = TRUE;
    }
  if (mask & TOOL_COLOR_MASK_B && box->priv->sideRGB[2] != rgba[2])
    {
      box->priv->sideRGB[2] = rgba[2];
      diff = TRUE;
    }
  if (mask & TOOL_COLOR_MASK_A && box->priv->sideRGB[3] != rgba[3])
    {
      box->priv->sideRGB[3] = rgba[3];
      diff = TRUE;
    }
  if (!diff)
    return FALSE;

  visu_gl_ext_setDirty(VISU_GL_EXT(box), TRUE);
  g_object_notify_by_pspec(G_OBJECT(box), properties[SIDE_COLOR_PROP]);
  return TRUE;
}
static gboolean _setLineWidth(VisuGlExtLined *box, float width)
{
  VisuGlExtBoxPrivate *self;
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), FALSE);
  self = VISU_GL_EXT_BOX(box)->priv;
  
  self->lineWidth = width;
  visu_gl_ext_setDirty(VISU_GL_EXT(box), TRUE);
  return TRUE;
}
static gboolean _setLineStipple(VisuGlExtLined *box, guint16 stipple)
{
  VisuGlExtBoxPrivate *self;
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), FALSE);
  self = VISU_GL_EXT_BOX(box)->priv;

  self->lineStipple[0] = stipple;
  visu_gl_ext_setDirty(VISU_GL_EXT(box), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_box_setExpandStipple:
 * @box: the #VisuGlExtBox to update.
 * @stipple: a pattern for line stipple in OpenGL.
 *
 * Method used to change the value of the parameter box_line_stipple
 * (expanded part).
 *
 * Returns: TRUE if value is actually changed.
 */
gboolean visu_gl_ext_box_setExpandStipple(VisuGlExtBox *box, guint16 stipple)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), FALSE);
    
  if (stipple == box->priv->lineStipple[1])
    return FALSE;

  box->priv->lineStipple[1] = stipple;
  visu_gl_ext_setDirty(VISU_GL_EXT(box), TRUE);
  g_object_notify_by_pspec(G_OBJECT(box), properties[EXT_STIPPLE_PROP]);
  return TRUE;
}
/* Get methods. */
static float* _getRGB(const VisuGlExtLined *box)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), rgbDefault);
  
  return ((VisuGlExtBox*)box)->priv->rgb;
}
/**
 * visu_gl_ext_box_getSideRGB:
 * @box: the #VisuGlExtBox to inquire.
 *
 * Read the colour components of the sides of the box (in [0;1]). 
 *
 * Returns: all the colour values of the current box line.
 */
float* visu_gl_ext_box_getSideRGB(VisuGlExtBox *box)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), rgbDefault);
  
  return box->priv->sideRGB;
}
static float _getLineWidth(const VisuGlExtLined *box)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), LINE_WIDTH_DEFAULT);
  
  return ((VisuGlExtBox*)box)->priv->lineWidth;
}
static guint16 _getLineStipple(const VisuGlExtLined *box)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), stippleDefault[0]);
  
  return ((VisuGlExtBox*)box)->priv->lineStipple[0];
}
/**
 * visu_gl_ext_box_getExpandStipple:
 * @box: the #VisuGlExtBox to inquire.
 *
 * Read the line stipple pattern used for box (expanded part).
 *
 * Returns: the value of current box line pattern.
 */
guint16 visu_gl_ext_box_getExpandStipple(VisuGlExtBox *box)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX(box), stippleDefault[1]);
  
  return box->priv->lineStipple[1];
}


/****************/
/* Private part */
/****************/
static void visu_gl_ext_box_rebuild(VisuGlExt *ext)
{
  visu_gl_ext_box_draw(ext);
}
static void onSizeChanged(VisuBox *boxObj _U_, gfloat extens _U_, gpointer user_data)
{
  DBG_fprintf(stderr, "Extension Box: caught the 'SizeChanged' signal.\n");
  visu_gl_ext_setDirty(VISU_GL_EXT(user_data), TRUE);
}
static void onEntryUsed(VisuGlExtBox *box, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_setActive(VISU_GL_EXT(box), RESOURCE_BOX_USED_DEFAULT);
}
static void onEntryColor(VisuGlExtBox *box, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_lined_setRGBA(VISU_GL_EXT_LINED(box), rgbDefault, TOOL_COLOR_MASK_RGBA);
}
static void onEntryWidth(VisuGlExtBox *box, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_lined_setWidth(VISU_GL_EXT_LINED(box), LINE_WIDTH_DEFAULT);
}
static void onEntryStipple(VisuGlExtBox *box, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_lined_setStipple(VISU_GL_EXT_LINED(box), stippleDefault[0]);
  visu_gl_ext_box_setExpandStipple(box, stippleDefault[1]);
}
static void onEntrySide(VisuGlExtBox *box, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_box_setSideRGB(box, sideRGBDefault, TOOL_COLOR_MASK_RGBA);
}
static void drawSides(float ext[3], float v[8][3], float rgba[4])
{
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDisable(GL_CULL_FACE);
  glColor4fv(rgba);
  if (ext[0] > 0.)
    {
      glBegin(GL_POLYGON);
      glVertex3fv(v[0]);
      glVertex3fv(v[3]);
      glVertex3fv(v[7]);
      glVertex3fv(v[4]);
      glEnd();
      glBegin(GL_POLYGON);
      glVertex3fv(v[6]);
      glVertex3fv(v[5]);
      glVertex3fv(v[1]);
      glVertex3fv(v[2]);
      glEnd();
    }
  if (ext[1] > 0.)
    {
      glBegin(GL_POLYGON);
      glVertex3fv(v[0]);
      glVertex3fv(v[1]);
      glVertex3fv(v[5]);
      glVertex3fv(v[4]);
      glEnd();
      glBegin(GL_POLYGON);
      glVertex3fv(v[6]);
      glVertex3fv(v[7]);
      glVertex3fv(v[3]);
      glVertex3fv(v[2]);
      glEnd();
    }
  if (ext[2] > 0.)
    {
      glBegin(GL_POLYGON);
      glVertex3fv(v[0]);
      glVertex3fv(v[1]);
      glVertex3fv(v[2]);
      glVertex3fv(v[3]);
      glEnd();
      glBegin(GL_POLYGON);
      glVertex3fv(v[4]);
      glVertex3fv(v[5]);
      glVertex3fv(v[6]);
      glVertex3fv(v[7]);
      glEnd();
    }
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);
}
/**
 * visu_gl_ext_box_draw:
 * @box: a #VisuBox object.
 *
 * This method create a compile list that draw a box for the given @box.
 */
static void visu_gl_ext_box_draw(VisuGlExt *ext)
{
  int i, j, k;
  float v[8][3], e[8][3];
  float extens[3] = {0.f, 0.f, 0.f}, centre[3];
  float material[5] = {.5f, .5f, .2f, .5f, .0f};
  GLUquadricObj *obj;
  gchar strLg[64];
  VisuGlExtBox *box;

  /* Nothing to draw; */
  g_return_if_fail(VISU_IS_GL_EXT_BOX(ext));
  box = VISU_GL_EXT_BOX(ext);

  DBG_fprintf(stderr, "Extension box: creating box for"
	      " VisuBox %p.\n", (gpointer)box->priv->box);
  if (box->priv->box)
    {
      visu_box_getVertices(box->priv->box, v, FALSE);
      if (visu_box_getExtensionActive(box->priv->box))
        visu_box_getExtension(box->priv->box, extens);
    }
  else
    {
      /* We build the vertex array. */
      v[0][0] = 0.f;
      v[0][1] = 0.f;
      v[0][2] = 0.f;
      v[1][0] = box->priv->matrix[0][0];
      v[1][1] = box->priv->matrix[1][0];
      v[1][2] = box->priv->matrix[2][0];
      v[2][0] = box->priv->matrix[0][0] + box->priv->matrix[0][1];
      v[2][1] = box->priv->matrix[1][0] + box->priv->matrix[1][1];
      v[2][2] = box->priv->matrix[2][0] + box->priv->matrix[2][1];
      v[3][0] = box->priv->matrix[0][1];
      v[3][1] = box->priv->matrix[1][1];
      v[3][2] = box->priv->matrix[2][1];
      v[4][0] = box->priv->matrix[0][2];
      v[4][1] = box->priv->matrix[1][2];
      v[4][2] = box->priv->matrix[2][2];
      v[5][0] = box->priv->matrix[0][0] + box->priv->matrix[0][2];
      v[5][1] = box->priv->matrix[1][0] + box->priv->matrix[1][2];
      v[5][2] = box->priv->matrix[2][0] + box->priv->matrix[2][2];
      v[6][0] = box->priv->matrix[0][0] + box->priv->matrix[0][1] + box->priv->matrix[0][2];
      v[6][1] = box->priv->matrix[1][0] + box->priv->matrix[1][1] + box->priv->matrix[1][2];
      v[6][2] = box->priv->matrix[2][0] + box->priv->matrix[2][1] + box->priv->matrix[2][2];
      v[7][0] = box->priv->matrix[0][1] + box->priv->matrix[0][2];
      v[7][1] = box->priv->matrix[1][1] + box->priv->matrix[1][2];
      v[7][2] = box->priv->matrix[2][1] + box->priv->matrix[2][2];
      extens[0] = 1.f;
      extens[1] = 1.f;
      extens[2] = 1.f;
    }

  glDeleteLists(visu_gl_ext_getGlList(ext), 1);
  visu_gl_ext_startDrawing(ext);

  glDisable(GL_LIGHTING);
  glDisable(GL_DITHER);

  glLineWidth(box->priv->lineWidth);

  /* We draw sides of the box, if specified. */
  if ((extens[0] > 0. || extens[1] > 0. || extens[2] > 0.) &&
      box->priv->sideRGB[3] == 1.f)
    drawSides(extens, v, box->priv->sideRGB);
  /* Draw the basic lines. */
  glColor3fv(box->priv->rgb);
  if (box->priv->lineStipple[0] != 65535)
    {
      glEnable(GL_LINE_STIPPLE);
      glLineStipple(1, box->priv->lineStipple[0]);
    }
  glBegin(GL_LINES);
  glVertex3fv(v[0]);
  glVertex3fv(v[1]);
  glVertex3fv(v[1]);
  glVertex3fv(v[2]);
  glVertex3fv(v[2]);
  glVertex3fv(v[3]);
  glVertex3fv(v[3]);
  glVertex3fv(v[0]);
  glVertex3fv(v[4]);
  glVertex3fv(v[5]);
  glVertex3fv(v[5]);
  glVertex3fv(v[6]);
  glVertex3fv(v[6]);
  glVertex3fv(v[7]);
  glVertex3fv(v[7]);
  glVertex3fv(v[4]);
  glVertex3fv(v[0]);
  glVertex3fv(v[4]);
  glVertex3fv(v[1]);
  glVertex3fv(v[5]);
  glVertex3fv(v[2]);
  glVertex3fv(v[6]);
  glVertex3fv(v[3]);
  glVertex3fv(v[7]);
  glEnd();
  if (box->priv->lineStipple[0] != 65535)
    glDisable(GL_LINE_STIPPLE);
  /* Draw the extension lines. */
  if (extens[0] > 0. || extens[1] > 0. || extens[2] > 0.)
    {
      glColor3fv(box->priv->rgb);
      /* We draw then the expansion lines. */
      if (box->priv->lineStipple[1] != 65535)
	{
	  glEnable(GL_LINE_STIPPLE);
	  glLineStipple(1, box->priv->lineStipple[1]);
	}
      for (i = 1; i < 8; i++)
        {
          e[i][0] = v[i][0] - v[0][0];
          e[i][1] = v[i][1] - v[0][1];
          e[i][2] = v[i][2] - v[0][2];
        }
      glTranslatef(v[0][0], v[0][1], v[0][2]);
      glBegin(GL_LINES);
      /* X coordinate. */
      for (j = -(int)extens[1]; j < 2 + (int)extens[1]; j++)
	for (k = -(int)extens[2]; k < 2 + (int)extens[2]; k++)
	  {
	    glVertex3f(-extens[0] * e[1][0] +  e[3][0] * j + e[4][0] * k,
		       -extens[0] * e[1][1] +  e[3][1] * j + e[4][1] * k,
		       -extens[0] * e[1][2] +  e[3][2] * j + e[4][2] * k);
	    if ((j == 0 || j == 1) && (k == 0 || k == 1))
	      {
		glVertex3f(e[3][0] * j + e[4][0] * k,
			   e[3][1] * j + e[4][1] * k,
			   e[3][2] * j + e[4][2] * k);
		glVertex3f(e[1][0] +  e[3][0] * j + e[4][0] * k,
			   e[1][1] +  e[3][1] * j + e[4][1] * k,
			   e[1][2] +  e[3][2] * j + e[4][2] * k);
	      }
	    glVertex3f((1. + extens[0]) * e[1][0] +  e[3][0] * j + e[4][0] * k,
		       (1. + extens[0]) * e[1][1] +  e[3][1] * j + e[4][1] * k,
		       (1. + extens[0]) * e[1][2] +  e[3][2] * j + e[4][2] * k);
	  }
      /* Y coordinate. */
      for (i = -(int)extens[0]; i < 2 + (int)extens[0]; i++)
	for (k = -(int)extens[2]; k < 2 + (int)extens[2]; k++)
	  {
	    glVertex3f(-extens[1] * e[3][0] +  e[1][0] * i + e[4][0] * k,
		       -extens[1] * e[3][1] +  e[1][1] * i + e[4][1] * k,
		       -extens[1] * e[3][2] +  e[1][2] * i + e[4][2] * k);
	    if ((i == 0 || i == 1) && (k == 0 || k == 1))
	      {
		glVertex3f(e[1][0] * i + e[4][0] * k,
			   e[1][1] * i + e[4][1] * k,
			   e[1][2] * i + e[4][2] * k);
		glVertex3f(e[3][0] +  e[1][0] * i + e[4][0] * k,
			   e[3][1] +  e[1][1] * i + e[4][1] * k,
			   e[3][2] +  e[1][2] * i + e[4][2] * k);
	      }
	    glVertex3f((1. + extens[1]) * e[3][0] +  e[1][0] * i + e[4][0] * k,
		       (1. + extens[1]) * e[3][1] +  e[1][1] * i + e[4][1] * k,
		       (1. + extens[1]) * e[3][2] +  e[1][2] * i + e[4][2] * k);
	  }
      /* Z coordinate. */
      for (i = -(int)extens[0]; i < 2 + (int)extens[0]; i++)
	for (j = -(int)extens[1]; j < 2 + (int)extens[1]; j++)
	  {
	    glVertex3f(-extens[2] * e[4][0] +  e[1][0] * i + e[3][0] * j,
		       -extens[2] * e[4][1] +  e[1][1] * i + e[3][1] * j,
		       -extens[2] * e[4][2] +  e[1][2] * i + e[3][2] * j);
	    if ((j == 0 || j == 1) && (i == 0 || i == 1))
	      {
		glVertex3f(e[1][0] * i + e[3][0] * j,
			   e[1][1] * i + e[3][1] * j,
			   e[1][2] * i + e[3][2] * j);
		glVertex3f(e[4][0] +  e[1][0] * i + e[3][0] * j,
			   e[4][1] +  e[1][1] * i + e[3][1] * j,
			   e[4][2] +  e[1][2] * i + e[3][2] * j);
	      }
	    glVertex3f((1. + extens[2]) * e[4][0] +  e[1][0] * i + e[3][0] * j,
		       (1. + extens[2]) * e[4][1] +  e[1][1] * i + e[3][1] * j,
		       (1. + extens[2]) * e[4][2] +  e[1][2] * i + e[3][2] * j);
	  }
      glEnd();
      if (box->priv->lineStipple[1] != 65535)
	glDisable(GL_LINE_STIPPLE);
      glTranslatef(-v[0][0], -v[0][1], -v[0][2]);
    }
  /* We draw sides of the box, if specified. */
  if ((extens[0] > 0. || extens[1] > 0. || extens[2] > 0.) &&
      box->priv->sideRGB[3] > 0.f && box->priv->sideRGB[3] < 1.f)
    drawSides(extens, v, box->priv->sideRGB);

  glEnable(GL_LIGHTING);
  glEnable(GL_DITHER); /* WARNING: it is the default! */
  /* Draw the basis set if needed. */
  if (FALSE)
    {
      visu_box_getCentre(box->priv->box, centre);

      obj = gluNewQuadric();
      visu_gl_setHighlightColor((VisuGl*)0, material, box->priv->rgb, 1.f);

      /* Draw the basis set. */
      glPushMatrix();
      glTranslated(0., 0., 0.);
      glRotated(90., 0, 1, 0);  
      visu_gl_drawSmoothArrow(obj, VISU_GL_ARROW_BOTTOM_CENTERED,
                              basisLength - 0.3f, 0.1f, 10, NULL,
                              0.3f, 0.2f, 10, NULL);
      glRasterPos3f(0.0f, 0.0f, basisLength);
      sprintf(strLg, _("x: %7.3f"), centre[0]);
      visu_gl_text_drawChars(strLg, VISU_GL_TEXT_SMALL); 
      glPopMatrix();
      glPushMatrix();
      glTranslated(0., 0., 0.);
      glRotated(-90., 1, 0, 0);  
      visu_gl_drawSmoothArrow(obj, VISU_GL_ARROW_BOTTOM_CENTERED,
                              basisLength - 0.3f, 0.1f, 10, NULL,
                              0.3f, 0.2f, 10, NULL);
      glRasterPos3f(0.0f, 0.0f, basisLength);
      sprintf(strLg, _("y: %7.3f"), centre[1]);
      visu_gl_text_drawChars(strLg, VISU_GL_TEXT_SMALL); 
      glPopMatrix();
      glPushMatrix();
      glTranslated(0., 0., 0.);
      visu_gl_drawSmoothArrow(obj, VISU_GL_ARROW_BOTTOM_CENTERED,
                              basisLength - 0.3f, 0.1f, 10, NULL,
                              0.3f, 0.2f, 10, NULL);
      glRasterPos3f(0.0f, 0.0f, basisLength);
      sprintf(strLg, _("z: %7.3f"), centre[2]);
      visu_gl_text_drawChars(strLg, VISU_GL_TEXT_SMALL); 
      glPopMatrix();

      gluDeleteQuadric(obj);
    }
  glLineWidth(1.);

  visu_gl_ext_completeDrawing(ext);
}

/* Parameters & resources*/
/* Export function that is called by visu_module to write the
   values of resources to a file. */
static void exportResourcesBox(GString *data, VisuData *dataObj _U_)
{
  if (!defaultBox)
    return;

  visu_config_file_exportComment(data, DESC_RESOURCE_BOX_USED);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_BOX_USED, NULL,
                               "%d", visu_gl_ext_getActive(VISU_GL_EXT(defaultBox)));

  visu_config_file_exportComment(data, DESC_RESOURCE_BOX_COLOR);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_BOX_COLOR, NULL,
                               "%4.3f %4.3f %4.3f",
                               defaultBox->priv->rgb[0], defaultBox->priv->rgb[1],
                               defaultBox->priv->rgb[2]);

  visu_config_file_exportComment(data, DESC_RESOURCE_BOX_LINE);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_BOX_LINE, NULL,
                               "%4.0f", defaultBox->priv->lineWidth);

  visu_config_file_exportComment(data, DESC_RESOURCE_BOX_STIPPLE);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_BOX_STIPPLE, NULL,
                               "%d %d", defaultBox->priv->lineStipple[0],
                               defaultBox->priv->lineStipple[1]);

  visu_config_file_exportComment(data, DESC_RESOURCE_BOX_SIDE);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_BOX_SIDE, NULL,
                               "%4.3f %4.3f %4.3f  %4.3f",
                               defaultBox->priv->sideRGB[0], defaultBox->priv->sideRGB[1],
                               defaultBox->priv->sideRGB[2], defaultBox->priv->sideRGB[3]);

  visu_config_file_exportComment(data, "");
}
