/*
 * 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-service-private.h"
#include "translate-pair-private.h"
#include "translate.h"

#define LOCK(service)	g_mutex_lock((service)->priv->mutex)
#define UNLOCK(service)	g_mutex_unlock((service)->priv->mutex)

enum {
  PROP_0,
  PROP_NAME,
  PROP_NICK,
  PROP_MAX_CHUNK_LEN,
  PROP_PAIRS
};

struct _TranslateServicePrivate
{
  char			*name;
  char			*nick;
  unsigned int		max_chunk_len;

  GMutex		*mutex;
  gboolean		pairs_set;
  GSList		*pairs;
};

static GObjectClass *parent_class = NULL;

static void translate_service_register_type (GType *type);
static void translate_service_class_init (TranslateServiceClass *class);
static void translate_service_init (TranslateService *service);
static void translate_service_finalize (GObject *object);
static void translate_service_set_property (GObject *object,
					    unsigned int prop_id,
					    const GValue *value,
					    GParamSpec *pspec);
static void translate_service_get_property (GObject *object,
					    unsigned int prop_id,
					    GValue *value,
					    GParamSpec *pspec);

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

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

  return type;
}

static void
translate_service_register_type (GType *type)
{
  static const GTypeInfo info = {
    sizeof(TranslateServiceClass),
    NULL,
    NULL,
    (GClassInitFunc) translate_service_class_init,
    NULL,
    NULL,
    sizeof(TranslateService),
    0,
    (GInstanceInitFunc) translate_service_init
  };
      
  *type = g_type_register_static(G_TYPE_OBJECT,
				 "TranslateService",
				 &info,
				 G_TYPE_FLAG_ABSTRACT);
}

static void
translate_service_class_init (TranslateServiceClass *class)
{
  GObjectClass *object_class = G_OBJECT_CLASS(class);

  g_type_class_add_private(class, sizeof(TranslateServicePrivate));
  parent_class = g_type_class_peek_parent(class);
  
  object_class->finalize = translate_service_finalize;
  object_class->set_property = translate_service_set_property;
  object_class->get_property = translate_service_get_property;

  g_object_class_install_property(object_class,
				  PROP_NAME,
				  g_param_spec_string("name",
						      _("Name"),
						      _("The service symbolic name, encoded in ASCII"),
						      NULL,
						      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property(object_class,
				  PROP_NICK,
				  g_param_spec_string("nick",
						      _("Nick"),
						      _("The service human-readable name"),
						      NULL,
						      G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property(object_class,
				  PROP_MAX_CHUNK_LEN,
				  g_param_spec_uint("max-chunk-len",
						    _("Maximum chunk length"),
						    _("The maximum length of an input chunk, in characters (0 means unlimited)"),
						    0,
						    G_MAXUINT,
						    0,
						    G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property(object_class,
				  PROP_PAIRS,
				  g_param_spec_pointer("pairs",
						       _("Pairs"),
						       _("The list of language pairs this service implements"),
						       G_PARAM_READABLE));
}

static void
translate_service_init (TranslateService *service)
{
  service->priv = G_TYPE_INSTANCE_GET_PRIVATE(service,
					      TRANSLATE_TYPE_SERVICE,
					      TranslateServicePrivate);
  service->priv->mutex = g_mutex_new();
}

static void
translate_service_finalize (GObject *object)
{
  TranslateService *service = TRANSLATE_SERVICE(object);

  g_free(service->priv->name);
  g_free(service->priv->nick);
  
  g_mutex_free(service->priv->mutex);
  g_slist_foreach(service->priv->pairs, (GFunc) g_object_unref, NULL);
  g_slist_free(service->priv->pairs);

  parent_class->finalize(object);
}

static void
translate_service_set_property (GObject *object,
				unsigned int prop_id,
				const GValue *value,
				GParamSpec *pspec)
{
  TranslateService *service = TRANSLATE_SERVICE(object);

  switch (prop_id)
    {
    case PROP_NAME:
      service->priv->name = g_value_dup_string(value);
      break;

    case PROP_NICK:
      service->priv->nick = g_value_dup_string(value);
      break;

    case PROP_MAX_CHUNK_LEN:
      service->priv->max_chunk_len = g_value_get_uint(value);
      break;
    
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
      break;
    }
}

static void
translate_service_get_property (GObject *object,
				unsigned int prop_id,
				GValue *value,
				GParamSpec *pspec)
{
  TranslateService *service = TRANSLATE_SERVICE(object);

  switch (prop_id)
    {
    case PROP_NAME:
      g_value_set_string(value, service->priv->name);
      break;

    case PROP_NICK:
      g_value_set_string(value, service->priv->nick);
      break;

    case PROP_MAX_CHUNK_LEN:
      g_value_set_uint(value, service->priv->max_chunk_len);
      break;

    case PROP_PAIRS:
      LOCK(service);
      if (! service->priv->pairs_set)
	{
	  TranslateServiceClass *class = TRANSLATE_SERVICE_GET_CLASS(service);
	  GError *err = NULL;

	  if (class->get_pairs)
	    {
	      if (class->get_pairs(service, &service->priv->pairs, NULL, NULL, &err))
		{
		  GSList *l;

		  /* hunt down programming errors */
		  
		  for (l = service->priv->pairs; l != NULL; l = l->next)
		    {
		      TranslatePair *pair = l->data;
		      TranslatePairFlags flags = translate_pair_get_flags(pair);
		      
		      if (! class->translate_text && (flags & TRANSLATE_PAIR_TEXT))
			{
			  g_critical(_("%s: the TRANSLATE_PAIR_TEXT flag is set for %s->%s, but the translate_text method is not implemented (this indicates a programming error in the service implementation): unsetting the flag"),
				     service->priv->nick,
				     translate_pair_get_from(pair),
				     translate_pair_get_to(pair));
			  flags &= ~TRANSLATE_PAIR_TEXT;
			}
			  
		      if (! class->translate_web_page && (flags & TRANSLATE_PAIR_WEB_PAGE))
			{
			  g_critical(_("%s: the TRANSLATE_PAIR_WEB_PAGE flag is set for %s->%s, but the translate_web_page method is not implemented (this indicates a programming error in the service implementation): unsetting the flag"),
				     service->priv->nick,
				     translate_pair_get_from(pair),
				     translate_pair_get_to(pair));
			  flags &= ~TRANSLATE_PAIR_WEB_PAGE;
			}

		      translate_pair_set_flags(pair, flags);
		    }
		}
	      else
		{
		  g_warning(_("%s: unable to get language pairs: %s"), service->priv->nick, err->message);
		  g_error_free(err);
		}
	    }
	  else
	    g_critical(_("%s: the get_pairs method is not implemented (this indicates a programming error in the service implementation)"),
		       service->priv->nick);

	  service->priv->pairs_set = TRUE;
	}
      UNLOCK(service);
      g_value_set_pointer(value, service->priv->pairs);
      break;

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

/**
 * translate_service_get_name:
 * @service: a service.
 *
 * Gets the symbolic name of @service.
 *
 * Return value: the symbolic name of @service, encoded in ASCII.
 **/
const char *
translate_service_get_name (TranslateService *service)
{
  g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);

  return service->priv->name;
}

/**
 * translate_service_get_nick:
 * @service: a service.
 *
 * Gets the human-readable name of @service.
 *
 * Return value: the human-readable name of @service.
 **/
const char *
translate_service_get_nick (TranslateService *service)
{
  g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);

  return service->priv->nick;
}

/**
 * translate_service_get_max_chunk_len:
 * @service: a service.
 *
 * Gets the maximum chunk length of @service.
 *
 * Return value: the maximum chunk length of @service, in characters.
 **/
unsigned int
translate_service_get_max_chunk_len (TranslateService *service)
{
  g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), 0);

  return service->priv->max_chunk_len;
}

/**
 * translate_service_get_pairs:
 * @service: a service.
 *
 * Gets the list of language pairs implemented by @service.
 *
 * Return value: a list of #TranslatePair objects.
 **/
const GSList *
translate_service_get_pairs (TranslateService *service)
{
  const GSList *pairs;

  g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);

  g_object_get(G_OBJECT(service), "pairs", &pairs, NULL);

  return pairs;
}

char *
translate_service_translate_text (TranslateService *service,
				  const char *text,
				  const char *from,
				  const char *to,
				  TranslateProgressFunc progress_func,
				  gpointer user_data,
				  GError **err)
{
  g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);
  g_return_val_if_fail(TRANSLATE_SERVICE_GET_CLASS(service)->translate_text != NULL, NULL);
  g_return_val_if_fail(text != NULL, NULL);
  g_return_val_if_fail(from != NULL, NULL);
  g_return_val_if_fail(to != NULL, NULL);

  return TRANSLATE_SERVICE_GET_CLASS(service)->translate_text(service, text, from, to, progress_func, user_data, err);
}

char *
translate_service_translate_web_page (TranslateService *service,
				      const char *url,
				      const char *from,
				      const char *to,
				      TranslateProgressFunc progress_func,
				      gpointer user_data,
				      GError **err)
{
  g_return_val_if_fail(TRANSLATE_IS_SERVICE(service), NULL);
  g_return_val_if_fail(TRANSLATE_SERVICE_GET_CLASS(service)->translate_web_page != NULL, NULL);
  g_return_val_if_fail(url != NULL, NULL);
  g_return_val_if_fail(from != NULL, NULL);
  g_return_val_if_fail(to != NULL, NULL);

  return TRANSLATE_SERVICE_GET_CLASS(service)->translate_web_page(service, url, from, to, progress_func, user_data, err);
}
