/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD, Damien
	CALISTE, Olivier D'Astier, laboratoire L_Sim, (2001-2005)
  
	Adresse 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-2005)

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

#include <string.h>

#include <visu_tools.h>
#include <visu_configFile.h>

#include <openGLFunctions/light.h>

/**
 * SECTION:surfaces_resources
 * @short_description: Define the rendering parameters of a surface.
 *
 * <para>This structure stores all the rendering elements of a set of #VisuSurface.</para>
 */

/**
 * VisuSurfaceResource:
 * @label: the name of the surface (in UTF-8) ;
 * @color: a #ToolColor for the surface ;
 * @material: the lighting effects of the surface ;
 * @rendered: boolean for the visibility of the surface ;
 * @sensitiveToPlanes: boolean for the sensitivity of a surface
 * to the masking effect of planes.
 *
 * This structure defines some drawing properties of a set of #VisuSurface.
 */
struct _VisuSurfaceResourcePrivate
{
  /* Name used to label the surface. */
  gchar *label;

  /* ToolMaterialIds used to draw a specific surface. */
  ToolColor color;
  float material[TOOL_MATERIAL_N_VALUES];

  /* Rendered or not */
  gboolean rendered;

  /* Sensitive to masking properties of planes. */
  gboolean sensitiveToPlanes;
};


#define DESC_RESOURCE_COLOR      "Define the colour of one surface ;"	\
  " 4 floats (RGBA) 5 floats (material)"
#define FLAG_RESOURCE_COLOR      "isosurface_color"
static gfloat _colorMat[9];
#define DESC_RESOURCE_PROPERTIES "Define some surface properties ;"	\
  " rendered (0 or 1) sensitive to planes (0 or 1)"
#define FLAG_RESOURCE_PROPERTIES "isosurface_properties"
static gboolean _prop[2];

/* Local variables. */
static GHashTable *isosurfaces_resources = NULL;

/* Local methods. */
static void isosurfaces_export_resources(GString *data,
                                         VisuData *dataObj);
static void onEntryColor(VisuConfigFile *obj, VisuConfigFileEntry *entry, gpointer data);
static void onEntryProperties(VisuConfigFile *obj, VisuConfigFileEntry *entry, gpointer data);

enum
  {
    PROP_0,
    LABEL_PROP,
    COLOR_PROP,
    MATERIAL_PROP,
    RENDERED_PROP,
    MASKING_PROP,
    N_PROP
  };
static GParamSpec *properties[N_PROP];

static void visu_surface_resource_finalize    (GObject* obj);
static void visu_surface_resource_get_property(GObject* obj, guint property_id,
                                               GValue *value, GParamSpec *pspec);
static void visu_surface_resource_set_property(GObject* obj, guint property_id,
                                               const GValue *value, GParamSpec *pspec);

G_DEFINE_TYPE_WITH_CODE(VisuSurfaceResource, visu_surface_resource, VISU_TYPE_OBJECT,
                        G_ADD_PRIVATE(VisuSurfaceResource))

static void visu_surface_resource_class_init(VisuSurfaceResourceClass *klass)
{
  VisuConfigFileEntry *entry, *oldEntry;
  gfloat rgColor[2] = {0.f, 1.f};

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

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->finalize = visu_surface_resource_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_surface_resource_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_surface_resource_get_property;

  /**
   * VisuSurfaceResource::label:
   *
   * Label used to identify the resource.
   *
   * Since: 3.8
   */
  properties[LABEL_PROP] = g_param_spec_string("label", "Label",
                                               "label of the resource",
                                               NULL, G_PARAM_READWRITE |
                                               G_PARAM_CONSTRUCT_ONLY |
                                               G_PARAM_STATIC_STRINGS);
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), LABEL_PROP, properties[LABEL_PROP]);
  /**
   * VisuSurfaceResource::color:
   *
   * Rendering colour.
   *
   * Since: 3.8
   */
  properties[COLOR_PROP] = g_param_spec_boxed("color", "Color", "rendering color",
                                              TOOL_TYPE_COLOR, G_PARAM_READWRITE |
                                               G_PARAM_STATIC_STRINGS);
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), COLOR_PROP, properties[COLOR_PROP]);
  /**
   * VisuSurfaceResource::material:
   *
   * Rendering material properties.
   *
   * Since: 3.8
   */
  properties[MATERIAL_PROP] = g_param_spec_boxed("material", "Material", "rendering material",
                                              TOOL_TYPE_MATERIAL, G_PARAM_READWRITE |
                                               G_PARAM_STATIC_STRINGS);
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), MATERIAL_PROP, properties[MATERIAL_PROP]);
  /**
   * VisuSurfaceResource::rendered:
   *
   * If the surface is displayed or not.
   *
   * Since: 3.8
   */
  properties[RENDERED_PROP] = g_param_spec_boolean("rendered", "Rendered", "rendered or not",
                                                  FALSE, G_PARAM_READWRITE |
                                                  G_PARAM_STATIC_STRINGS);
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), RENDERED_PROP, properties[RENDERED_PROP]);
  /**
   * VisuSurfaceResource::maskable:
   *
   * If the surface is maskable by planes or not.
   *
   * Since: 3.8
   */
  properties[MASKING_PROP] = g_param_spec_boolean("maskable", "Maskable", "masked or not",
                                                  FALSE, G_PARAM_READWRITE |
                                                  G_PARAM_STATIC_STRINGS);
  g_object_class_install_property
    (G_OBJECT_CLASS(klass), MASKING_PROP, properties[MASKING_PROP]);

  /* This entry is now obsolete but is kept for backward compatibility. */
  oldEntry = visu_config_file_addEntry(VISU_CONFIG_FILE_RESOURCE,
                                       "isosurface_property",
                                       "Properties of a given isosurface",
                                       1, NULL);
  visu_config_file_entry_setVersion(oldEntry, 3.3f);
  entry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                              FLAG_RESOURCE_COLOR,
                                              DESC_RESOURCE_COLOR,
                                              9, _colorMat, rgColor, TRUE);
  visu_config_file_entry_setVersion(entry, 3.4f);
  visu_config_file_entry_setReplace(entry, oldEntry);
  g_signal_connect(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_COLOR,
                   G_CALLBACK(onEntryColor), (gpointer)0);
  entry = visu_config_file_addBooleanArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                                FLAG_RESOURCE_PROPERTIES,
                                                DESC_RESOURCE_PROPERTIES,
                                                2, _prop, TRUE);
  visu_config_file_entry_setVersion(entry, 3.4f);
  g_signal_connect(VISU_CONFIG_FILE_RESOURCE, "parsed::" FLAG_RESOURCE_PROPERTIES,
                   G_CALLBACK(onEntryProperties), (gpointer)0);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE,
                                     isosurfaces_export_resources);

  isosurfaces_resources = g_hash_table_new_full(g_str_hash, g_str_equal,
						NULL, g_object_unref);
}
static void visu_surface_resource_init(VisuSurfaceResource *res)
{
  const float rgba[4] = {1.f, 0.5f, 0.5f, 0.75f};

  DBG_fprintf(stderr, "Surface Resource: initializing a new object (%p).\n",
	      (gpointer)res);
  res->priv = visu_surface_resource_get_instance_private(res);

  tool_color_copy(&res->priv->color, tool_color_addFloatRGBA(rgba, NULL));
  res->priv->material[0] = 0.2f;
  res->priv->material[1] = 1.0f;
  res->priv->material[2] = 0.5f;
  res->priv->material[3] = 0.5f;
  res->priv->material[4] = 0.0f;
  res->priv->rendered = TRUE;
  res->priv->label = (gchar*)0;
  res->priv->sensitiveToPlanes = TRUE;
}

/* This method is called once only. */
static void visu_surface_resource_finalize(GObject* obj)
{
  VisuSurfaceResourcePrivate *res;

  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Surface Resource: finalize object %p.\n", (gpointer)obj);
  res = VISU_SURFACE_RESOURCE(obj)->priv;

  g_free(res->label);

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Visu Plane Res: chain to parent.\n");
  G_OBJECT_CLASS(visu_surface_resource_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Visu Plane Res: freeing ... OK.\n");
}
static void visu_surface_resource_get_property(GObject* obj, guint property_id,
                                               GValue *value, GParamSpec *pspec)
{
  VisuSurfaceResourcePrivate *self = VISU_SURFACE_RESOURCE(obj)->priv;

  DBG_fprintf(stderr, "Visu SurfaceResource: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case LABEL_PROP:
      g_value_set_static_string(value, self->label);
      DBG_fprintf(stderr, "%s.\n", self->label);
      break;
    case COLOR_PROP:
      g_value_set_static_boxed(value, (gconstpointer)&self->color);
      DBG_fprintf(stderr, "%g,%g,%g.\n", self->color.rgba[0], self->color.rgba[1], self->color.rgba[2]);
      break;
    case MATERIAL_PROP:
      g_value_set_static_boxed(value, (gconstpointer)self->material);
      DBG_fprintf(stderr, "%g,%g,%g,%g,%g.\n",
                  self->material[0], self->material[1], self->material[2], self->material[3], self->material[4]);
      break;
    case RENDERED_PROP:
      g_value_set_boolean(value, self->rendered);
      DBG_fprintf(stderr, "%d.\n", self->rendered);
      break;
    case MASKING_PROP:
      g_value_set_boolean(value, self->sensitiveToPlanes);
      DBG_fprintf(stderr, "%d.\n", self->sensitiveToPlanes);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

static void visu_surface_resource_set_property(GObject* obj, guint property_id,
                                               const GValue *value, GParamSpec *pspec)
{
  VisuSurfaceResourcePrivate *self = VISU_SURFACE_RESOURCE(obj)->priv;
  float *mat;

  DBG_fprintf(stderr, "Visu SurfaceResource: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case LABEL_PROP:
      g_free(self->label);
      self->label = g_value_dup_string(value);
      if (self->label)
        {
          g_strstrip(self->label);
          if (!self->label[0])
            {
              g_free(self->label);
              self->label = (gchar*)0;
            }
        }
      DBG_fprintf(stderr, "%s.\n", self->label);
      break;
    case COLOR_PROP:
      tool_color_copy(&self->color, (ToolColor*)g_value_get_boxed(value));
      DBG_fprintf(stderr, "%g,%g,%g (%p).\n", self->color.rgba[0],
                  self->color.rgba[1], self->color.rgba[2], (gpointer)&self->color);
      break;
    case MATERIAL_PROP:
      mat = (float*)g_value_get_boxed(value);
      self->material[0] = mat[0];
      self->material[1] = mat[1];
      self->material[2] = mat[2];
      self->material[3] = mat[3];
      self->material[4] = mat[4];
      DBG_fprintf(stderr, "%g,%g,%g,%g,%g.\n", self->material[0],
                  self->material[1], self->material[2], self->material[3], self->material[4]);
      break;
    case RENDERED_PROP:
      self->rendered = g_value_get_boolean(value);
      DBG_fprintf(stderr, "%d.\n", self->rendered);
      break;
    case MASKING_PROP:
      self->sensitiveToPlanes = g_value_get_boolean(value);
      DBG_fprintf(stderr, "%d.\n", self->sensitiveToPlanes);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_surface_resource_new_fromName:
 * @surf_name: the name of the surface (can be NULL) ;
 * @new_surf: a location to store a boolean value (can be NULL).
 * 
 * This returns the resource information matching the given @surf_name. If
 * the resource doesn't exist, it is created and @new is set to TRUE. If the given
 * name (@surf_name) is NULL, then a new resource is created, but it is not stored
 * and will not be shared by surfaces.
 *
 * Returns: (transfer full): the resource (created or retrieved).
 */
VisuSurfaceResource* visu_surface_resource_new_fromName(const gchar *surf_name,
                                                        gboolean *nw)
{
  VisuSurfaceResource *res;

  if (!isosurfaces_resources)
    g_type_class_ref(VISU_TYPE_SURFACE_RESOURCE);

  res = (VisuSurfaceResource*)0;
  if (surf_name && surf_name[0])
    res = g_hash_table_lookup(isosurfaces_resources, surf_name);

  if (nw)
    *nw = (!res);

  if (res)
    {
      g_object_ref(G_OBJECT(res));     
      return res;
    }

  res = VISU_SURFACE_RESOURCE(g_object_new(VISU_TYPE_SURFACE_RESOURCE,
                                           "label", surf_name, NULL));
  if (res->priv->label)
    {
      g_object_ref(G_OBJECT(res));     
      g_hash_table_insert(isosurfaces_resources, res->priv->label, res);
    }
  return res;
}
/**
 * visu_surface_resource_pool_finalize: (skip)
 *
 * Free memory allocated to store various #VisuSurfaceResource objects.
 *
 * Since: 3.8
 **/
void visu_surface_resource_pool_finalize(void)
{
  g_hash_table_destroy(isosurfaces_resources);
  isosurfaces_resources = (GHashTable*)0;
}

/**
 * visu_surface_resource_new_fromCopy:
 * @surf_name: a label.
 * @orig: a #VisuSurfaceResource obejct.
 *
 * Copy constructor of #VisuSurfaceResource objects.
 *
 * Since: 3.8
 *
 * Returns: (transfer full): a newly created #VisuSurfaceResource object.
 **/
VisuSurfaceResource* visu_surface_resource_new_fromCopy(const gchar *surf_name,
                                                        const VisuSurfaceResource *orig)
{
  VisuSurfaceResource *res;
  gboolean nw;

  g_return_val_if_fail(VISU_IS_SURFACE_RESOURCE(orig), (VisuSurfaceResource*)0);

  res = visu_surface_resource_new_fromName(surf_name, &nw);

  if (nw)
    {
      res->priv->color = orig->priv->color;
      res->priv->material[0] = orig->priv->material[0];
      res->priv->material[1] = orig->priv->material[1];
      res->priv->material[2] = orig->priv->material[2];
      res->priv->material[3] = orig->priv->material[3];
      res->priv->material[4] = orig->priv->material[4];
      res->priv->rendered = orig->priv->rendered;
      res->priv->sensitiveToPlanes = orig->priv->sensitiveToPlanes;
    }

  return res;
}

/**
 * visu_surface_resource_getLabel:
 * @res: a #VisuSurfaceResource object.
 *
 * Retrieves the label of @res.
 *
 * Since: 3.8
 *
 * Returns: a string.
 **/
const gchar* visu_surface_resource_getLabel(const VisuSurfaceResource *res)
{
  g_return_val_if_fail(VISU_IS_SURFACE_RESOURCE(res), (const gchar*)0);

  return res->priv->label;
}

/**
 * visu_surface_resource_getColor:
 * @res: a #VisuSurfaceResource object.
 *
 * Retrieves the color of @res.
 *
 * Since: 3.8
 *
 * Returns: a color.
 **/
const ToolColor* visu_surface_resource_getColor(const VisuSurfaceResource *res)
{
  g_return_val_if_fail(VISU_IS_SURFACE_RESOURCE(res), (const ToolColor*)0);

  return &res->priv->color;
}

/**
 * visu_surface_resource_getMaterial:
 * @res: a #VisuSurfaceResource object.
 *
 * Retrieves the material of @res.
 *
 * Since: 3.8
 *
 * Returns: a material.
 **/
const float* visu_surface_resource_getMaterial(const VisuSurfaceResource *res)
{
  g_return_val_if_fail(VISU_IS_SURFACE_RESOURCE(res), (const float*)0);

  return res->priv->material;
}

/**
 * visu_surface_resource_getRendered:
 * @res: a #VisuSurfaceResource object.
 *
 * Retrieves wether @res is drawn or not.
 *
 * Since: 3.8
 *
 * Returns: a boolean.
 **/
gboolean visu_surface_resource_getRendered(const VisuSurfaceResource *res)
{
  g_return_val_if_fail(VISU_IS_SURFACE_RESOURCE(res), FALSE);

  return res->priv->rendered;
}

/**
 * visu_surface_resource_getMaskable:
 * @res: a #VisuSurfaceResource object.
 *
 * Retrieves if @res is sensitive to masking properties.
 *
 * Since: 3.8
 *
 * Returns: a boolean.
 **/
gboolean visu_surface_resource_getMaskable(const VisuSurfaceResource *res)
{
  g_return_val_if_fail(VISU_IS_SURFACE_RESOURCE(res), FALSE);

  return res->priv->sensitiveToPlanes;
}

/******************/
/* Local methods. */
/******************/
static void onEntryColor(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry, gpointer data _U_)
{
  VisuSurfaceResource *res;
  ToolColor color;

  DBG_fprintf(stderr, "Isosurfaces Resources: resources '%s' found for surface '%s'\n",
	      FLAG_RESOURCE_COLOR, visu_config_file_entry_getLabel(entry));

  /* Read resources are always made public and added in the hashtable. */
  res = visu_surface_resource_new_fromName(visu_config_file_entry_getLabel(entry),
                                           (gboolean*)0);
  memcpy(color.rgba, _colorMat, sizeof(gfloat) * 4);
  g_object_set(G_OBJECT(res), "color", &color, "material", _colorMat + 4, NULL);
  g_object_unref(G_OBJECT(res));  
}
static void onEntryProperties(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry, gpointer data _U_)
{
  VisuSurfaceResource *res;

  DBG_fprintf(stderr, "Surface Resource: resources '%s' found for surface '%s'\n",
	      FLAG_RESOURCE_PROPERTIES, visu_config_file_entry_getLabel(entry));

  /* Read resources are always made public and added in the hashtable. */
  res = visu_surface_resource_new_fromName(visu_config_file_entry_getLabel(entry),
                                           (gboolean*)0);
  g_object_set(G_OBJECT(res), "rendered", _prop[0], "maskable", _prop[1], NULL);
  g_object_unref(G_OBJECT(res));
}
static void isosurfaces_export_one_surf_resources(gpointer key, gpointer value,
						  gpointer user_data)
{
  VisuSurfaceResource *res;

  res = (VisuSurfaceResource*)value;
  DBG_fprintf(stderr, "Isosurfaces Resources: exporting surface '%s' properties...",
	      (char *)key);
  visu_config_file_exportEntry((GString*)user_data, FLAG_RESOURCE_COLOR, (gchar *)key,
                               "%4.3f %4.3f %4.3f %4.3f   %4.2f %4.2f %4.2f %4.2f %4.2f",
                               res->priv->color.rgba[0], res->priv->color.rgba[1],
                               res->priv->color.rgba[2], res->priv->color.rgba[3],
                               res->priv->material[0], res->priv->material[1],
                               res->priv->material[2], res->priv->material[3],
                               res->priv->material[4]);
  visu_config_file_exportEntry((GString*)user_data, FLAG_RESOURCE_PROPERTIES, (gchar *)key,
                               "%d %d", res->priv->rendered, res->priv->sensitiveToPlanes);
  DBG_fprintf(stderr, "OK.\n");
}
static void isosurfaces_export_resources(GString *data,
                                         VisuData *dataObj _U_)
{
  if (isosurfaces_resources != NULL && g_hash_table_size(isosurfaces_resources) > 0)
    {
      visu_config_file_exportComment(data, DESC_RESOURCE_COLOR);
      visu_config_file_exportComment(data, DESC_RESOURCE_PROPERTIES);
      g_hash_table_foreach(isosurfaces_resources, isosurfaces_export_one_surf_resources, data);
      visu_config_file_exportComment(data, "");
    }
  DBG_fprintf(stderr, "Isosurfaces Resources: exporting OK.\n");
}
