/**
 * getxml-merge:
 * @title: getxml-merge
 * @subtitle: getxml-merge
 * @project: getxml
 * @lang: fr,en
 * @authors: Philippe Roy <ph_roy@toutdoux.org>
 * @copyright: Copyright (c) 2001 Philippe Roy
 * @license: GNU GPL
 *
 * fr: Fusion
 *
 * en: Merge
 **/

/*
 * Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier
 * sous les termes de la licence publique générale GNU telle qu'elle est publiée par
 * la Free Software Foundation ; soit la version 2 de la licence, ou
 * (comme vous voulez) toute version ultérieure.
 *
 * Ce programme est distribué dans l'espoir qu'il sera utile,
 * mais SANS AUCUNE GARANTIE ; même sans la garantie de
 * COMMERCIALITÉ ou d'ADÉQUATION A UN BUT PARTICULIER. Voir la
 * licence publique générale GNU pour plus de détails.
 *
 * Vous devriez avoir reçu une copie de la licence publique générale GNU
 * avec ce programme ; si ce n'est pas le cas, écrivez à la Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <string.h>
#include <fcntl.h>
#include <parser.h>
#include <tree.h>
/*  #include <libxml/parser.h> */
/*  #include <libxml/tree.h> */

#include "getxml-merge.h"

/*****************************************************************************/
/*** Standardisation du vocabulaire - Vocabulary standardization */
/*****************************************************************************/

#define XmlDoc xmlDoc
#define XmlNs xmlNs
#define XmlNode xmlNode
#define XmlAttribute xmlAttribute
#define XmlAttr xmlAttr
#define XmlChar xmlChar

/*****************************************************************************/
/*** Variables globales - Global variables */
/*****************************************************************************/

#define BUFFER_SIZE 256
#define MAX_RING 100
gchar *PACKAGE_COMMAND;
TdPopt popts[6];

/*****************************************************************************/
/*** Communs - Commons
/*****************************************************************************/

/**
 * string_file:
 * @file: file
 *
 * fr: Retourne le contenu du fichier
 *
 * en: Returns the file content
 *
 * Return value: string
 **/

GString* string_file (gchar *file)
{
  gint i;
  gint fd;
  GString *ret;
  GIOChannel *iochannel;
  char *buff;
  guint readden;
  buff = g_malloc0 (BUFFER_SIZE);
  ret = g_string_new ("");

  /* Go ! */
  fd = open (file, O_RDONLY);
  if (fd < 0)
    g_error ("getxml-extract: loading file '%s' unreadable", file);
  iochannel = g_io_channel_unix_new (fd);
  g_io_channel_read (iochannel, buff, BUFFER_SIZE, &readden);
  if (readden == 0)
    return NULL;
  i=0;
  while (readden != 0)
    {
      g_string_append (ret, g_strdup (buff));
      g_free (buff);
      buff = g_malloc0 (BUFFER_SIZE);
      g_io_channel_read (iochannel, buff, BUFFER_SIZE, &readden);
    }
  close (fd);
  g_free (buff);
  return ret;
}


/*****************************************************************************/
/*** Arguments de la ligne de commande - Command line arguments */
/*****************************************************************************/

/**
 * popt_def:
 * @key: char key
 * @long key: string key
 * @argument: argument
 * @description: description
 *
 * fr: Créé une option de ligne de commande
 *
 * en: Creates an option of command line
 *
 * Return value: popt structure
 **/

TdPopt popt_def (gchar *key, gchar *longkey, gchar *argument, gchar *description)
{
  TdPopt ret = NULL;
  ret = g_malloc0 (sizeof (TdPopt_tmp));
  ret->key = g_strdup (key);
  ret->longkey = g_strdup (longkey);
  ret->argument = g_strdup (argument);
  ret->description = description;
  ret->flag = FALSE;
  ret->value = NULL;
  ret->nbpredef = 0;
  return ret;
}

/**
 * popts_help:
 * @nbpopts: counter of options of commande line
 * @popts[]: arraw of options of commande line options
 *
 * fr: Affiche l'usage
 *
 * en: Displays the usage
 **/

void popts_help (gint nbpopts, TdPopt popts[])
{
  gint i, j, k, key, longkey, argument;
  gint length = 0;

  /*** Alignement - Aligment */
  g_print ("%s [option ...]\n", PACKAGE_COMMAND);
  for (i=0; i<nbpopts; i++)
    {
      key = 0;
      longkey = 0;
      argument = 0;
      if (popts[i]->key)
	key = 3;
      if (popts[i]->longkey)
	longkey = strlen (popts[i]->longkey);
      if (popts[i]->argument)
	argument = strlen (popts[i]->argument);
      if (length < (9+key+longkey+argument))
	length = 9+key+longkey+argument;
    }


  /*** Texte - Text */
  for (i=0; i<nbpopts; i++)
    {
      g_print ("  ");
      key = 0;
      longkey = 0;
      argument = 0;
      if (popts[i]->key)
	{
	  g_print ("-%s, ", popts[i]->key);
	  key = 4;
	}
      if (popts[i]->longkey)
	{
	  g_print ("--%s ", popts[i]->longkey);
	  longkey = strlen (popts[i]->longkey);
	}
      if (popts[i]->argument)
	{
	  g_print ("%s", popts[i]->argument);
	  argument = strlen (popts[i]->argument);
	}
      for (j=7+key+longkey+argument; j<length; j++)
	g_print (" ");
      g_print ("%s\n", popts[i]->description);
      for (j=0; j<popts[i]->nbpredef; j++)
	{
	  for (k=0; k<length; k++)
	    g_print (" ");
	  g_print ("%s\n", popts[i]->predef[j]);
	}
    }
  exit (0);
}

/**
 * popts_parse:
 * @argc: counter of options used of command line
 * @argv[]: arraw of options used of command line
 * @nbpopts: counter of options of command line
 * @popts[]: arraw of options of command line
 *
 * fr: Analyse de la ligne de commande
 *
 * en: Parses the command line
 **/

void popts_parse (gint argc, char *argv[], gint nbpopts, TdPopt popts[])
{
  gint i, j;
  GList *option = NULL;
  GList *argument = NULL;
  gchar *txt_tmp = NULL;
  gchar *txt_tmp1 = NULL;
  gchar *txt_tmp2 = NULL;
  gboolean bool_tmp;

  /*** Découpage - Cutting */
  for (i=1; i<argc; i++)
    if (argv[i][0] == '-')
      {
	if (argv[i][1] == '-')
	  option = g_list_append (option, g_strdup (argv[i]+2));
	else
	  option = g_list_append (option, g_strdup (argv[i]+1));
	if (i!=1)
	  {
	    argument = g_list_append (argument, g_strdup (txt_tmp));
	    txt_tmp = "(null)";
	  }
      }
    else
      txt_tmp = g_strdup (argv[i]);
  if (i!=1)
    argument = g_list_append (argument, g_strdup (txt_tmp));

  /*** Affectation - Allocation */
  for (i=0; i<g_list_length (option); i++)
    {

      /*** Aide - Help */
      if ((!strcmp ("h", g_list_nth_data (option, i))) || (!strcmp ("help", g_list_nth_data (option, i))))
	{
	  popts_help (nbpopts, popts);
	  break;
	}
      if (!strcmp ("about", g_list_nth_data (option, i)))
	{
	  popts_about();
	  break;
	}
      if ((!strcmp ("v", g_list_nth_data (option, i))) || (!strcmp ("version", g_list_nth_data (option, i))))
	{
	  popts_version();
	  break;
	}

      /*** Application */
      bool_tmp = FALSE;
      for (j=0; j<nbpopts; j++)
	if (((popts[j]->key) && (!strcmp (popts[j]->key, g_list_nth_data (option, i)))) || 
	    ((popts[j]->longkey) && (!strcmp (popts[j]->longkey, g_list_nth_data (option, i)))))
	  {
	    popts[j]->flag = TRUE;
	    bool_tmp = TRUE;
	    if (g_list_nth_data (argument, i))
	      popts[j]->value = g_strdup (g_list_nth_data (argument, i));
	    else
	      if (popts[j]->argument)
		g_error ("getxml-extract: option '%s' needs argument '%s'", (gchar*) g_list_nth_data (option, i), popts[j]->argument);
	  }
      if (!bool_tmp)
	g_error ("getxml-extract: option '%s' unknow", (gchar*) g_list_nth_data (option, i));
    }
}

/**
 * popts_version:
 *
 * fr: Affiche la version du programme
 *
 * en: Display the version of the program
 **/

void popts_version (void)
{
  g_print ("%s %s\n", PACKAGE_COMMAND, GETXML_VERSION);
  exit (0);
}

/*****************************************************************************/
/*** Dictionnaire - Dictionnary */
/*****************************************************************************/

/**
 * dictionnary:
 * @file: po file location
 * @statistics_flag: display statistics
 *
 * fr: Créé le dictionnaire
 *
 * en: Creates the dictionnary
 *
 * Return value: hash table
 **/

GHashTable *dictionnary (gchar *file, gboolean statistics_flag)
{
  GHashTable *ret;
  gint i, j;
  GString *string;
  gchar **string2;
  gchar **string3;
  gchar **string4;
  gchar **string5;
  gchar **string6;
  gchar *msgid;
  gchar *msgstr;
  gchar **fuzzy_string;
  gchar **untranslated_string;
  gchar **obsoleted_string;
  gint fuzzy_count = 0;
  gint untranslated_count = 0;
  gint obsoleted_count = 0;
  gint word_count = 0;
  gchar *txt_tmp;

  /*** Découpage - Cutting */
  string = string_file (file);
  ret = g_hash_table_new (g_str_hash, g_str_equal);
  string2 = g_strsplit (string->str, "\nmsgid \"", string->len);
  i=0;
  while (string2[i])
    {      
      if (strstr (string2[i], "\nmsgstr \""))
	{
	  msgid = NULL;
	  msgstr = NULL;
	  string3 = g_strsplit (string2[i], "\nmsgstr \"", 2);
	  string4 = g_strsplit (string3[1], "\"\n\n#", 2);

	  /*** msgid */
	  if (*string3[0] == '"')
	    {
	      string5 = g_strsplit (g_strndup (string3[0], strlen (string3[0])-1), "\"\n\"", MAX_RING);
	      j=0;
	      while (string5[j])
		{
		  if (msgid)
		    msgid = g_strdup_printf ("%s%s", msgid, string5[j]);
		  else
		    msgid = g_strdup (string5[j]);
		  j++;
		}
	    }
	  else
	    msgid = g_strndup (string3[0], strlen (string3[0])-1);

	  /*** msgstr */
	  if (*string4[0] == '"')
	    {
	      string5 = g_strsplit (g_strndup (string4[0], strlen (string4[0])), "\"\n\"", MAX_RING);
	      j=0;
	      while (string5[j])
		{
		  if (msgstr)
		    msgstr = g_strdup_printf ("%s%s", msgstr, string5[j]);
		  else
		    msgstr = g_strdup (string5[j]);
		  j++;
		}
	    }
	  else
	    msgstr = g_strdup (string4[0]);

	  /*** Dernier mot - Lastest word */
  	  if (strstr (msgstr+strlen (msgstr)-2, "\""))
	    msgstr = g_strndup (msgstr, strlen (msgstr)-2);

	  /*** Ajout - Add */
	  if (!strcmp (msgstr, "\""))
	      msgstr = "";
	  if (msgid && msgstr)
	    g_hash_table_insert (ret, msgid, msgstr);
	}
      i++;
    }

  /*** Compteurs - Counters */
  if (statistics_flag)
    {
      i--;
      untranslated_string = g_strsplit (string->str, "\nmsgstr \"\"\n\n#", string->len);
      untranslated_count=0;
      while (untranslated_string[untranslated_count])
	untranslated_count++;
      untranslated_count--;
      fuzzy_string = g_strsplit (string->str, "\n#, fuzzy\n", string->len);
      fuzzy_count=0;
      while (fuzzy_string[fuzzy_count])
	fuzzy_count++;
      fuzzy_count--;
      obsoleted_string = g_strsplit (string->str, "\"\n\n#~", string->len);
      obsoleted_count=0;
      while (obsoleted_string[obsoleted_count])
	obsoleted_count++;
      obsoleted_count--;
      g_print ("%d translated messages, %d fuzzy translations, %d untranslated messages, %d obsolted translations\n", i-fuzzy_count-untranslated_count, fuzzy_count, untranslated_count, obsoleted_count);
    }
  return ret;
}

/*****************************************************************************/
/*** Traduction - Translation */
/*****************************************************************************/

/**
 * translation:
 * @file_in: input file location
 * @file_out: output file location
 * @dico: dictionnary
 *
 * fr: Traduit le fichier
 *
 * en: Translates the file
 **/

void translation (gchar *file_in, gchar *file_out, GHashTable *dico)
{
  XmlDoc *doc;
  XmlNs *ns;
  XmlNode *cur;
  XmlNode *cur_parent = NULL;
  gboolean stop_doc;
  gboolean stop_node;
  gchar *carac;
  gchar *txt_tmp;
  XmlAttr *property;

  /*** En-tête - Head */
  xmlKeepBlanksDefault (0);
  doc = xmlParseFile (file_in);
  if (!doc)
    g_error ("getxml-extract: parsing XML file : doc == NULL");
  cur = xmlDocGetRootElement (doc);
  if (!cur)
    {
      xmlFreeDoc (doc);
      g_error ("getxml-extract: parsing XML file : cur == NULL");
    }

  /*** Valeurs - Values */
  stop_doc = FALSE;
  while (!stop_doc)
    {

      /*** Attributs - Attributes */
      property = (XmlAttr*) (cur->properties);
      if (property)
	{
	  stop_node = FALSE;
	  if ((!property->name) || (!strlen (g_strstrip ((gchar*) property->name))))
	    stop_node = TRUE;
	  while (!stop_node)
	    {
	      carac = (guchar*) property->name;
	      if (*carac == '_')
		{
		  txt_tmp = g_hash_table_lookup (dico, xmlGetProp (cur, property->name));
		  if (txt_tmp && (strlen (g_strstrip (g_strdup (txt_tmp)))))
		    xmlSetProp (cur, property->name, txt_tmp);
		  property->name = g_strndup (property->name+1, strlen (property->name)-1);
		}
	      if (!property->next)
		stop_node = TRUE;
	      else
		property = property->next;
	    }
	}

      /*** Suivant - Next */
      if (cur->xmlChildrenNode)
	{
	  cur = cur->xmlChildrenNode;
	  continue;
	}
      else
	{
	  if (cur->next)
	    {
	      cur = cur->next;
	      continue;
	    }
	  else 
	    {
	      do
		{
		  if (cur == xmlDocGetRootElement (doc))
		    stop_doc = TRUE;
		  else
		    {
		      cur_parent = cur->parent;
		      if (cur_parent->next == NULL)
			cur = cur_parent;
		      else
			cur = cur_parent->next;
		    }
		}
	      while ((cur == cur_parent) && (!stop_doc));
	    }
	}
    }

  /* FIXME: libxml2 : attente d'une version plus mature - libxml2 : wait a more mature version */ 
  /* if (base->encoding) */
  /* { */
  /* if (xmlSaveFileEnc (file, doc, base->encoding)<0) */
  /* { */
  /* td_app_message (g_strdup_printf (_("Saving geometry '%s'"), file), TD_MSG_FAILED); */
  /* xmlFreeDoc (doc); */
  /* return FALSE; */
  /* } */
  /* } */
  /* else */
  if (xmlSaveFile (file_out, doc)<0)
    g_error ("getxml-extract: saving XML file failed");
  xmlFreeDoc (doc);
}

/*****************************************************************************/
/*** Principale - Main */
/*****************************************************************************/

/**
 * popts_about:
 *
 * fr: Affiche le à propos du programme
 *
 * en: Display the about of the program
 **/

void popts_about (void)
{
  g_print ("%s %s : Traducteur XML - XML translator\n", PACKAGE_COMMAND, GETXML_VERSION);
  g_print ("Copyright (c) 2001 Philippe Roy\n");
  g_print ("Author : Philippe Roy <ph_roy@toutdoux.org>\n");
  g_print ("getxml-merge is a free software, covered by the GNU General Public License. (http://www.gnu.org)\n");
  g_print ("getxml-merge is a tool developped for the ToutDoux project. (http://www.toutdoux.org)\n");
  exit (0);
}

/**
 * popts_def:
 *
 * fr: Definition des options de la ligne de commande
 *
 * en: Definition of the options of command line
 **/

void popts_def (void)
{
  PACKAGE_COMMAND = "getxml-merge";
  popts[0] = popt_def (NULL, "about", NULL, "About");
  popts[1] = popt_def ("v", "version", NULL, "Version");
  popts[2] = popt_def ("i", "input", "<xml file>", "Specify the XML input file (obligatory)");
  popts[3] = popt_def ("p", "po", "<po file>", "Specify the po file (obligatory)");
  popts[4] = popt_def ("o", "output", "<xml file>", "Specify the XML output file (obligatory)");
  popts[5] = popt_def (NULL, "stat", NULL, "Enable statistics of po file (optional)");
};

/**
 * main:
 * @argc: counter of command line options used
 * @argv[]: arraw of command line options used
 *
 * fr: Procédure principale du programme
 *
 * en: Main procedure of the program
 *
 * Return value: -1 on error
 **/

int main (gint argc, char *argv[])
{
  FILE *file_in;
  FILE *file_out;
  void *ctx;
  GHashTable *dico;

  /*** Popts */
  popts_def();
  popts_parse (argc, argv, 6, popts);

  /*** Vérification des fichiers - Files checking */
  file_in = fopen (popts[2]->value, "r");
  if (file_in == NULL)
    g_error ("getxml-extract: reading XML input file : error in opening");
  fclose (file_in);
  file_out = fopen (popts[4]->value, "w");
  if (file_out == NULL)
    g_error ("getxml-extract: reading XML output file : error in opening");
  fclose (file_out);

  /*** Dictionnaire - Dictionnary */
  dico = dictionnary (popts[3]->value, popts[5]->flag);
  if (!dico)
    g_error ("getxml-extract: dictionnary incomplete");

  /*** Traduction - Translation */
  translation (popts[2]->value, popts[4]->value, dico);
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1