/*
 * Copyright (C) 2004, 2005 Jean-Yves Lefort
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of Jean-Yves Lefort nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include <string.h>
#include <glib/gi18n-lib.h>
#include "translate-pair.h"
#include "translate-pair-private.h"
#include "translate.h"

enum {
  PROP_0,
  PROP_FLAGS,
  PROP_FROM,
  PROP_TO
};

struct _TranslatePairPrivate
{
  TranslatePairFlags	flags;
  char			*from;
  char			*to;
};

static GObjectClass *parent_class = NULL;

static void translate_pair_register_type (GType *type);
static void translate_pair_flags_register_type (GType *type);
static void translate_pair_class_init (TranslatePairClass *class);
static void translate_pair_init (TranslatePair *pair);
static void translate_pair_finalize (GObject *object);
static void translate_pair_set_property (GObject *object,
					 unsigned int prop_id,
					 const GValue *value,
					 GParamSpec *pspec);
static void translate_pair_get_property (GObject *object,
					 unsigned int prop_id,
					 GValue *value,
					 GParamSpec *pspec);

static int translate_pairs_tag_compare_func (TranslatePair *a, TranslatePair *b);
static int translate_pairs_name_compare_func (TranslatePair *a, TranslatePair *b);

GType
translate_pair_get_type (void)
{
  static GType type;
  static GOnce once = G_ONCE_INIT;

  g_once(&once, (GThreadFunc) translate_pair_register_type, &type);

  return type;
}

static void
translate_pair_register_type (GType *type)
{
  static const GTypeInfo info = {
    sizeof(TranslatePairClass),
    NULL,
    NULL,
    (GClassInitFunc) translate_pair_class_init,
    NULL,
    NULL,
    sizeof(TranslatePair),
    0,
    (GInstanceInitFunc) translate_pair_init
  };
      
  *type = g_type_register_static(G_TYPE_OBJECT,
				 "TranslatePair",
				 &info,
				 0);
}

GType
translate_pair_flags_get_type (void)
{
  static GType type;
  static GOnce once = G_ONCE_INIT;

  g_once(&once, (GThreadFunc) translate_pair_flags_register_type, &type);

  return type;
}

static void
translate_pair_flags_register_type (GType *type)
{
  static const GFlagsValue values[] = {
    { TRANSLATE_PAIR_TEXT, "TRANSLATE_PAIR_TEXT", "text" },
    { TRANSLATE_PAIR_WEB_PAGE, "TRANSLATE_PAIR_WEB_PAGE", "web-page" },
    { 0, NULL, NULL }
  };

  *type = g_flags_register_static("TranslatePairFlags", values);
}

static void
translate_pair_class_init (TranslatePairClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS(class);

  g_type_class_add_private(class, sizeof(TranslatePairPrivate));
  parent_class = g_type_class_peek_parent(class);

  object_class->finalize = translate_pair_finalize;
  object_class->set_property = translate_pair_set_property;
  object_class->get_property = translate_pair_get_property;

  g_object_class_install_property(object_class,
				  PROP_FLAGS,
				  g_param_spec_flags("flags",
						     _("Flags"),
						     _("The pair flags"),
						     TRANSLATE_TYPE_PAIR_FLAGS,
						     0,
						     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property(object_class,
				  PROP_FROM,
				  g_param_spec_string("from",
						      _("From"),
						      _("The source language RFC 3066 tag"),
						      NULL,
						      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property(object_class,
				  PROP_TO,
				  g_param_spec_string("to",
						      _("To"),
						      _("The destination language RFC 3066 tag"),
						      NULL,
						      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}

static void
translate_pair_init (TranslatePair *pair)
{
  pair->priv = G_TYPE_INSTANCE_GET_PRIVATE(pair,
					   TRANSLATE_TYPE_PAIR,
					   TranslatePairPrivate);
}

static void
translate_pair_finalize (GObject *object)
{
  TranslatePair *pair = TRANSLATE_PAIR(object);

  g_free(pair->priv->from);
  g_free(pair->priv->to);
  
  parent_class->finalize(object);
}

static void
translate_pair_set_property (GObject *object,
			     unsigned int prop_id,
			     const GValue *value,
			     GParamSpec *pspec)
{
  TranslatePair *pair = TRANSLATE_PAIR(object);

  switch (prop_id)
    {
    case PROP_FLAGS:
      pair->priv->flags = g_value_get_flags(value);
      break;

    case PROP_FROM:
      pair->priv->from = g_value_dup_string(value);
      break;

    case PROP_TO:
      pair->priv->to = g_value_dup_string(value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
    }
}

static void
translate_pair_get_property (GObject *object,
			     unsigned int prop_id,
			     GValue *value,
			     GParamSpec *pspec)
{
  TranslatePair *pair = TRANSLATE_PAIR(object);

  switch (prop_id)
    {
    case PROP_FLAGS:
      g_value_set_flags(value, pair->priv->flags);
      break;

    case PROP_FROM:
      g_value_set_string(value, pair->priv->from);
      break;

    case PROP_TO:
      g_value_set_string(value, pair->priv->to);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
    }
}

/**
 * translate_pair_get_flags:
 * @pair: a pair.
 *
 * Gets the flags of @pair.
 *
 * Return value: the pair flags.
 **/
TranslatePairFlags
translate_pair_get_flags (TranslatePair *pair)
{
  g_return_val_if_fail(TRANSLATE_IS_PAIR(pair), 0);

  return pair->priv->flags;
}

void
translate_pair_set_flags (TranslatePair *pair, TranslatePairFlags flags)
{
  g_return_if_fail(TRANSLATE_IS_PAIR(pair));

  pair->priv->flags = flags;
}

/**
 * translate_pair_get_from:
 * @pair: a pair.
 *
 * Gets the source language of @pair.
 *
 * Return value: the RFC 3066 tag of the source language of @pair.
 **/
const char *
translate_pair_get_from (TranslatePair *pair)
{
  g_return_val_if_fail(TRANSLATE_IS_PAIR(pair), NULL);

  return pair->priv->from;
}

/**
 * translate_pair_get_to:
 * @pair: a pair.
 *
 * Gets the destination language of @pair.
 *
 * Return value: the RFC 3066 tag of the destination language of
 * @pair.
 **/
const char *
translate_pair_get_to (TranslatePair *pair)
{
  g_return_val_if_fail(TRANSLATE_IS_PAIR(pair), NULL);

  return pair->priv->to;
}

/**
 * translate_pair_new:
 * @flags: the pair flags.
 * @from: the source language RFC 3066 tag.
 * @to: the destination language RFC 3066 tag.
 *
 * Creates a new language pair.
 *
 * Return value: a new language pair.
 **/
TranslatePair *
translate_pair_new (TranslatePairFlags flags,
		    const char *from,
		    const char *to)
{
  g_return_val_if_fail(from != NULL, NULL);
  g_return_val_if_fail(to != NULL, NULL);

  return g_object_new(TRANSLATE_TYPE_PAIR,
		      "flags", flags,
		      "from", from,
		      "to", to,
		      NULL);
}

GSList *
translate_pairs_merge (GSList *pairs1, const GSList *pairs2)
{
  const GSList *l;

  for (l = pairs2; l != NULL; l = l->next)
    {
      TranslatePair *pair = l->data;
      TranslatePair *existing_pair;

      existing_pair = translate_pairs_find(pairs1, pair->priv->from, pair->priv->to);
      if (existing_pair)
	existing_pair->priv->flags |= pair->priv->flags;
      else
	pairs1 = g_slist_append(pairs1, g_object_ref(pair));
    }

  return pairs1;
}

/**
 * translate_pairs_sort_by_tag:
 * @pairs: a list of #TranslatePair objects.
 *
 * Sorts a pair list by source and destination RFC 3066 tag. Tag
 * comparisons are performed using translate_ascii_strcasecoll().
 *
 * Return value: @pairs sorted.
 **/
GSList *
translate_pairs_sort_by_tag (GSList *pairs)
{
  return g_slist_sort(pairs, (GCompareFunc) translate_pairs_tag_compare_func);
}

/**
 * translate_pairs_sort_by_name:
 * @pairs: a list of #TranslatePair objects.
 *
 * Sorts a pair list by source and destination human-readable language
 * name. Names are obtained using translate_get_language_name(), and
 * are compared using translate_utf8_strcasecoll().
 *
 * Return value: @pairs sorted.
 **/
GSList *
translate_pairs_sort_by_name (GSList *pairs)
{
  return g_slist_sort(pairs, (GCompareFunc) translate_pairs_name_compare_func);
}

static int
translate_pairs_tag_compare_func (TranslatePair *a, TranslatePair *b)
{
  int coll;

  coll = translate_ascii_strcasecoll(a->priv->from, b->priv->from);
  if (! coll)
    coll = translate_ascii_strcasecoll(a->priv->to, b->priv->to);

  return coll;
}

static int
translate_pairs_name_compare_func (TranslatePair *a, TranslatePair *b)
{
  int coll;

  coll = translate_utf8_strcasecoll(translate_get_language_name(a->priv->from),
				    translate_get_language_name(b->priv->from));
  if (! coll)
    coll = translate_utf8_strcasecoll(translate_get_language_name(a->priv->to),
				      translate_get_language_name(b->priv->to));

  return coll;
}

/**
 * translate_pairs_find:
 * @pairs: a list of #TranslatePair objects.
 * @from: a RFC 3066 tag, or %NULL.
 * @to: a RFC 3066 tag, or %NULL.
 *
 * Finds the first pair in @pairs whose source and/or destination
 * language match @from and/or @to.
 *
 * Return value: a pair, or %NULL if not found. The returned pair
 * belongs to @pairs, its reference count is not incremented.
 **/
TranslatePair *
translate_pairs_find (const GSList *pairs, const char *from, const char *to)
{
  const GSList *l;

  for (l = pairs; l != NULL; l = l->next)
    {
      TranslatePair *pair = l->data;

      if ((! from || ! g_ascii_strcasecmp(pair->priv->from, from))
	  && (! to || ! g_ascii_strcasecmp(pair->priv->to, to)))
	return pair;
    }

  return NULL;
}
