/* GIO - GLib Input, Output and Streaming Library
 *
 * Copyright © 2010 Red Hat, Inc
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser 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.
 *
 * Authors: Colin Walters <walters@verbum.org>
 *          Emmanuele Bassi <ebassi@linux.intel.com>
 */

#include "config.h"

#include <string.h>
#include <stdlib.h>

#include <gobject/gvaluecollector.h>

#include "gapplication.h"
#include "gio-marshal.h"
#include "glibintl.h"

#include "gioerror.h"
#include "ginitable.h"

#include "gdbusconnection.h"
#include "gdbusintrospection.h"
#include "gdbusmethodinvocation.h"


/**
 * SECTION: gapplication
 * @title: GApplication
 * @short_description: Core application class
 *
 * A #GApplication is the foundation of an application, unique for a
 * given application identifier.  The #GApplication wraps some
 * low-level platform-specific services and is intended to act as the
 * foundation for higher-level application classes such as
 * #GtkApplication or #MxApplication.  In general, you should not use
 * this class outside of a higher level framework.  By default,
 * g_application_register_with_data() will invoke g_error() if it is
 * run in a context where it cannot support its core features.  Note
 * that g_error() is by default fatal.
 *
 * One of the core features that #GApplication provides is process
 * uniqueness, in the context of a "session".  The session concept is
 * platform-dependent, but corresponds roughly to a graphical desktop
 * login.  When your application is launched again, its arguments
 * are passed through platform communication to the already running
 * program.
 *
 * In addition, #GApplication provides support for 'actions', which
 * can be presented to the user in a platform-specific way
 * (e.g. Windows 7 jump lists). Note that these are just simple
 * actions without parameters. For more flexible scriptability,
 * implementing a a separate D-Bus interface is recommended, see e.g.
 * <xref linkend="gdbus-convenience"/>.
 * 
 * Finally, #GApplication acts as a basic lifecycle root; see the
 * g_application_run() and g_application_quit_with_data() methods.
 *
 * Before using #GApplication, you must choose an "application identifier".
 * The expected form of an application identifier is very close to that of
 * of a <ulink url="http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-interface">DBus bus name</ulink>.
 * Examples include: "com.example.MyApp" "org.example.internal-apps.Calculator"
 * For convenience, the restrictions on application identifiers are reproduced
 * here:
 * <itemizedlist>
 *   <listitem>Application identifiers must contain only the ASCII characters "[A-Z][a-z][0-9]_-" and must not begin with a digit.</listitem>
 *   <listitem>Application identifiers must contain at least one '.' (period) character (and thus at least two elements).</listitem>
 *   <listitem>Application identifiers must not begin with a '.' (period) character.</listitem>
 *   <listitem>Application identifiers must not exceed 255 characters.</listitem>
 * </itemizedlist>
 *
 * <refsect2><title>D-Bus implementation</title>
 * <para>
 * On UNIX systems using D-Bus, #GApplication is implemented by claiming the
 * application identifier as a bus name on the session bus. The implementation
 * exports an object at the object path that is created by replacing '.' with
 * '/' in the application identifier (e.g. the object path for the
 * application id 'org.gtk.TestApp' is '/org/gtk/TestApp'). The object
 * implements the org.gtk.Application interface.
 * </para>
 * <classsynopsis class="interface">
 *   <ooclass><interfacename>org.gtk.Application</interfacename></ooclass>
 *   <methodsynopsis>
 *     <void/>
 *     <methodname>Activate</methodname>
 *     <methodparam><modifier>in</modifier><type>aay</type><parameter>arguments</parameter></methodparam>
 *     <methodparam><modifier>in</modifier><type>a{sv}</type><parameter>data</parameter></methodparam>
 *   </methodsynopsis>
 *   <methodsynopsis>
 *     <void/>
 *     <methodname>InvokeAction</methodname>
 *     <methodparam><modifier>in</modifier><type>s</type><parameter>action</parameter></methodparam>
 *     <methodparam><modifier>in</modifier><type>a{sv}</type><parameter>data</parameter></methodparam>
 *   </methodsynopsis>
 *   <methodsynopsis>
 *     <type>a{s(sb)}</type>
 *     <methodname>ListActions</methodname>
 *     <void/>
 *   </methodsynopsis>
 *   <methodsynopsis>
 *     <void/>
 *     <methodname>Quit</methodname>
 *     <methodparam><modifier>in</modifier><type>a{sv}</type><parameter>data</parameter></methodparam>
 *   </methodsynopsis>
 *   <methodsynopsis>
 *     <modifier>Signal</modifier>
 *     <void/>
 *     <methodname>ActionsChanged</methodname>
 *     <void/>
 *   </methodsynopsis>
 * </classsynopsis>
 * <para>
 * The <methodname>Activate</methodname> function is called on the existing
 * application instance when a second instance fails to take the bus name.
 * @arguments contains the commandline arguments given to the second instance
 * and @data contains platform-specific additional data.
 *
 * On all platforms, @data will have a key "cwd" of type signature
 * "ay" which contains the working directory of the invoked
 * executable; this data is defined to be in the default GLib
 * filesystem encoding for the platform.  See g_filename_to_utf8().
 *
 * </para>
 * <para>
 * The <methodname>InvokeAction</methodname> function can be called to
 * invoke one of the actions exported by the application.  On X11
 * platforms, the platform_data argument should have a "timestamp"
 * parameter of type "u" with the server time of the initiating event.
 * </para>
 * <para>
 * The <methodname>ListActions</methodname> function returns a dictionary
 * with the exported actions of the application. The keys of the dictionary
 * are the action names, and the values are structs containing the description
 * for the action and a boolean that represents if the action is enabled or not.
 * </para>
 * <para>
 * The <methodname>Quit</methodname> function can be called to
 * terminate the application. The @data parameter contains
 * platform-specific data.  On X11 platforms, the platform_data
 * argument should have a "timestamp" parameter of type "u" with the
 * server time of the initiating event.
 * </para>
 * <para>
 * The <methodname>ActionsChanged</methodname> signal is emitted when the
 * exported actions change (i.e. an action is added, removed, enabled,
 * disabled, or otherwise changed).
 * </para>
 * <para>
 * #GApplication is supported since Gio 2.26.
 * </para>
 * </refsect2>
 */

static void initable_iface_init       (GInitableIface      *initable_iface);

G_DEFINE_TYPE_WITH_CODE (GApplication, g_application, G_TYPE_OBJECT,
                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init));


enum
{
  PROP_0,

  PROP_APPLICATION_ID,
  PROP_REGISTER,
  PROP_DEFAULT_QUIT,
  PROP_IS_REMOTE,
  PROP_ARGV,
  PROP_PLATFORM_DATA
};

enum
{
  QUIT_WITH_DATA,
  ACTION_WITH_DATA,
  PREPARE_ACTIVATION,

  LAST_SIGNAL
};

static guint application_signals[LAST_SIGNAL] = { 0 };

typedef struct {
  gchar *name;
  gchar *description;
  guint enabled : 1;
} GApplicationAction;

struct _GApplicationPrivate
{
  gchar *appid;
  GHashTable *actions; /* name -> GApplicationAction */
  GMainLoop *mainloop;

  GVariant *argv;
  GVariant *platform_data;

  guint do_register  : 1;
  guint default_quit : 1;
  guint is_remote    : 1;

  guint actions_changed_id;

#ifdef G_OS_UNIX
  gchar *dbus_path;
  GDBusConnection *session_bus;
#endif
};

static GApplication *primary_application = NULL;
static GHashTable *instances_for_appid = NULL;

static gboolean initable_init (GInitable     *initable,
			       GCancellable  *cancellable,
			       GError       **error);

static gboolean _g_application_platform_init                    (GApplication  *app,
								 GCancellable  *cancellable,
								 GError       **error); 
static gboolean _g_application_platform_register                (GApplication  *app,
								 gboolean      *unique,
								 GCancellable  *cancellable,
								 GError       **error); 

static void     _g_application_platform_remote_invoke_action    (GApplication  *app,
                                                                 const gchar   *action,
                                                                 GVariant      *platform_data);
static void     _g_application_platform_remote_quit             (GApplication  *app,
                                                                 GVariant      *platform_data);
static void     _g_application_platform_on_actions_changed      (GApplication  *app);

static void
initable_iface_init (GInitableIface *initable_iface)
{
  initable_iface->init = initable_init;
}

#ifdef G_OS_UNIX
#include "gdbusapplication.c"
#else
#include "gnullapplication.c"
#endif

static gboolean
_g_application_validate_id (const char *id)
{
  gboolean allow_dot;

  if (strlen (id) > 255)
    return FALSE;

  if (!g_ascii_isalpha (*id))
    return FALSE;

  id++;
  allow_dot = FALSE;
  for (; *id; id++)
    {
      if (g_ascii_isalnum (*id) || (*id == '-') || (*id == '_'))
        allow_dot = TRUE;
      else if (allow_dot && *id == '.')
        allow_dot = FALSE;
      else
        return FALSE;
    }
  return TRUE;
}

static gpointer
init_appid_statics (gpointer data)
{
  instances_for_appid = g_hash_table_new (g_str_hash, g_str_equal);
  return NULL;
}

static GApplication *
application_for_appid (const char *appid)
{
  static GOnce appid_once = G_ONCE_INIT;

  g_once (&appid_once, init_appid_statics, NULL);

  return g_hash_table_lookup (instances_for_appid, appid);
}

static gboolean
g_application_default_quit_with_data (GApplication *application,
				      GVariant     *platform_data)
{
  g_return_val_if_fail (application->priv->mainloop != NULL, FALSE);
  g_main_loop_quit (application->priv->mainloop);

  return TRUE;
}

static void
g_application_default_run (GApplication *application)
{
  if (application->priv->mainloop == NULL)
    application->priv->mainloop = g_main_loop_new (NULL, TRUE);

  g_main_loop_run (application->priv->mainloop);
}

static GVariant *
append_cwd_to_platform_data (GVariant *platform_data)
{
  GVariantBuilder builder;
  gchar *cwd;
  GVariant *result;

  cwd = g_get_current_dir ();

  g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
  if (cwd)
    g_variant_builder_add (&builder, "{sv}",
			   "cwd",
			   g_variant_new_bytestring (cwd));
  g_free (cwd);

  if (platform_data)
    {
      GVariantIter iter;
      GVariant *item;

      g_variant_iter_init (&iter, platform_data);
      while (g_variant_iter_next (&iter, "@{sv}", &item))
	{
	  g_variant_builder_add_value (&builder, item);
	  g_variant_unref (item);
	}
    }
  result = g_variant_builder_end (&builder);
  return result;
}

static gboolean
timeout_handle_actions_changed (gpointer user_data)
{
  GApplication *application = user_data;

  application->priv->actions_changed_id = 0;

  _g_application_platform_on_actions_changed (application);

  return FALSE;
}

static inline void
queue_actions_change_notification (GApplication *application)
{
  GApplicationPrivate *priv = application->priv;

  if (priv->actions_changed_id == 0)
    priv->actions_changed_id = g_timeout_add (0, timeout_handle_actions_changed, application);
}

static gboolean
initable_init (GInitable     *initable,
               GCancellable  *cancellable,
               GError       **error)
{
  GApplication *app = G_APPLICATION (initable);
  gboolean unique;

  if (!_g_application_platform_init (app, cancellable, error))
    return FALSE;

  if (app->priv->do_register &&
      !_g_application_platform_register (app, &unique, cancellable ,error))
    return FALSE;

  return TRUE;
}

static void
g_application_action_free (gpointer data)
{
  if (G_LIKELY (data != NULL))
    {
      GApplicationAction *action = data;

      g_free (action->name);
      g_free (action->description);

      g_slice_free (GApplicationAction, action);
    }
}

/**
 * g_application_new:
 * @appid: System-dependent application identifier
 * @argc: Number of arguments in @argv
 * @argv: (allow-none) (array length=argc): Argument vector, usually from the <parameter>argv</parameter> parameter of main() 
 *
 * Create a new #GApplication.  This uses a platform-specific
 * mechanism to ensure the current process is the unique owner of the
 * application (as defined by the @appid). If successful, the
 * #GApplication:is-remote property will be %FALSE, and it is safe to
 * continue creating other resources such as graphics windows.
 *
 * If the given @appid is already running in another process, the the
 * GApplication::activate_with_data signal will be emitted in the
 * remote process, with the data from @argv and other
 * platform-specific data available.  Subsequently the
 * #GApplication:default-quit property will be evaluated.  If it's
 * %TRUE, then the current process will terminate.  If %FALSE, then
 * the application remains in the #GApplication:is-remote state, and
 * you can e.g. call g_application_invoke_action(). Note that proxy
 * instances should not call g_application_add_action().
 *
 * This function may do synchronous I/O to obtain unique ownership
 * of the application id, and will block the calling thread in this
 * case.
 *
 * If the environment does not support the basic functionality of
 * #GApplication, this function will invoke g_error(), which by
 * default is a fatal operation.  This may arise for example on
 * UNIX systems using D-Bus when the session bus is not available.
 *
 * As a convenience, this function is defined to call g_type_init() as
 * its very first action.
 *
 * Returns: (transfer full): An application instance
 *
 * Since: 2.26
 */
GApplication *
g_application_new (const gchar *appid,
		   int          argc,
		   char       **argv)
{
  const gchar * const *args = (const gchar **) argv;
  GObject *app;
  GError *error = NULL;
  GVariant *argv_variant;

  g_type_init ();

  g_return_val_if_fail (appid != NULL, NULL);
  
  argv_variant = g_variant_new_bytestring_array (args, argc);
  
  app = g_initable_new (G_TYPE_APPLICATION, 
			NULL,
			&error,
			"application-id", appid, 
			"argv", argv_variant, 
			NULL);
  if (!app)
    {
      g_error ("%s", error->message);
      g_clear_error (&error);
      return NULL;
    }
  return G_APPLICATION (app);
}

/**
 * g_application_try_new:
 * @appid: System-dependent application identifier
 * @argc: Number of arguments in @argv
 * @argv: (allow-none) (array length=argc): Argument vector, usually from the <parameter>argv</parameter> parameter of main() 
 * @error: a #GError
 *
 * This function is similar to g_application_new(), but allows for
 * more graceful fallback if the environment doesn't support the
 * basic #GApplication functionality.
 *
 * Returns: (transfer full): An application instance
 *
 * Since: 2.26
 */
GApplication *
g_application_try_new (const gchar *appid,
		       int          argc,
		       char       **argv,
		       GError     **error)
{
  const gchar * const *args = (const gchar **) argv;
  GVariant *argv_variant;

  g_type_init ();

  g_return_val_if_fail (appid != NULL, NULL);
  
  argv_variant = g_variant_new_bytestring_array (args, argc);
  
  return G_APPLICATION (g_initable_new (G_TYPE_APPLICATION, 
					NULL,
					error,
					"application-id", appid, 
					"argv", argv_variant, 
					NULL));
}

/**
 * g_application_unregistered_try_new:
 * @appid: System-dependent application identifier
 * @argc: Number of arguments in @argv
 * @argv: (allow-none) (array length=argc): Argument vector, usually from the <parameter>argv</parameter> parameter of main() 
 * @error: a #GError
 *
 * This function is similar to g_application_try_new(), but also
 * sets the GApplication:register property to %FALSE.  You can later
 * call g_application_register() to complete initialization.
 *
 * Returns: (transfer full): An application instance
 *
 * Since: 2.26
 */
GApplication *
g_application_unregistered_try_new (const gchar *appid,
				    int          argc,
				    char       **argv,
				    GError     **error)
{
  const gchar * const *args = (const gchar **) argv;
  GVariant *argv_variant;

  g_type_init ();

  g_return_val_if_fail (appid != NULL, NULL);
  
  argv_variant = g_variant_new_bytestring_array (args, argc);
  
  return G_APPLICATION (g_initable_new (G_TYPE_APPLICATION, 
					NULL,
					error,
					"application-id", appid, 
					"argv", argv_variant, 
					"register", FALSE,
					NULL));
}

/**
 * g_application_register:
 * @application: a #GApplication
 *
 * By default, #GApplication ensures process uniqueness when
 * initialized, but this behavior is controlled by the
 * GApplication:register property.  If it was given as %FALSE at
 * construction time, this function allows you to later attempt
 * to ensure uniqueness.
 *
 * Returns: %TRUE if registration was successful
 */
gboolean
g_application_register (GApplication *application)
{
  gboolean unique;

  g_return_val_if_fail (G_IS_APPLICATION (application), FALSE);
  g_return_val_if_fail (application->priv->is_remote, FALSE);

  if (!_g_application_platform_register (application, &unique, NULL, NULL))
    return FALSE;
  return unique;
}

/**
 * g_application_add_action:
 * @application: a #GApplication
 * @name: the action name
 * @description: the action description; can be a translatable
 *   string
 *
 * Adds an action @name to the list of exported actions of @application.
 *
 * It is an error to call this function if @application is a proxy for
 * a remote application.
 *
 * You can invoke an action using g_application_invoke_action().
 *
 * The newly added action is enabled by default; you can call
 * g_application_set_action_enabled() to disable it.
 *
 * Since: 2.26
 */
void
g_application_add_action (GApplication *application,
                          const gchar  *name,
                          const gchar  *description)
{
  GApplicationPrivate *priv;
  GApplicationAction *action;

  g_return_if_fail (G_IS_APPLICATION (application));
  g_return_if_fail (name != NULL && *name != '\0');
  g_return_if_fail (!application->priv->is_remote);

  priv = application->priv;

  g_return_if_fail (g_hash_table_lookup (priv->actions, name) == NULL);

  action = g_slice_new (GApplicationAction);
  action->name = g_strdup (name);
  action->description = g_strdup (description);
  action->enabled = TRUE;

  g_hash_table_insert (priv->actions, action->name, action);
  queue_actions_change_notification (application);
}

/**
 * g_application_remove_action:
 * @application: a #GApplication
 * @name: the name of the action to remove
 *
 * Removes the action @name from the list of exported actions of @application.
 *
 * It is an error to call this function if @application is a proxy for
 * a remote application.
 *
 * Since: 2.26
 */
void
g_application_remove_action (GApplication *application,
                             const gchar  *name)
{
  GApplicationPrivate *priv;

  g_return_if_fail (G_IS_APPLICATION (application));
  g_return_if_fail (name != NULL && *name != '\0');
  g_return_if_fail (!application->priv->is_remote);

  priv = application->priv;

  g_return_if_fail (g_hash_table_lookup (priv->actions, name) != NULL);

  g_hash_table_remove (priv->actions, name);
  queue_actions_change_notification (application);
}

/**
 * g_application_invoke_action:
 * @application: a #GApplication
 * @name: the name of the action to invoke
 * @platform_data: (allow-none): platform-specific event data
 *
 * Invokes the action @name of the passed #GApplication.
 *
 * This function has different behavior depending on whether @application
 * is acting as a proxy for another process.  In the normal case where
 * the current process is hosting the application, and the specified
 * action exists and is enabled, the #GApplication::action signal will
 * be emitted.
 *
 * If @application is a proxy, then the specified action will be invoked
 * in the remote process. It is not necessary to call
 * g_application_add_action() in the current process in order to invoke
 * one remotely.
 *
 * Since: 2.26
 */
void
g_application_invoke_action (GApplication *application,
                             const gchar  *name,
                             GVariant     *platform_data)
{
  GApplicationPrivate *priv;
  GApplicationAction *action;

  g_return_if_fail (G_IS_APPLICATION (application));
  g_return_if_fail (name != NULL);
  g_return_if_fail (platform_data == NULL
                    || g_variant_is_of_type (platform_data, G_VARIANT_TYPE ("a{sv}")));
  
  if (platform_data == NULL)
    platform_data = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
  else
    g_variant_ref (platform_data);

  priv = application->priv;
  
  if (priv->is_remote)
    {
      _g_application_platform_remote_invoke_action (application, name, platform_data);
      goto out;
    }

  action = g_hash_table_lookup (priv->actions, name);
  g_return_if_fail (action != NULL);
  if (!action->enabled)
    goto out;

  g_signal_emit (application, application_signals[ACTION_WITH_DATA],
                 g_quark_from_string (name),
                 name,
                 platform_data);

 out:
  g_variant_unref (platform_data);
}

/**
 * g_application_list_actions:
 * @application: a #GApplication
 *
 * Retrieves the list of action names currently exported by @application.
 *
 * It is an error to call this function if @application is a proxy for
 * a remote application.
 *
 * Return value: (transfer full): a newly allocation, %NULL-terminated array
 *   of strings containing action names; use g_strfreev() to free the
 *   resources used by the returned array
 *
 * Since: 2.26
 */
gchar **
g_application_list_actions (GApplication *application)
{
  GApplicationPrivate *priv;
  GHashTableIter iter;
  gpointer key;
  gchar **retval;
  gint i;

  g_return_val_if_fail (G_IS_APPLICATION (application), NULL);
  g_return_val_if_fail (!application->priv->is_remote, NULL);

  priv = application->priv;

  retval = g_new (gchar*, g_hash_table_size (priv->actions));

  i = 0;
  g_hash_table_iter_init (&iter, priv->actions);
  while (g_hash_table_iter_next (&iter, &key, NULL))
    retval[i++] = g_strdup (key);

  retval[i] = NULL;

  return retval;
}

/**
 * g_application_set_action_enabled:
 * @application: a #GApplication
 * @name: the name of the application
 * @enabled: whether to enable or disable the action @name
 *
 * Sets whether the action @name inside @application should be enabled
 * or disabled.
 *
 * It is an error to call this function if @application is a proxy for
 * a remote application.
 *
 * Invoking a disabled action will not result in the #GApplication::action
 * signal being emitted.
 *
 * Since: 2.26
 */
void
g_application_set_action_enabled (GApplication *application,
                                  const gchar  *name,
                                  gboolean      enabled)
{
  GApplicationAction *action;

  g_return_if_fail (G_IS_APPLICATION (application));
  g_return_if_fail (name != NULL);
  g_return_if_fail (!application->priv->is_remote);

  enabled = !!enabled;

  action = g_hash_table_lookup (application->priv->actions, name);
  g_return_if_fail (action != NULL);
  if (action->enabled == enabled)
    return;

  action->enabled = enabled;

  queue_actions_change_notification (application);
}


/**
 * g_application_get_action_description:
 * @application: a #GApplication
 * @name: Action name
 *
 * Gets the description of the action @name.
 *
 * It is an error to call this function if @application is a proxy for
 * a remote application.
 *
 * Returns: Description for the given action named @name
 *
 * Since: 2.26
 */
G_CONST_RETURN gchar *
g_application_get_action_description (GApplication *application,
                                      const gchar  *name)
{
  GApplicationAction *action;
  
  g_return_val_if_fail (G_IS_APPLICATION (application), NULL);
  g_return_val_if_fail (name != NULL, NULL);
  g_return_val_if_fail (!application->priv->is_remote, NULL);

  action = g_hash_table_lookup (application->priv->actions, name);
  g_return_val_if_fail (action != NULL, NULL);

  return action->description;
}


/**
 * g_application_get_action_enabled:
 * @application: a #GApplication
 * @name: the name of the action
 *
 * Retrieves whether the action @name is enabled or not.
 *
 * See g_application_set_action_enabled().
 *
 * It is an error to call this function if @application is a proxy for
 * a remote application.
 *
 * Return value: %TRUE if the action was enabled, and %FALSE otherwise
 *
 * Since: 2.26
 */
gboolean
g_application_get_action_enabled (GApplication *application,
                                  const gchar  *name)
{
  GApplicationAction *action;

  g_return_val_if_fail (G_IS_APPLICATION (application), FALSE);
  g_return_val_if_fail (name != NULL, FALSE);
  g_return_val_if_fail (!application->priv->is_remote, FALSE);

  action = g_hash_table_lookup (application->priv->actions, name);
  g_return_val_if_fail (action != NULL, FALSE);

  return action->enabled;
}

/**
 * g_application_run:
 * @application: a #GApplication
 *
 * Starts the application.
 *
 * The default implementation of this virtual function will simply run
 * a main loop.
 *
 * It is an error to call this function if @application is a proxy for
 * a remote application.
 *
 * Since: 2.26
 */
void
g_application_run (GApplication *application)
{
  g_return_if_fail (G_IS_APPLICATION (application));
  g_return_if_fail (!application->priv->is_remote);

  G_APPLICATION_GET_CLASS (application)->run (application);
}

/**
 * g_application_quit_with_data:
 * @application: a #GApplication
 * @platform_data: (allow-none): platform-specific data
 *
 * Request that the application quits.
 *
 * This function has different behavior depending on whether @application
 * is acting as a proxy for another process.  In the normal case where
 * the current process is hosting the application, the default
 * implementation will quit the main loop created by g_application_run().
 *
 * If @application is a proxy, then the remote process will be asked
 * to quit.
 *
 * Returns: %TRUE if the application accepted the request, %FALSE otherwise
 *
 * Since: 2.26
 */
gboolean
g_application_quit_with_data (GApplication *application,
			      GVariant     *platform_data)
{
  gboolean retval = FALSE;

  g_return_val_if_fail (G_IS_APPLICATION (application), FALSE);
  g_return_val_if_fail (platform_data == NULL
			|| g_variant_is_of_type (platform_data, G_VARIANT_TYPE ("a{sv}")), FALSE);

  if (platform_data == NULL)
    platform_data = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
  else
    g_variant_ref (platform_data);

  if (application->priv->is_remote)
    {
       _g_application_platform_remote_quit (application, platform_data);
       retval = TRUE;
    }
  else
    g_signal_emit (application, application_signals[QUIT_WITH_DATA], 0, platform_data, &retval);

  g_variant_unref (platform_data);

  return retval;
}

/**
 * g_application_get_instance:
 *
 * In the normal case where there is exactly one #GApplication instance
 * in this process, return that instance.  If there are multiple, the
 * first one created will be returned.  Otherwise, return %NULL.
 *
 * Returns: (transfer none): The primary instance of #GApplication,
 *   or %NULL if none is set
 *
 * Since: 2.26
 */
GApplication *
g_application_get_instance (void)
{
  return primary_application;
}

/**
 * g_application_get_id:
 * @application: a #GApplication
 *
 * Retrieves the platform-specific identifier for the #GApplication.
 *
 * Return value: The platform-specific identifier. The returned string
 *   is owned by the #GApplication instance and it should never be
 *   modified or freed
 *
 * Since: 2.26
 */
G_CONST_RETURN gchar *
g_application_get_id (GApplication *application)
{
  g_return_val_if_fail (G_IS_APPLICATION (application), NULL);

  return application->priv->appid;
}

/**
 * g_application_is_remote:
 * @application: a #GApplication
 *
 * Returns whether the object represents a proxy for a remote application.
 *
 * Returns: %TRUE if this object represents a proxy for a remote application.
 */
gboolean
g_application_is_remote (GApplication *application)
{
  g_return_val_if_fail (G_IS_APPLICATION (application), FALSE);

  return application->priv->is_remote;
}

static void
g_application_init (GApplication *app)
{
  app->priv = G_TYPE_INSTANCE_GET_PRIVATE (app,
                                           G_TYPE_APPLICATION,
                                           GApplicationPrivate);

  app->priv->actions = g_hash_table_new_full (g_str_hash, g_str_equal,
                                              NULL,
                                              g_application_action_free);
  app->priv->default_quit = TRUE;
  app->priv->do_register = TRUE;
  app->priv->is_remote = TRUE;
  app->priv->platform_data = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
}

static void
g_application_get_property (GObject    *object,
                            guint       prop_id,
                            GValue     *value,
                            GParamSpec *pspec)
{
  GApplication *app = G_APPLICATION (object);

  switch (prop_id)
    {
    case PROP_APPLICATION_ID:
      g_value_set_string (value, g_application_get_id (app));
      break;

    case PROP_DEFAULT_QUIT:
      g_value_set_boolean (value, app->priv->default_quit);
      break;

    case PROP_IS_REMOTE:
      g_value_set_boolean (value, g_application_is_remote (app));
      break;

    case PROP_REGISTER:
      g_value_set_boolean (value, app->priv->do_register);
      break;

    case PROP_ARGV:
      g_value_set_variant (value, app->priv->argv);
      break;

    case PROP_PLATFORM_DATA:
      g_value_set_variant (value, app->priv->platform_data);
      break;

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

static void
g_application_set_property (GObject      *object,
                            guint         prop_id,
                            const GValue *value,
                            GParamSpec   *pspec)
{
  GApplication *app = G_APPLICATION (object);

  switch (prop_id)
    {
    case PROP_APPLICATION_ID:
      g_return_if_fail (_g_application_validate_id (g_value_get_string (value)));
      app->priv->appid = g_value_dup_string (value);
      break;

    case PROP_DEFAULT_QUIT:
      app->priv->default_quit = g_value_get_boolean (value);
      break;

    case PROP_REGISTER:
      app->priv->do_register = g_value_get_boolean (value);
      break;

    case PROP_ARGV:
      app->priv->argv = g_value_dup_variant (value);
      break;

    case PROP_PLATFORM_DATA:
      {
	GVariant *platform_data = g_value_get_variant (value);
	if (app->priv->platform_data)
	  g_variant_unref (app->priv->platform_data);
	app->priv->platform_data = g_variant_ref_sink (append_cwd_to_platform_data (platform_data));
      }
      break;

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

static GObject*
g_application_constructor (GType                  type,
                           guint                  n_construct_properties,
                           GObjectConstructParam *construct_params)
{
  GApplication *app;
  GObject *object;
  const char *appid = NULL;
  guint i;

  for (i = 0; i < n_construct_properties; i++)
    {
      GObjectConstructParam *param = &construct_params[i];
      if (strcmp (param->pspec->name, "application-id") == 0)
        appid = g_value_get_string (param->value);
    }

  g_return_val_if_fail (appid != NULL, NULL);

  app = application_for_appid (appid);
  if (app != NULL)
    return g_object_ref (app);

  object = (* G_OBJECT_CLASS (g_application_parent_class)->constructor) (type,
                                                                         n_construct_properties,
                                                                         construct_params);
  app = G_APPLICATION (object);

  if (primary_application == NULL)
    primary_application = app;
  g_hash_table_insert (instances_for_appid, g_strdup (appid), app);

  return object;
}

static void
g_application_finalize (GObject *object)
{
  GApplication *app = G_APPLICATION (object);

  g_free (app->priv->appid);
  if (app->priv->actions)
    g_hash_table_unref (app->priv->actions);
  if (app->priv->actions_changed_id)
    g_source_remove (app->priv->actions_changed_id);
  if (app->priv->mainloop)
    g_main_loop_unref (app->priv->mainloop);

#ifdef G_OS_UNIX
  g_free (app->priv->dbus_path);
  if (app->priv->session_bus)
    g_object_unref (app->priv->session_bus);
#endif

  G_OBJECT_CLASS (g_application_parent_class)->finalize (object);
}

static void
g_application_class_init (GApplicationClass *klass)
{
  GObjectClass *gobject_class G_GNUC_UNUSED = G_OBJECT_CLASS (klass);

  g_type_class_add_private (klass, sizeof (GApplicationPrivate));

  gobject_class->constructor = g_application_constructor;
  gobject_class->set_property = g_application_set_property;
  gobject_class->get_property = g_application_get_property;

  gobject_class->finalize = g_application_finalize;

  klass->run = g_application_default_run;
  klass->quit_with_data = g_application_default_quit_with_data;

  /**
   * GApplication::quit-with-data:
   * @application: the object on which the signal is emitted
   * @platform_data: Platform-specific data, or %NULL
   *
   * This signal is emitted when the Quit action is invoked on the
   * application.
   *
   * The default handler for this signal exits the mainloop of the
   * application.
   *
   * Returns: %TRUE if the signal has been handled, %FALSE to continue
   *   signal emission
   */
  application_signals[QUIT_WITH_DATA] =
    g_signal_new (g_intern_static_string ("quit-with-data"),
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GApplicationClass, quit_with_data),
                  g_signal_accumulator_true_handled, NULL,
                  _gio_marshal_BOOLEAN__VARIANT,
                  G_TYPE_BOOLEAN, 1,
                  G_TYPE_VARIANT);

  /**
   * GApplication::action-with-data:
   * @application: the object on which the signal is emitted
   * @name: The name of the activated action
   * @platform_data: Platform-specific data, or %NULL
   *
   * This signal is emitted when an action is activated. The action name
   * is passed as the first argument, but also as signal detail, so it
   * is possible to connect to this signal for individual actions.
   *
   * The signal is never emitted for disabled actions.
   */
  application_signals[ACTION_WITH_DATA] =
    g_signal_new (g_intern_static_string ("action-with-data"),
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED,
                  G_STRUCT_OFFSET (GApplicationClass, action_with_data),
                  NULL, NULL,
                  _gio_marshal_VOID__STRING_VARIANT,
                  G_TYPE_NONE, 2,
                  G_TYPE_STRING,
                  G_TYPE_VARIANT);

   /**
   * GApplication::prepare-activation:
   * @application: the object on which the signal is emitted
   * @arguments: A #GVariant with the signature "aay"
   * @platform_data: A #GVariant with the signature "a{sv}", or %NULL
   *
   * This signal is emitted when a non-primary process for a given
   * application is invoked while your application is running; for
   * example, when a file browser launches your program to open a
   * file.  The raw operating system arguments are passed in the
   * @arguments variant.  Additional platform-dependent data is
   * stored in @platform_data.
   */
  application_signals[PREPARE_ACTIVATION] =
    g_signal_new (g_intern_static_string ("prepare-activation"),
                  G_OBJECT_CLASS_TYPE (klass),
                  G_SIGNAL_RUN_LAST,
                  G_STRUCT_OFFSET (GApplicationClass, prepare_activation),
                  NULL, NULL,
                  _gio_marshal_VOID__VARIANT_VARIANT,
                  G_TYPE_NONE, 2,
                  G_TYPE_VARIANT,
                  G_TYPE_VARIANT);

  /**
   * GApplication:application-id:
   *
   * The unique identifier for this application.  See the documentation for
   * #GApplication for more information about this property.
   *
   */
  g_object_class_install_property (gobject_class,
                                   PROP_APPLICATION_ID,
                                   g_param_spec_string ("application-id",
                                                        P_("Application ID"),
                                                        P_("Identifier for this application"),
                                                        NULL,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT_ONLY |
                                                        G_PARAM_STATIC_STRINGS));

  /**
   * GApplication:argv:
   *
   * The argument vector given to this application.  It must be a #GVariant
   * with a type signature "aay".
   *
   */
  g_object_class_install_property (gobject_class,
                                   PROP_ARGV,
                                   g_param_spec_variant ("argv",
						        P_("Argument vector"),
						        P_("System argument vector with type signature aay"),
						        G_VARIANT_TYPE ("aay"),
                                                        NULL,
						        G_PARAM_READWRITE |
						        G_PARAM_CONSTRUCT_ONLY |
						        G_PARAM_STATIC_STRINGS));

  /**
   * GApplication:platform-data:
   *
   * Platform-specific data retrieved from the operating system
   * environment.  It must be a #GVariant with type signature "a{sv}".
   *
   */
  g_object_class_install_property (gobject_class,
                                   PROP_PLATFORM_DATA,
                                   g_param_spec_variant ("platform-data",
						         P_("Platform data"),
						         P_("Environmental data, must have type signature a{sv}"),
						         G_VARIANT_TYPE ("a{sv}"),
                                                         NULL,
						         G_PARAM_READWRITE |
						         G_PARAM_CONSTRUCT_ONLY |
						         G_PARAM_STATIC_STRINGS));

  /**
   * GApplication:default-quit:
   *
   * By default, if a different process is running this application, the
   * process will be exited.  Set this property to %FALSE to allow custom
   * interaction with the remote process.
   *
   */
  g_object_class_install_property (gobject_class,
                                   PROP_DEFAULT_QUIT,
                                   g_param_spec_boolean ("default-quit",
                                                         P_("Default Quit"),
                                                         P_("Exit the process by default"),
                                                         TRUE,
                                                         G_PARAM_READWRITE |
                                                         G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_STATIC_STRINGS));


  /**
   * GApplication:is-remote:
   *
   * This property is %TRUE if this application instance represents a proxy
   * to the instance of this application in another process.
   *
   */
  g_object_class_install_property (gobject_class,
                                   PROP_IS_REMOTE,
                                   g_param_spec_boolean ("is-remote",
                                                         P_("Is Remote"),
                                                         P_("Whether this application is a proxy for another process"),
                                                         TRUE,
                                                         G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));

  /**
   * GApplication:register:
   *
   * If this property is %FALSE, the application construction will not attempt
   * to ensure process uniqueness, and the application is guaranteed to be in the
   * remote state.  See GApplication:is-remote.
   */
  g_object_class_install_property (gobject_class,
                                   PROP_REGISTER,
                                   g_param_spec_boolean ("register",
                                                         P_("Register"),
                                                         P_("If false, do not "),
                                                         TRUE,
                                                         G_PARAM_READWRITE | 
							 G_PARAM_CONSTRUCT_ONLY |
							 G_PARAM_STATIC_STRINGS));
}