/* eLectrix - a pdf viewer
 * Copyright (C) 2010, 2011 Martin Linder <mali2297@users.sf.net>
 *
 * 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, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 */
#include <gtk/gtk.h>
#include "e6x-common.h"
#include "e6x-util.h"
#include "e6x-window.h"
#include "e6x-pref.h"

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef ENABLE_DBUS
# include <dbus/dbus-glib-lowlevel.h>
# define BUS_NAME "net.sf.electrix"
# define BUS_OBJECT_PATH "/net/sf/electrix"
# define BUS_INTERFACE BUS_NAME
# define BUS_SIGNAL_MATCH_RULE \
   "type='signal',interface='net.sf.electrix'"
# define BUS_DUP_SIGNAL "duplicate_instance_invoked"

void send_bus_signal (DBusConnection *bus,
                      const gchar *signal_str,
                      gint argc,
                      gchar ***argv);
DBusHandlerResult receive_bus_signal (DBusConnection *bus,
                                      DBusMessage *msg,
                                      void *win);
#endif /* ENABLE_DBUS */

void open_documents (gint argc,
                     gchar **argv,
                     E6xWindow *win);

gboolean version = FALSE;
gboolean separate_instance = FALSE;

static GOptionEntry entries[] =
{
  { "version", 'v', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
      &version, "Print version information and exit", NULL },
  { "separate-instance", 's', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
      &separate_instance, "Open separate instance", NULL },
  { NULL }
};

int
main (int argc, char **argv)
{
  GtkWidget *win = NULL;
  GError *error = NULL;
  gchar *config_dir_path = NULL;
  gchar *accel_map_path = NULL;
  gchar *preferences_path = NULL;

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

  gtk_init_with_args (&argc, &argv, "[FILE...]", entries,
                      GETTEXT_PACKAGE, &error);
  g_set_application_name (_("eLectrix"));

  if (G_UNLIKELY (error != NULL))
  {
    g_print ("%s\nSee '%s --help' for information.\n",
             error->message, argv[0]);
    g_clear_error (&error);
    return 1;
  }
  if (G_UNLIKELY (version != FALSE))
  {
    g_print ("%s %s\n", g_get_application_name (), VERSION);
    return 0;
  }

#ifdef ENABLE_DBUS
  DBusConnection *bus = NULL;
  gint ret_code = 0;

  if (separate_instance == FALSE)
    bus = dbus_bus_get (DBUS_BUS_SESSION, NULL);
  if (bus != NULL)
    ret_code = dbus_bus_request_name(bus, BUS_NAME,
                                     DBUS_NAME_FLAG_DO_NOT_QUEUE, NULL);
	if (ret_code == DBUS_REQUEST_NAME_REPLY_EXISTS)
  {
    send_bus_signal (bus, BUS_DUP_SIGNAL, argc, &argv);
    dbus_connection_unref (bus);
    return 0;
  }
#endif /* ENABLE_DBUS */

  win = e6x_window_new ();
  open_documents (argc, argv, E6X_WINDOW (win));

  /* Read preferences */
  config_dir_path = g_strdup_printf ("%s/electrix", g_get_user_config_dir());
  preferences_path = g_strdup_printf ("%s/electrixrc", config_dir_path);
  accel_map_path = g_strdup_printf ("%s/accel-map.scm", config_dir_path);
  if (g_mkdir_with_parents (config_dir_path, 0755) == 0)
  {
    e6x_pref_load (preferences_path);
    gtk_accel_map_load (accel_map_path);
  }

#ifdef ENABLE_DBUS
  if (bus != NULL)
  {
    dbus_connection_setup_with_g_main (bus, NULL);
    dbus_bus_add_match (bus, BUS_SIGNAL_MATCH_RULE, NULL);
    dbus_connection_add_filter (bus, receive_bus_signal, win, NULL);
  }
#endif /* ENABLE_DBUS */

  /* Run main loop */
  gtk_main ();
  
  /* Destroy all open windows */
  GList *l = NULL;
  while ((l = gtk_window_list_toplevels ()) != NULL) 
  {
    gtk_widget_destroy (GTK_WIDGET (l->data));
    g_list_free (l);
  }

  /* Save preferences and clear up associated memory */
  if (g_mkdir_with_parents (config_dir_path, 0755) == 0)
  {
    e6x_pref_save (preferences_path);
    gtk_accel_map_save (accel_map_path);
  }
  e6x_pref_free ();
  g_free (preferences_path);
  g_free (accel_map_path);
  g_free (config_dir_path);
  
#ifdef ENABLE_DBUS
  dbus_connection_unref (bus);
#endif /* ENABLE_DBUS */

  return 0;
}

#ifdef ENABLE_DBUS
void
send_bus_signal (DBusConnection *bus,
                 const gchar *signal_str,
                 gint argc,
                 gchar ***argv)
{
  DBusMessage *msg = NULL;

  msg = dbus_message_new_signal (BUS_OBJECT_PATH,
                                 BUS_INTERFACE,
                                 signal_str);
  dbus_message_append_args (msg,
                            DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, argv, argc,
                            DBUS_TYPE_INVALID);
  dbus_connection_send (bus, msg, NULL);
  dbus_connection_flush (bus);

  dbus_message_unref (msg);
}

DBusHandlerResult
receive_bus_signal (DBusConnection *bus,
                    DBusMessage *msg,
                    void *win)
{
  g_return_val_if_fail (E6X_IS_WINDOW (win),
                        DBUS_HANDLER_RESULT_NOT_YET_HANDLED);

  gchar **argv = NULL;
  gint argc = 0;

  if (dbus_message_is_signal (msg, BUS_INTERFACE, BUS_DUP_SIGNAL))
  {
    dbus_message_get_args (msg, NULL,
                           DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &argv, &argc,
                           DBUS_TYPE_INVALID);
    open_documents (argc, argv, E6X_WINDOW (win));
    gtk_window_present (GTK_WINDOW (win));
    dbus_free_string_array (argv);

    return DBUS_HANDLER_RESULT_HANDLED;
  }

  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
#endif /* ENABLE_DBUS */

void
open_documents (gint argc,
                gchar **argv,
                E6xWindow *win)
{
  GError *error = NULL;
  gint i;

  for (i = 1; i < argc; i++)
  {
    E6xDocument *doc = NULL;

    doc = e6x_util_open_document (argv[i], NULL, &error);
    if (G_UNLIKELY (error != NULL))
    {
      g_print ("Unable to read %s:\n%s\n", argv[i], error->message);
      g_clear_error (&error);
    }
    if (G_LIKELY (doc != NULL))
    {
      e6x_window_add_document (E6X_WINDOW (win), doc);
      g_object_unref (doc);
    }
  }
}

