/*   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 "box_legend.h"

#include <visu_data.h>
#include <visu_configFile.h>

#include <openGLFunctions/text.h>

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

/**
 * SECTION:box_legend
 * @short_description: Draw a legend describing the box.
 *
 * <para>This extension allows V_Sim to draw a small box with box
 * information like the box size.</para>
 */

/* A resource to control the position to render the box lengths. */
#define FLAG_RESOURCE_LEGEND_POSITION   "box_legend_position"
#define DESC_RESOURCE_LEGEND_POSITION   "Position of the legend area for the box ; two floating point values (0. <= v <= 1.)"
static float POSITION_DEFAULT[2] = {.5f, 0.f};

/* A resource to control if the box lengths are printed. */
#define FLAG_RESOURCE_BOX_LENGTHS   "box_show_lengths"
#define DESC_RESOURCE_BOX_LENGTHS   "Print the box lengths ; boolean (0 or 1)"
static gboolean WITH_LENGTHS_DEFAULT = FALSE;
static void exportResourcesBoxLegend(GString *data, VisuData *dataObj);

/**
 * VisuGlExtBoxLegendClass:
 * @parent: the parent class;
 *
 * A short way to identify #_VisuGlExtBoxLegendClass structure.
 *
 * Since: 3.7
 */
/**
 * VisuGlExtBoxLegend:
 *
 * An opaque structure.
 *
 * Since: 3.7
 */
/**
 * VisuGlExtBoxLegendPrivate:
 *
 * Private fields for #VisuGlExtBoxLegend objects.
 *
 * Since: 3.7
 */
struct _VisuGlExtBoxLegendPrivate
{
  gboolean dispose_has_run;

  /* Box definition. */
  VisuBox *box;
  gulong box_signal;
};
static VisuGlExtBoxLegend* defaultBoxLegend;

static void visu_gl_ext_box_legend_dispose(GObject* obj);
static void visu_gl_ext_box_legend_rebuild(VisuGlExt *ext);
static void visu_gl_ext_box_legend_draw(const VisuGlExtFrame *frame);
static gboolean visu_gl_ext_box_legend_isValid(const VisuGlExtFrame *frame);

/* Callbacks. */
static void onBoxSizeChanged(VisuBox *box, gfloat extens, gpointer user_data);
static void onEntryLgUsed(VisuGlExtBoxLegend *lg, VisuConfigFileEntry *entry, VisuConfigFile *obj);
static void onEntryLgPosition(VisuGlExtBoxLegend *lg, VisuConfigFileEntry *entry, VisuConfigFile *obj);

G_DEFINE_TYPE_WITH_CODE(VisuGlExtBoxLegend, visu_gl_ext_box_legend, VISU_TYPE_GL_EXT_FRAME,
                        G_ADD_PRIVATE(VisuGlExtBoxLegend))

static void visu_gl_ext_box_legend_class_init(VisuGlExtBoxLegendClass *klass)
{
  float rgPos[2] = {0.f, 1.f};
  VisuConfigFileEntry *resourceEntry;

  DBG_fprintf(stderr, "Extension Box: creating the class of the object (legend).\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_LENGTHS,
                                                   DESC_RESOURCE_BOX_LENGTHS,
                                                   &WITH_LENGTHS_DEFAULT, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.6f);
  resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                      FLAG_RESOURCE_LEGEND_POSITION,
                                                      DESC_RESOURCE_LEGEND_POSITION,
                                                      2, POSITION_DEFAULT, rgPos, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.7f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE,
                                     exportResourcesBoxLegend);

  defaultBoxLegend = (VisuGlExtBoxLegend*)0;

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_box_legend_dispose;
  VISU_GL_EXT_CLASS(klass)->rebuild = visu_gl_ext_box_legend_rebuild;
  VISU_GL_EXT_FRAME_CLASS(klass)->draw = visu_gl_ext_box_legend_draw;
  VISU_GL_EXT_FRAME_CLASS(klass)->isValid = visu_gl_ext_box_legend_isValid;
}

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

  /* Private data. */
  obj->priv->box        = (VisuBox*)0;
  obj->priv->box_signal = 0;

  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_BOX_LENGTHS,
                          G_CALLBACK(onEntryLgUsed), (gpointer)obj, G_CONNECT_SWAPPED);
  g_signal_connect_object(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_LEGEND_POSITION,
                          G_CALLBACK(onEntryLgPosition), (gpointer)obj, G_CONNECT_SWAPPED);

  if (!defaultBoxLegend)
    defaultBoxLegend = obj;
}
static void visu_gl_ext_box_legend_dispose(GObject* obj)
{
  VisuGlExtBoxLegend *legend;

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

  legend = VISU_GL_EXT_BOX_LEGEND(obj);
  if (legend->priv->dispose_has_run)
    return;
  legend->priv->dispose_has_run = TRUE;

  /* Disconnect signals. */
  visu_gl_ext_box_legend_setBox(legend, (VisuBox*)0);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_gl_ext_box_legend_parent_class)->dispose(obj);
}

/**
 * visu_gl_ext_box_legend_new:
 * @name: (allow-none): the name to give to the extension.
 *
 * Creates a new #VisuGlExt to draw a legend with the box size.
 *
 * Since: 3.7
 *
 * Returns: a pointer to the #VisuGlExt it created or
 * NULL otherwise.
 */
VisuGlExtBoxLegend* visu_gl_ext_box_legend_new(const gchar *name)
{
  char *name_ = VISU_GL_EXT_BOX_LEGEND_ID;
  char *description = _("Draw informations related to the box.");
  VisuGlExt *extensionBox;
#define BOX_WIDTH   100
#define BOX_HEIGHT   55

  DBG_fprintf(stderr,"Extension Box: new legend object.\n");
  
  extensionBox = VISU_GL_EXT(g_object_new(VISU_TYPE_GL_EXT_BOX_LEGEND,
                                          "active", WITH_LENGTHS_DEFAULT,
                                          "name", (name)?name:name_, "label", _(name),
                                          "description", description, "nGlObj", 1,
                                          "priority", VISU_GL_EXT_PRIORITY_LAST,
                                          "saveState", TRUE, NULL));
  visu_gl_ext_frame_setTitle(VISU_GL_EXT_FRAME(extensionBox), _("Box lengths"));
  visu_gl_ext_frame_setPosition(VISU_GL_EXT_FRAME(extensionBox),
                                POSITION_DEFAULT[0], POSITION_DEFAULT[1]);
  visu_gl_ext_frame_setRequisition(VISU_GL_EXT_FRAME(extensionBox), BOX_WIDTH, BOX_HEIGHT);

  return VISU_GL_EXT_BOX_LEGEND(extensionBox);
}
/**
 * visu_gl_ext_box_legend_setBox:
 * @legend: The #VisuGlExtBoxLegend to attached to.
 * @boxObj: the box to get the size of.
 *
 * Attach an #VisuGlView to render to and setup the box to get the
 * size of also.
 *
 * Since: 3.7
 *
 * Returns: TRUE if model is changed.
 **/
gboolean visu_gl_ext_box_legend_setBox(VisuGlExtBoxLegend *legend, VisuBox *boxObj)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX_LEGEND(legend), FALSE);

  DBG_fprintf(stderr, "Extension Box Legend: set box %p.\n", (gpointer)boxObj);

  if (legend->priv->box == boxObj)
    return FALSE;

  if (legend->priv->box)
    {
      g_signal_handler_disconnect(G_OBJECT(legend->priv->box), legend->priv->box_signal);
      g_object_unref(legend->priv->box);
    }
  if (boxObj)
    {
      g_object_ref(boxObj);
      legend->priv->box_signal =
        g_signal_connect(G_OBJECT(boxObj), "SizeChanged",
                         G_CALLBACK(onBoxSizeChanged), (gpointer)legend);
    }
  else
    legend->priv->box_signal = 0;
  legend->priv->box = boxObj;

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


/****************/
/* Private part */
/****************/
static void visu_gl_ext_box_legend_rebuild(VisuGlExt *ext)
{
  visu_gl_text_rebuildFontList();
  if (VISU_GL_EXT_GET_CLASS(ext)->draw)
    VISU_GL_EXT_GET_CLASS(ext)->draw(ext);
}
static void onBoxSizeChanged(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 onEntryLgUsed(VisuGlExtBoxLegend *lg, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_setActive(VISU_GL_EXT(lg), WITH_LENGTHS_DEFAULT);
}
static void onEntryLgPosition(VisuGlExtBoxLegend *lg, VisuConfigFileEntry *entry _U_, VisuConfigFile *obj _U_)
{
  visu_gl_ext_frame_setPosition(VISU_GL_EXT_FRAME(lg),
                                POSITION_DEFAULT[0], POSITION_DEFAULT[1]);
}
static gboolean visu_gl_ext_box_legend_isValid(const VisuGlExtFrame *frame)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_BOX_LEGEND(frame), FALSE);

  /* Nothing to draw if no data is associated to
     the current rendering window. */
  return (VISU_GL_EXT_BOX_LEGEND(frame)->priv->box != (VisuBox*)0);
}
static void visu_gl_ext_box_legend_draw(const VisuGlExtFrame *frame)
{
  gchar strLg[64];
  float vertices[8][3], box[3];
  VisuGlExtBoxLegend *legend;

  g_return_if_fail(VISU_IS_GL_EXT_BOX_LEGEND(frame));
  legend = VISU_GL_EXT_BOX_LEGEND(frame);

  visu_box_getVertices(legend->priv->box, vertices, FALSE);
  box[0] = sqrt((vertices[1][0] - vertices[0][0]) *
		(vertices[1][0] - vertices[0][0]) +
		(vertices[1][1] - vertices[0][1]) *
		(vertices[1][1] - vertices[0][1]) +
		(vertices[1][2] - vertices[0][2]) *
		(vertices[1][2] - vertices[0][2]));
  box[1] = sqrt((vertices[3][0] - vertices[0][0]) *
		(vertices[3][0] - vertices[0][0]) +
		(vertices[3][1] - vertices[0][1]) *
		(vertices[3][1] - vertices[0][1]) +
		(vertices[3][2] - vertices[0][2]) *
		(vertices[3][2] - vertices[0][2]));
  box[2] = sqrt((vertices[4][0] - vertices[0][0]) *
		(vertices[4][0] - vertices[0][0]) +
		(vertices[4][1] - vertices[0][1]) *
		(vertices[4][1] - vertices[0][1]) +
		(vertices[4][2] - vertices[0][2]) *
		(vertices[4][2] - vertices[0][2]));

  glColor3fv(frame->fontRGB);

  glRasterPos2f(0.f, frame->height * 2.f / 3.f);
  sprintf(strLg, "  %s: %7.3f", "x", box[0]);
  visu_gl_text_drawChars(strLg, VISU_GL_TEXT_SMALL); 

  glRasterPos2f(0.f, frame->height / 3.f);
  sprintf(strLg, "  %s: %7.3f", "y", box[1]);
  visu_gl_text_drawChars(strLg, VISU_GL_TEXT_SMALL); 

  glRasterPos2f(0.f, 0.f);
  sprintf(strLg, "  %s: %7.3f", "z", box[2]);
  visu_gl_text_drawChars(strLg, VISU_GL_TEXT_SMALL); 
}

/* Parameters & resources*/
/* Export function that is called by visu_module to write the
   values of resources to a file. */
static void exportResourcesBoxLegend(GString *data, VisuData *dataObj _U_)
{
  float xpos, ypos;

  if (!defaultBoxLegend)
    return;

  visu_config_file_exportComment(data, DESC_RESOURCE_BOX_LENGTHS);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_BOX_LENGTHS, NULL,
                               "%d", visu_gl_ext_getActive(VISU_GL_EXT(defaultBoxLegend)));

  visu_gl_ext_frame_getPosition(VISU_GL_EXT_FRAME(defaultBoxLegend), &xpos, &ypos);
  visu_config_file_exportComment(data, DESC_RESOURCE_LEGEND_POSITION);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_LEGEND_POSITION, NULL,
                               "%4.3f %4.3f", xpos, ypos);

  visu_config_file_exportComment(data, "");
}
