/*
 * 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 <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
#include <locale.h>
#include <glib/gi18n.h>
#include "translate.h"

static void translate_cli_fatal (const char *format, ...) G_GNUC_NORETURN G_GNUC_PRINTF(1, 2);

static void translate_cli_print_help (void);
static void translate_cli_print_version (void);
static void translate_cli_list_services (void);
static void translate_cli_list_pairs (TranslateSession *session,
				      const char *from,
				      const char *to);
static void translate_cli_translate (TranslateSession *session,
				     const char *from,
				     const char *to,
				     const char *arg);
static void translate_cli_translate_text (TranslateSession *session,
					  const char *from,
					  const char *to,
					  const char *filename,
					  gboolean tty);
static void translate_cli_translate_web_page (TranslateSession *session,
					      const char *from,
					      const char *to,
					      const char *url,
					      gboolean tty);
static gboolean translate_cli_progress_cb (double progress, gpointer user_data);

int
main (int argc, char **argv)
{
  int c;
  char mode = 0;		/* translate */
  char *services = NULL;
  char *from = NULL;
  char *to = NULL;
  gboolean set_max_threads = FALSE;
  unsigned int max_threads;
  gboolean set_max_retries = FALSE;
  int max_retries;
#define OPT_HELP		'?'
#define OPT_HELP_S		"?"
#define OPT_VERSION		'v'
#define OPT_VERSION_S		"v"
#define OPT_LIST_SERVICES	'1'
#define OPT_SERVICES		's'
#define OPT_SERVICES_S		"s"
#define OPT_FROM		'f'
#define OPT_FROM_S		"f"
#define OPT_TO			't'
#define OPT_TO_S		"t"
#define OPT_LIST_PAIRS		'l'
#define OPT_LIST_PAIRS_S	"l"
#define OPT_MAX_THREADS		'2'
#define OPT_MAX_RETRIES		'3'
  const struct option options[] = {
    { "help",		no_argument,		NULL, OPT_HELP		},
    { "version",	no_argument,		NULL, OPT_VERSION	},
    { "list-services",	no_argument,		NULL, OPT_LIST_SERVICES	},
    { "services",	required_argument,	NULL, OPT_SERVICES	},
    { "from",		required_argument,	NULL, OPT_FROM		},
    { "to",		required_argument,	NULL, OPT_TO		},
    { "list-pairs",	no_argument,		NULL, OPT_LIST_PAIRS	},
    { "max-threads",	required_argument,	NULL, OPT_MAX_THREADS	},
    { "max-retries",	required_argument,	NULL, OPT_MAX_RETRIES	},
    { NULL,		0,			NULL, 0			}
  };
    
  g_set_prgname(argc > 0 ? argv[0] : "translate");
  setlocale(LC_ALL, "");	/* use user's locale */

#ifdef ENABLE_NLS
  bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
  bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
  textdomain(GETTEXT_PACKAGE);
#endif

  while ((c = getopt_long(argc, argv,
			  OPT_HELP_S
			  OPT_VERSION_S
			  OPT_SERVICES_S	":"
			  OPT_FROM_S		":"
			  OPT_TO_S		":"
			  OPT_LIST_PAIRS_S,
			  options, NULL)) != -1)
    switch (c)
      {
      case OPT_HELP:
      case OPT_VERSION:
      case OPT_LIST_SERVICES:
      case OPT_LIST_PAIRS:
	mode = c;
	break;

      case OPT_SERVICES:
	g_free(services);
	services = g_strdup(optarg);
	break;

      case OPT_FROM:
	g_free(from);
	from = g_strdup(optarg);
	break;

      case OPT_TO:
	g_free(to);
	to = g_strdup(optarg);
	break;

      case OPT_MAX_THREADS:
	set_max_threads = TRUE;
	max_threads = atoi(optarg);
	break;

      case OPT_MAX_RETRIES:
	set_max_retries = TRUE;
	max_retries = atoi(optarg);
	break;

      default:
	g_assert_not_reached();
      }

  argc -= optind;
  argv += optind;

  if (argc > (mode ? 0: 1))
    translate_cli_fatal(_("too many arguments"));

  if (mode == OPT_HELP)
    translate_cli_print_help();
  else if (mode == OPT_VERSION)
    translate_cli_print_version();
  else
    {
      GError *err = NULL;
      const char *http_proxy;
      
      if (! translate_init(&err))
	translate_cli_fatal(_("unable to initialize libtranslate: %s"), err->message);

      http_proxy = g_getenv("HTTP_PROXY");
      if (! http_proxy)
	http_proxy = g_getenv("http_proxy");
      
      translate_set_proxy(http_proxy);

      if (mode == OPT_LIST_SERVICES)
	translate_cli_list_services();
      else
	{
	  GSList *list = NULL;
	  TranslateSession *session;

	  if (services)
	    {
	      char **vector;
	      int i;
	      
	      vector = g_strsplit(services, ",", 0);
	      for (i = 0; vector[i]; i++)
		{
		  TranslateService *service;
		  
		  service = translate_get_service(vector[i]);
		  if (! service)
		    translate_cli_fatal(_("unknown service \"%s\""), vector[i]);
		  
		  list = g_slist_append(list, service);
		}
	      g_strfreev(vector);
	    }
	  else
	    list = translate_get_services();

	  session = translate_session_new(list);

	  g_slist_foreach(list, (GFunc) g_object_unref, NULL);
	  g_slist_free(list);

	  if (set_max_threads)
	    translate_session_set_max_threads(session, max_threads);
	  if (set_max_retries)
	    translate_session_set_max_retries(session, max_retries);

	  if (mode == OPT_LIST_PAIRS)
	    translate_cli_list_pairs(session, from, to);
	  else
	    translate_cli_translate(session, from, to, argc == 1 ? argv[0] : NULL);

	  g_object_unref(session);
	}
    }
      
  /* on some systems, the return value of main() is ignored */
  exit(0);

  return 0;
}

static void
translate_cli_fatal (const char *format, ...)
{
  va_list args;
  char *message;

  g_assert(format != NULL);

  va_start(args, format);
  message = g_strdup_vprintf(format, args);
  va_end(args);

  g_printerr("%s: %s\n", g_get_prgname(), message);
  g_free(message);

  exit(1);
}

static void
translate_cli_print_help (void)
{
  g_print("%s\n", _("Synopsis:"));
  g_print("  %s {-? | -v | --list-services}\n", g_get_prgname());
  g_print("  %s [-s SERVICES] [-f LANG] [-t LANG] -l\n", g_get_prgname());
  g_print("  %s [-s SERVICES] [-f LANG] [-t LANG] {HTTP_URL | HTTPS_URL}\n", g_get_prgname());
  g_print("  %s [-s SERVICES] [-f LANG] [-t LANG] [--max-threads=N] [--max-retries=N] [FILE]\n", g_get_prgname());
  g_print("\n");
  g_print("%s\n", _("Options:"));
  g_print("  -?, --help               %s\n", _("Show this help"));
  g_print("  -v, --version            %s\n", _("Show version information"));
  g_print("      --list-services      %s\n", _("List the available services"));
  g_print("  -s, --services=SERVICES  %s\n", _("Specify the list of services to use"));
  g_print("  -f, --from=LANG          %s\n", _("Specify the source language"));
  g_print("  -t, --to=LANG            %s\n", _("Specify the destination language"));
  g_print("  -l, --list-pairs         %s\n", _("List the available language pairs"));
  g_print("      --max-threads=N      %s\n", _("Specify the maximum number of threads to use"));
  g_print("      --max-retries=N      %s\n", _("Specify the maximum number of retries per chunk"));
}

static void
translate_cli_print_version (void)
{
  g_print(_("%s version %s\n"), "translate", VERSION);
  g_print("Copyright (C) 2004, 2005 Jean-Yves Lefort\n");
}

static void
translate_cli_list_services (void)
{
  GSList *l;
  GSList *services;

  services = translate_get_services();

  for (l = services; l != NULL; l = l->next)
    g_print("%s (%s)\n", translate_service_get_name(l->data), translate_service_get_nick(l->data));

  g_slist_foreach(services, (GFunc) g_object_unref, NULL);
  g_slist_free(services);
}

static void
translate_cli_list_pairs (TranslateSession *session,
			  const char *from,
			  const char *to)
{
  GSList *pairs;
  GSList *l;

  g_return_if_fail(TRANSLATE_IS_SESSION(session));

  pairs = translate_session_get_pairs(session);
  pairs = translate_pairs_sort_by_name(pairs);

  for (l = pairs; l != NULL; l = l->next)
    {
      TranslatePair *pair = l->data;
      const char *pair_from = translate_pair_get_from(pair);
      const char *pair_to = translate_pair_get_to(pair);
      
      if ((! from || ! g_ascii_strcasecmp(from, pair_from))
	  && (! to || ! g_ascii_strcasecmp(to, pair_to)))
	{
	  TranslatePairFlags flags = translate_pair_get_flags(pair);
	  
	  g_print("%s (%s) -> %s (%s): %s\n",
		  pair_from,
		  translate_get_language_name(pair_from),
		  pair_to,
		  translate_get_language_name(pair_to),
		  (flags & TRANSLATE_PAIR_TEXT && flags & TRANSLATE_PAIR_WEB_PAGE)
		  ? _("text, web page")
		  : ((flags & TRANSLATE_PAIR_TEXT)
		     ? _("text")
		     : ((flags & TRANSLATE_PAIR_WEB_PAGE)
			? _("web page")
			: "")));
	}
    }
  
  g_slist_foreach(pairs, (GFunc) g_object_unref, NULL);
  g_slist_free(pairs);
}

static void
translate_cli_translate (TranslateSession *session,
			 const char *from,
			 const char *to,
			 const char *arg)
{
  const char *var;
  char *default_from;
  char *default_to;
  gboolean tty;
  
  var = g_getenv("TRANSLATE_DEFAULT_FROM");
  default_from = g_strdup(var ? var : "en");

  var = g_getenv("TRANSLATE_DEFAULT_TO");
  default_to = g_strdup(var ? var : "fr");

  if (! from)
    from = ! to || g_ascii_strcasecmp(to, default_from) ? default_from : default_to;
  if (! to)
    to = ! from || g_ascii_strcasecmp(from, default_to) ? default_to : default_from;

  tty = isatty(fileno(stderr));

  if (arg && (g_str_has_prefix(arg, "http://") || g_str_has_prefix(arg, "https://")))
    translate_cli_translate_web_page(session, from, to, arg, tty);
  else
    translate_cli_translate_text(session, from, to, arg, tty);

  g_free(default_from);
  g_free(default_to);
}

static void
translate_cli_translate_text (TranslateSession *session,
			      const char *from,
			      const char *to,
			      const char *filename,
			      gboolean tty)
{
  FILE *file;
  char buf[4096];
  GString *string;
  size_t len;
  char *converted;
  char *translated;
  GError *err = NULL;
      
  g_return_if_fail(TRANSLATE_IS_SESSION(session));
  g_return_if_fail(from != NULL);
  g_return_if_fail(to != NULL);

  if (filename && strcmp(filename, "-"))
    {
      file = fopen(filename, "r");
      if (! file)
	translate_cli_fatal(_("unable to open \"%s\": %s"), filename, g_strerror(errno));
    }
  else
    file = stdin;

  string = g_string_new(NULL);
	  
  while ((len = fread(buf, 1, sizeof(buf), file)))
    g_string_append_len(string, buf, len);
	      
  if (ferror(file))
    {
      if (file == stdin)
	translate_cli_fatal(_("unable to read from standard input: %s"), g_strerror(errno));
      else
	translate_cli_fatal(_("unable to read from \"%s\": %s"), filename, g_strerror(errno));
    }

  if (file != stdin)
    fclose(file);
	  
  if (g_utf8_validate(string->str, -1, NULL))
    converted = g_string_free(string, FALSE);
  else
    {
      converted = g_locale_to_utf8(string->str, -1, NULL, NULL, &err);
      g_string_free(string, TRUE);
      
      if (! converted)
	translate_cli_fatal(_("unable to convert input text to UTF-8: %s"), err->message);
    }
	  
  if (tty)
    g_printerr("[%77c]", ' ');

  translated = translate_session_translate_text(session,
						converted,
						from,
						to,
						tty ? translate_cli_progress_cb : NULL,
						NULL,
						&err);
  g_free(converted);

  if (tty)
    g_printerr("\r%79c\r", ' ');

  if (translated)
    {
      g_print("%s", translated);
      g_free(translated);
    }
  else
    translate_cli_fatal(_("unable to translate: %s"), err->message);
}

static void
translate_cli_translate_web_page (TranslateSession *session,
				  const char *from,
				  const char *to,
				  const char *url,
				  gboolean tty)
{
  char *translated_url;
  GError *err = NULL;

  g_return_if_fail(TRANSLATE_IS_SESSION(session));
  g_return_if_fail(from != NULL);
  g_return_if_fail(to != NULL);
  g_return_if_fail(url != NULL);

  if (tty)
    g_printerr("[%77c]", ' ');
  
  translated_url = translate_session_translate_web_page(session,
							url,
							from,
							to,
							tty ? translate_cli_progress_cb : NULL,
							NULL,
							&err);
  
  if (tty)
    g_printerr("\r%79c\r", ' ');

  if (translated_url)
    {
      g_print("%s\n", translated_url);
      g_free(translated_url);
    }
  else
    translate_cli_fatal(_("unable to get URL of translated web page: %s"), err->message);
}

static gboolean
translate_cli_progress_cb (double progress, gpointer user_data)
{
  int i;

  g_printerr("\r[");

  if (progress < 0)
    {
      static int pre_spaces = 0;
      static int direction = 1;
      int post_spaces;

      switch (direction)
	{
	case 1:
	  if (pre_spaces + 1 > 74)
	    direction = -1;
	  break;
	case -1:
	  if (pre_spaces - 1 < 0)
	    direction = 1;
	  break;
	}

      switch (direction)
	{
	case 1:
	  pre_spaces++;
	  break;
	case -1:
	  pre_spaces--;
	  break;
	}
      
      post_spaces = 74 - pre_spaces;

      for (i = 0; i < pre_spaces; i++)
	g_printerr(" ");
      g_printerr("###");
      for (i = 0; i < post_spaces; i++)
	g_printerr(" ");
    }
  else
    {
      int n_dashes;
      int n_spaces;
      
      n_dashes = progress * 77;
      n_spaces = 77 - n_dashes;

      for (i = 0; i < n_dashes; i++)
	g_printerr("#");
      for (i = 0; i < n_spaces; i++)
	g_printerr(" ");
    }
      
  g_printerr("]");

  return TRUE;			/* continue */
}
