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

#include <string.h>
#include <stdlib.h>

#include "interface.h"
#include "support.h"

#include "visu_tools.h"
#include "visu_basic.h"
#include "visu_plugins.h"

/**
 * SECTION: gtk_about
 * @short_description: The about dialog with a readme, the copyright,
 * the author list, the plug-in list and the version notes.
 *
 * <para>The about dialog window is activated when clicking on the V_Sim
 * icon on bottom right part of the command panel. It contains a
 * summary of V_Sim purpose and capabilities, the list of authors, the
 * list of loaded plug-ins, the list of changes since the previous
 * version, and the licence file. It displays also the version of
 * V_Sim and provide a link to the web site.</para>
 */

#define VISU_UI_ABOUT_LICENCE_FILE   _("licence.en.txt")
/* Translate if there is a authors file in another language. */
#define VISU_UI_ABOUT_AUTHORS_FILE   _("authors")
/* Translate if there is a readme file in another language. */
#define VISU_UI_ABOUT_README_FILE    _("readme")
/* Translate if there is a readme file in another language. */
#define VISU_UI_ABOUT_CHANGELOG_FILE _("ChangeLog.en")

enum
  {
    GTK_PLUGINS_COLUMN_ICON,
    GTK_PLUGINS_COLUMN_NAME,
    GTK_PLUGINS_COLUMN_DESCRIPTION,
    GTK_PLUGINS_COLUMN_AUTHORS,
    N_GTK_PLUGINS_COLUMNS
  };
static void buildPluginsTab();
/* static gboolean onUrlClicked(GtkTextTag *tag, GObject *object, */
/* 			     GdkEvent *event, GtkTextIter *iter, */
/* 			     gpointer user_data); */
/* static gboolean onMouseMotion(GtkWidget *wd, GdkEventMotion *event, */
/* 			      gpointer user_data); */
static void setChangelog(const gchar* buffer, gssize size, GtkTextBuffer *text);


/**
 * visu_ui_about_initBuild: (skip)
 * @main: the command panel the about dialog is associated to.
 *
 * Create the about window.
 */
void visu_ui_about_initBuild(VisuUiMain *main)
{
  GtkWidget *wd, *note;
  gchar *buffer, *utf8Buffer, *start, *end;
  GtkTextBuffer *text;
  GtkTextIter startIter, endIter;
  GtkTextTag *monoTag, *boldTag, *linkTag;
  gsize taille;
  GError *err;
  gchar *chemin;

  DBG_fprintf(stderr, "Gtk About : creating the dialog window.\n");
  main->aboutDialog = create_infoDialog();
  gtk_widget_set_name(main->aboutDialog, "message");

  wd = lookup_widget(main->aboutDialog, "labelInfoVersion");
  gtk_label_set_text(GTK_LABEL(wd), VISU_VERSION);
  wd = lookup_widget(main->aboutDialog, "labelInfoReleaseDate");
  gtk_label_set_text(GTK_LABEL(wd), V_SIM_RELEASE_DATE);
  wd = lookup_widget(main->aboutDialog, "labelInfoWebSite");
/* #if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 9 */
/*   gtk_widget_destroy(wd); */
/*   wd = lookup_widget(main->aboutDialog, "table4"); */
/*   gtk_table_attach(GTK_TABLE(wd), gtk_link_button_new(VISU_WEB_SITE), */
/* 		   1, 2, 2, 3, GTK_SHRINK, GTK_SHRINK, 5, 0); */
/*   gtk_widget_show_all(wd); */
/* #else */
  gtk_label_set_markup(GTK_LABEL(wd), "<span font_desc=\"monospace\"><u>"
		       VISU_WEB_SITE"</u></span>");
/* #endif */

  note = lookup_widget(main->aboutDialog, "notebookAbout");
  gtk_widget_set_name(note, "message_notebook");

  /* Create the interior of the tabs. */
  chemin = g_build_filename(V_SIM_LEGAL_DIR, VISU_UI_ABOUT_LICENCE_FILE, NULL);
  buffer = (gchar*)0;
  err = (GError*)0;
  if (!g_file_get_contents(chemin, &buffer, &taille, &err))
    g_warning("Can't find the licence file, normally"
	      " it should be in '%s'.\n", chemin);
  else
    {
      utf8Buffer = g_convert(buffer, taille, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
      g_free(buffer);

      wd = lookup_widget(main->aboutDialog, "textviewLicence");
      text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wd));
      gtk_text_buffer_get_start_iter(text, &startIter);
      monoTag = gtk_text_buffer_create_tag(text, "typewriter", "family", "monospace", NULL);
      gtk_text_buffer_insert_with_tags(text, &startIter, utf8Buffer,
				       -1, monoTag, NULL);
	  
      g_free(utf8Buffer);
    }
  g_free(chemin);

  chemin = g_build_filename(V_SIM_LEGAL_DIR, VISU_UI_ABOUT_README_FILE, NULL);
  buffer = (gchar*)0;
  err = (GError*)0;
  if (!g_file_get_contents(chemin, &buffer, &taille, &err))
    g_warning("Can't find the readme file, normally it"
	      " should be in '%s'.\n", chemin);
  else
    {
      utf8Buffer = g_convert(buffer, taille, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
      g_free(buffer);

      wd = lookup_widget(main->aboutDialog, "textviewReadme");
      gtk_widget_add_events(wd, GDK_POINTER_MOTION_MASK);
      text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wd));
      gtk_text_buffer_set_text (text, utf8Buffer, -1);

      start = strstr(utf8Buffer, "http://");
      end = g_utf8_strchr(start, -1, ' ');
      if (*g_utf8_prev_char(end) == '.')
	end = g_utf8_prev_char(end);
      linkTag = gtk_text_buffer_create_tag(text, "link",
					   "underline", PANGO_UNDERLINE_SINGLE, NULL);
/* 					   "foreground", "blue", NULL); */
/*       g_signal_connect(G_OBJECT(linkTag), "event", */
/* 		       G_CALLBACK(onUrlClicked), NULL); */
/*       g_signal_connect(G_OBJECT(wd), "motion-notify-event", */
/* 		       G_CALLBACK(onMouseMotion), (gpointer)linkTag); */
      gtk_text_buffer_get_iter_at_offset
	(text, &startIter,(gint)g_utf8_pointer_to_offset(utf8Buffer, start));
      gtk_text_buffer_get_iter_at_offset
	(text, &endIter, (gint)g_utf8_pointer_to_offset(utf8Buffer, end));
      gtk_text_buffer_apply_tag(text, linkTag, &startIter, &endIter);
	  
      g_free(utf8Buffer);
    }
  g_free(chemin);

  chemin = g_build_filename(V_SIM_LEGAL_DIR, VISU_UI_ABOUT_CHANGELOG_FILE, NULL);
  buffer = (gchar*)0;
  err = (GError*)0;
  if (!g_file_get_contents(chemin, &buffer, &taille, &err))
    g_warning("Can't find the changelog file, normally"
	      " it should be in '%s'.\n", chemin);
  else
    {
      wd = lookup_widget(main->aboutDialog, "textviewChangelog");
      gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(wd), GTK_WRAP_WORD);
      gtk_text_view_set_justification (GTK_TEXT_VIEW(wd), GTK_JUSTIFY_LEFT);
      text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wd));
      
      /* Parse here the XML to get only the relevant entries. */
      setChangelog(buffer, taille, text);

      g_free(buffer);
    }  

  chemin = g_build_filename(V_SIM_LEGAL_DIR, VISU_UI_ABOUT_AUTHORS_FILE, NULL);
  buffer = (gchar*)0;
  err = (GError*)0;
  if (!g_file_get_contents(chemin, &buffer, &taille, &err))
    g_warning("Can't find the authors file, normally"
	      " it should be in '%s'.\n", chemin);
  else
    {
      utf8Buffer = g_convert(buffer, taille, "UTF-8", "ISO-8859-1", NULL, NULL, NULL);
      g_free(buffer);

      wd = lookup_widget(main->aboutDialog, "textviewAuthors");
      text = gtk_text_view_get_buffer(GTK_TEXT_VIEW(wd));
      gtk_text_buffer_get_start_iter(text, &startIter);
      monoTag = gtk_text_buffer_create_tag(text, "typewriter",
					   "family", "monospace", NULL);
      gtk_text_buffer_insert_with_tags(text, &startIter, utf8Buffer,
				       -1, monoTag, NULL);
      boldTag = gtk_text_buffer_create_tag(text, "bold",
					   "weight", PANGO_WEIGHT_BOLD, NULL);
      end = utf8Buffer;
      do
	{
	  start = g_utf8_strchr(end, -1, '*');
	  if (start)
	    {
	      end = g_utf8_strchr(g_utf8_next_char(start), -1, '*');
	      if (end)
		{
		  gtk_text_buffer_get_iter_at_offset
		    (text, &startIter,(gint)g_utf8_pointer_to_offset(utf8Buffer, start));
		  gtk_text_buffer_get_iter_at_offset
		    (text, &endIter, (gint)g_utf8_pointer_to_offset(utf8Buffer, end));
		  gtk_text_buffer_apply_tag(text, boldTag, &startIter, &endIter);
		  end = g_utf8_next_char(end);
		}
	    }
	} while (start);
	  
      g_free(utf8Buffer);
    }
  g_free(chemin);

  buildPluginsTab(main);
}

/* static gboolean onMouseMotion(GtkWidget *wd, GdkEventMotion *event, */
/* 			      gpointer user_data) */
/* { */
/*   gint x, y; */
/*   GtkTextIter iter; */
/*   static GdkCursor *cur = NULL; */

/*   gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(wd), GTK_TEXT_WINDOW_WIDGET, */
/* 					event->x, event->y, &x, &y); */
/*   gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(wd), &iter, x, y); */

/*   if (gtk_text_iter_has_tag(&iter, GTK_TEXT_TAG(user_data))) */
/*     { */
/*       if (!cur) */
/* 	cur = gdk_cursor_new(GDK_HAND1); */
/*       gdk_window_set_cursor(event->window, cur); */
/*       g_object_set(G_OBJECT(user_data), "underline", PANGO_UNDERLINE_SINGLE, NULL); */
/*     } */
/*   else */
/*     { */
/*       gdk_window_set_cursor(event->window, NULL); */
/*       g_object_set(G_OBJECT(user_data), "underline", PANGO_UNDERLINE_NONE, NULL); */
/*     } */
/*   return FALSE; */
/* } */

/* static gboolean onUrlClicked(GtkTextTag *tag _U_, GObject *object _U_, */
/* 			     GdkEvent *event, GtkTextIter *iter _U_, */
/* 			     gpointer user_data _U_) */
/* { */
/*   if (event->type == GDK_BUTTON_RELEASE) */
/*     { */
/*       if (((GdkEventButton*)event)->button != 1) */
/* 	return FALSE; */
/*       DBG_fprintf(stderr, "Gtk About: clicked on URL.\n"); */
/*     } */
/*   return FALSE; */
/* } */

static void buildPluginsTab(VisuUiMain *main)
{
  GtkListStore *liststore;
  GtkTreeIter iter;
  GList *pnt;
  GtkWidget *tree, *wd;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  GdkPixbuf *pixbuf;
  const gchar *str;
  VisuPlugin *plug;

  liststore = gtk_list_store_new(N_GTK_PLUGINS_COLUMNS,
				 GDK_TYPE_PIXBUF,
				 G_TYPE_STRING,
				 G_TYPE_STRING,
				 G_TYPE_STRING);
  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore),
				       GTK_PLUGINS_COLUMN_NAME, GTK_SORT_ASCENDING);

  DBG_fprintf(stderr, "Gtk About: plugins:\n");
  for (pnt = visu_plugins_getListLoaded(); pnt; pnt = g_list_next(pnt))
    {
      plug = (VisuPlugin*)pnt->data;
      str = visu_plugin_getIconPath(plug);
      if (str)
        pixbuf = gdk_pixbuf_new_from_file_at_size(str, 32, 32, (GError**)0);
      else
	pixbuf = (GdkPixbuf*)0;
      gtk_list_store_append(liststore, &iter);
      gtk_list_store_set(liststore, &iter,
			 GTK_PLUGINS_COLUMN_ICON, pixbuf,
			 GTK_PLUGINS_COLUMN_NAME, visu_plugin_getName(plug),
			 GTK_PLUGINS_COLUMN_DESCRIPTION, visu_plugin_getDescription(plug),
			 GTK_PLUGINS_COLUMN_AUTHORS, visu_plugin_getAuthors(plug),
			 -1);
      DBG_fprintf(stderr, " - %s (%s) from %s\n",
                  visu_plugin_getName(plug), visu_plugin_getDescription(plug),
                  visu_plugin_getAuthors(plug));
    }

  /* Build the treeview. */
  tree= gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore));
  
  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)),
                              GTK_SELECTION_NONE);
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), TRUE);

  renderer = gtk_cell_renderer_pixbuf_new();
  column = gtk_tree_view_column_new_with_attributes("", renderer,
						    "pixbuf", GTK_PLUGINS_COLUMN_ICON,
						    NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);

  renderer = gtk_cell_renderer_text_new();
  g_object_set(renderer, "weight", 600, "weight-set", TRUE, NULL);
  column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
						    "text", GTK_PLUGINS_COLUMN_NAME,
						    NULL);
  gtk_tree_view_column_set_alignment(column, 0.5);
  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);

  renderer = gtk_cell_renderer_text_new();
  g_object_set(renderer, "xalign", 0., NULL);
  column = gtk_tree_view_column_new_with_attributes(_("Description"), renderer,
						    "markup", GTK_PLUGINS_COLUMN_DESCRIPTION,
						    NULL);
  gtk_tree_view_column_set_expand(column, TRUE);
  gtk_tree_view_column_set_alignment(column, 0.5);
  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes(_("Authors"), renderer,
						    "text", GTK_PLUGINS_COLUMN_AUTHORS,
						    NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);

  gtk_widget_show(tree);

  wd = lookup_widget(main->aboutDialog, "scrolledwindowPlugins");
  gtk_container_add(GTK_CONTAINER(wd), tree);
}


/****************************/
/* XML files for changelog. */
/****************************/

/*
  <ChangeLog logiciel="V_Sim" lang="en">
    <milestone version="3.5" date="(to come)">
      <intro>All changes leading to 3.5.x series.</intro>

      <entry titre="Development version 3.4.99-3">
      <li type="capability">Implement a visualisation for the
      difference</li>
    </milestone>
  </ChangeLog>
*/

/* Known elements. */
#define PICK_PARSER_ELEMENT_CHANGELOG  "ChangeLog"
#define PICK_PARSER_ELEMENT_MILESTONE  "milestone"
#define PICK_PARSER_ELEMENT_ENTRY      "entry"
#define PICK_PARSER_ELEMENT_LI         "li"
/* Known attributes. */
#define PICK_PARSER_ATTRIBUTES_VERSION "version"
#define PICK_PARSER_ATTRIBUTES_TITRE   "titre"
#define PICK_PARSER_ATTRIBUTES_TYPE    "type"

static gboolean startAbout, startLi;

/* This method is called for every element that is parsed.
   The user_data must be the text buffer to write into. */
static void aboutXML_element(GMarkupParseContext *context _U_,
			     const gchar         *element_name,
			     const gchar        **attribute_names,
			     const gchar        **attribute_values,
			     gpointer             user_data,
			     GError             **error _U_)
{
  GtkTextBuffer *text;
  GtkTextIter iter;
  gchar **version, **refVersion;
  int minor, refMinor, i;

  g_return_if_fail(user_data);
  text = GTK_TEXT_BUFFER(user_data);

  DBG_fprintf(stderr, "ChangeLog parser: found '%s' element.\n", element_name);
  if (!strcmp(element_name, PICK_PARSER_ELEMENT_MILESTONE))
    {
      startAbout = FALSE;
      /* Should have 1 mandatory attribute. */
      for (i = 0; attribute_names[i]; i++)
	{
	  if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_VERSION))
	    {
	      refVersion = g_strsplit_set(VISU_VERSION, ".-", 4);
	      version    = g_strsplit_set(attribute_values[i], ".", 3);
	      refMinor   = (atoi(refVersion[2]) == 99)?
		atoi(refVersion[1]) + 1:atoi(refVersion[1]);
	      minor      = (version[1])?atoi(version[1]):-1;
	      startAbout = (minor == refMinor);
	      g_strfreev(version);
	      g_strfreev(refVersion);
	    }
	}
    }
  else if (!strcmp(element_name, PICK_PARSER_ELEMENT_ENTRY))
    {
      if (startAbout)
	{
	  /* Should have 1 mandatory attribute. */
	  for (i = 0; attribute_names[i]; i++)
	    {
	      if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_TITRE))
		{
		  gtk_text_buffer_get_end_iter(text, &iter);
		  gtk_text_buffer_insert_with_tags_by_name(text, &iter,
							   attribute_values[i],
							   -1, "bold", NULL);
		  gtk_text_buffer_get_end_iter(text, &iter);
		  gtk_text_buffer_insert(text, &iter, _(":\n\n"), -1);
		}
	    }
	}
    }
  else if (!strcmp(element_name, PICK_PARSER_ELEMENT_LI))
    {
      if (startAbout)
	{
	  gtk_text_buffer_get_end_iter(text, &iter);
	  gtk_text_buffer_insert(text, &iter, "\342\200\242\t", -1);

	  /* May have the type attribute. */
	  for (i = 0; attribute_names[i]; i++)
	    {
	      if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_TYPE))
		{
		  gtk_text_buffer_get_end_iter(text, &iter);
		  gtk_text_buffer_insert_with_tags_by_name(text, &iter,
							   attribute_values[i],
							   -1, "italic", NULL);
		  gtk_text_buffer_get_end_iter(text, &iter);
		  gtk_text_buffer_insert(text, &iter, _(": "), -1);
		}
	    }
	  startLi = TRUE;
	}
    }
}

/* Check when a element is closed that everything required has been set. */
static void aboutXML_end(GMarkupParseContext *context _U_,
			 const gchar         *element_name,
			 gpointer             user_data,
			 GError             **error _U_)
{
  GtkTextBuffer *text;
  GtkTextIter iter;

  g_return_if_fail(user_data);
  text = GTK_TEXT_BUFFER(user_data);

  DBG_fprintf(stderr, "ChangeLog parser: close '%s' element.\n", element_name);
  if (!strcmp(element_name, PICK_PARSER_ELEMENT_MILESTONE))
    startAbout = FALSE;
  else if (!strcmp(element_name, PICK_PARSER_ELEMENT_ENTRY) && startAbout)
    {
      gtk_text_buffer_get_end_iter(text, &iter);
      gtk_text_buffer_insert(text, &iter, "\n", -1);
    }
  else if (!strcmp(element_name, PICK_PARSER_ELEMENT_LI) && startAbout)
    {
      startLi = FALSE;
      gtk_text_buffer_get_end_iter(text, &iter);
      gtk_text_buffer_insert(text, &iter, "\n", -1);
    }
}

static void aboutXML_text(GMarkupParseContext *context _U_,
                          const gchar         *buf,
                          gsize                text_len,  
                          gpointer             user_data,
                          GError             **error _U_)
{
  GtkTextBuffer *text;
  GtkTextIter iter;

  g_return_if_fail(user_data);
  text = GTK_TEXT_BUFFER(user_data);

  if (startAbout && startLi)
    {
      gtk_text_buffer_get_end_iter(text, &iter);
      gtk_text_buffer_insert(text, &iter, buf, text_len);
/*       gtk_text_buffer_get_end_iter(text, &iter); */
/*       gtk_text_buffer_insert(text, &iter, "\n", -1); */
    }
}

static void setChangelog(const gchar* buffer, gssize size, GtkTextBuffer *text)
{
  GMarkupParseContext* xmlContext;
  GMarkupParser parser;
  gboolean status;
  GError *error;


  /* Create context. */
  parser.start_element = aboutXML_element;
  parser.end_element   = aboutXML_end;
  parser.text          = aboutXML_text;
  parser.passthrough   = NULL;
  parser.error         = NULL;
  xmlContext = g_markup_parse_context_new(&parser, 0, text, NULL);

  gtk_text_buffer_create_tag(text, "bold",
                             "weight", PANGO_WEIGHT_BOLD, NULL);
  gtk_text_buffer_create_tag(text, "italic",
                             "style", PANGO_STYLE_ITALIC, NULL);

  /* Parse data. */
  startAbout = FALSE;
  startLi    = FALSE;
  error = (GError*)0;
  status = g_markup_parse_context_parse(xmlContext, buffer, size, &error);

  /* Free buffers. */
  g_markup_parse_context_free(xmlContext);

  DBG_fprintf(stderr, "ChangeLog parser: done, status %d (%s).\n",
              status, (error)?error->message:"no error");
  if (!status && error)
    g_warning("%s", error->message);

  if (error)
    g_error_free(error);
}

