/* gtkrichtext.c
 *
 * Copyright (C) 2006 Imendio AB
 * Contact: Michael Natterer <mitch@imendio.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"

#include <string.h>

#include "gtkrichtext.h"
#include "gtktextbufferserialize.h"
#include "gtkalias.h"
#include "gtkintl.h"


typedef struct
{
  gchar          *mime_type;
  gchar          *tag_set_name;
  GdkAtom         atom;
  gpointer        function;
  gpointer        user_data;
  GDestroyNotify  user_data_destroy;
} GtkRichTextFormat;


static GList *serialize_formats   = NULL;
static GList *deserialize_formats = NULL;


static GList   * register_format   (GList          *formats,
                                    const gchar    *mime_type,
                                    const gchar    *tag_set_name,
                                    gpointer        function,
                                    gpointer        user_data,
                                    GDestroyNotify  user_data_destroy,
                                    GdkAtom        *atom);
static GList   * unregister_format (GList          *formats,
                                    GdkAtom         atom);
static GdkAtom * get_formats       (GList          *formats,
                                    const gchar    *tag_set_name,
                                    gint           *n_formats);


/*  public functions  */

GdkAtom
gtk_rich_text_register_serialize_format (const gchar              *mime_type,
                                         const gchar              *tag_set_name,
                                         GtkRichTextSerializeFunc  function,
                                         gpointer                  user_data,
                                         GDestroyNotify            user_data_destroy)
{
  GdkAtom atom;

  if (! function)
    function = _gtk_text_buffer_serialize_rich_text;

  serialize_formats = register_format (serialize_formats,
                                       mime_type, tag_set_name,
                                       (gpointer) function,
                                       user_data, user_data_destroy,
                                       &atom);

  return atom;
}

GdkAtom
gtk_rich_text_register_deserialize_format (const gchar                *mime_type,
                                           const gchar                *tag_set_name,
                                           GtkRichTextDeserializeFunc  function,
                                           gpointer                    user_data,
                                           GDestroyNotify              user_data_destroy)
{
  GdkAtom atom;

  if (! function)
    function = _gtk_text_buffer_deserialize_rich_text;

  deserialize_formats = register_format (deserialize_formats,
                                         mime_type, tag_set_name,
                                         (gpointer) function,
                                         user_data, user_data_destroy,
                                         &atom);

  return atom;
}

void
gtk_rich_text_unregister_serialize_format (GdkAtom format)
{
  g_return_if_fail (format != GDK_NONE);

  serialize_formats = unregister_format (serialize_formats, format);
}

void
gtk_rich_text_unregister_deserialize_format (GdkAtom format)
{
  g_return_if_fail (format != GDK_NONE);

  deserialize_formats = unregister_format (deserialize_formats, format);
}

GdkAtom *
gtk_rich_text_get_serialize_formats (const gchar *tag_set_name,
                                     gint        *n_formats)
{
  g_return_val_if_fail (n_formats != NULL, NULL);

  return get_formats (serialize_formats, tag_set_name, n_formats);
}

GdkAtom *
gtk_rich_text_get_deserialize_formats (const gchar *tag_set_name,
                                       gint        *n_formats)
{
  g_return_val_if_fail (n_formats != NULL, NULL);

  return get_formats (deserialize_formats, tag_set_name, n_formats);
}

guint8 *
gtk_rich_text_serialize (GtkTextBuffer     *buffer,
                         GdkAtom            format,
                         const GtkTextIter *start,
                         const GtkTextIter *end,
                         gsize             *length)
{
  GList *list;

  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
  g_return_val_if_fail (format != GDK_NONE, NULL);
  g_return_val_if_fail (start != NULL, NULL);
  g_return_val_if_fail (end != NULL, NULL);
  g_return_val_if_fail (length != NULL, NULL);

  *length = 0;

  for (list = serialize_formats; list; list = g_list_next (list))
    {
      GtkRichTextFormat *fmt = list->data;

      if (fmt->atom == format)
        {
          GtkRichTextSerializeFunc function = fmt->function;

          return function (buffer, start, end, length, fmt->user_data);
        }
    }

  return NULL;
}

gboolean
gtk_rich_text_deserialize (GtkTextBuffer  *buffer,
                           GdkAtom         format,
                           GtkTextIter    *iter,
                           const guint8   *data,
                           gsize           length,
                           GError        **error)
{
  GList    *list;
  gboolean  retval = FALSE;

  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
  g_return_val_if_fail (format != GDK_NONE, FALSE);
  g_return_val_if_fail (iter != NULL, FALSE);
  g_return_val_if_fail (data != NULL, FALSE);
  g_return_val_if_fail (length > 0, FALSE);
  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);

  for (list = deserialize_formats; list; list = g_list_next (list))
    {
      GtkRichTextFormat *fmt = list->data;

      if (fmt->atom == format)
        {
          GtkRichTextDeserializeFunc function = fmt->function;

          retval = function (buffer, iter, data, length,
                             fmt->tag_set_name == NULL,
                             fmt->user_data,
                             error);

          if (!retval && error != NULL && *error == NULL)
            g_set_error (error, 0, 0,
                         _("Unknown error when trying to deserialize %s"),
                         gdk_atom_name (format));

          break;
        }
    }

  if (!retval && error != NULL && *error == NULL)
    g_set_error (error, 0, 0,
                 _("No seserialize function found for format %s"),
                 gdk_atom_name (format));

  return retval;
}


/*  private functions  */

static GList *
register_format (GList          *formats,
                 const gchar    *mime_type,
                 const gchar    *tag_set_name,
                 gpointer        function,
                 gpointer        user_data,
                 GDestroyNotify  user_data_destroy,
                 GdkAtom        *atom)
{
  GtkRichTextFormat *format;

  if (! mime_type)
    mime_type = "application/x-gtk-text-buffer-rich-text";

  if (tag_set_name)
    {
      gchar *tmp = g_strdup_printf ("%s;%s", mime_type, tag_set_name);

      *atom = gdk_atom_intern (tmp, FALSE);
      g_free (tmp);
    }
  else
    {
      *atom = gdk_atom_intern (mime_type, FALSE);
    }

  formats = unregister_format (formats, *atom);

  format = g_new0 (GtkRichTextFormat, 1);

  format->mime_type         = g_strdup (mime_type);
  format->tag_set_name      = g_strdup (tag_set_name);
  format->atom              = *atom;
  format->function          = function;
  format->user_data         = user_data;
  format->user_data_destroy = user_data_destroy;

  return g_list_append (formats, format);
}

static GList *
unregister_format (GList   *formats,
                   GdkAtom  atom)
{
  GList *list;

  for (list = formats; list; list = g_list_next (list))
    {
      GtkRichTextFormat *format = list->data;

      if (format->atom == atom)
        {
          if (format->user_data_destroy)
            format->user_data_destroy (format->user_data);

          g_free (format->mime_type);
          g_free (format->tag_set_name);
          g_free (format);

          return g_list_delete_link (formats, list);
        }
    }

  return formats;
}

static GdkAtom *
get_formats (GList       *formats,
             const gchar *tag_set_name,
             gint        *n_formats)
{
  GdkAtom *array;
  GList   *tmp_formats = NULL;
  GList   *list;
  gint     i;

  for (list = formats; list; list = g_list_next (list))
    {
      GtkRichTextFormat *format = list->data;

      if ((tag_set_name == NULL && format->tag_set_name == NULL) ||
          (tag_set_name != NULL && format->tag_set_name != NULL &&
           !strcmp (tag_set_name, format->tag_set_name)))
        {
          tmp_formats = g_list_prepend (tmp_formats, format);
        }
    }

  tmp_formats = g_list_reverse (tmp_formats);

  *n_formats = g_list_length (tmp_formats);
  array = g_new0 (GdkAtom, *n_formats);

  for (list = tmp_formats, i = 0; list; list = g_list_next (list), i++)
    {
      GtkRichTextFormat *format = list->data;

      array[i] = format->atom;
    }

  g_list_free (tmp_formats);

  return array;
}

#define __GTK_RICH_TEXT_C__
#include "gtkaliasdef.c"
