/*
 * Based on gdbus-example-server.c
 *
 * The "Test" method accepts a single numeric argument - the length of the
 * string to be returned.  One gets as big return message as is the number
 * passed. Sed is used to compress the string for printing.
 *
 * Compile with:
 *   cc gdbus-fd-error.c `pkg-config --cflags --libs glib-2.0 gio-unix-2.0` -o gdbus-fd-error
 * Execute with:
 *   ./gdbus-fd-error
 * In other terminal:
 *   dbus-send --print-reply --dest=org.example.FdErrorTest /org/example/FdErrorTest org.example.FdErrorTest.Test int32:$((1024*100)) |sed 's/#\{1024\}/./g'
 *   dbus-send --print-reply --dest=org.example.FdErrorTest /org/example/FdErrorTest org.example.FdErrorTest.Test int32:$((1024*200)) |sed 's/#\{1024\}/./g'
 *   dbus-send --print-reply --dest=org.example.FdErrorTest /org/example/FdErrorTest org.example.FdErrorTest.Test int32:$((1024*300)) |sed 's/#\{1024\}/./g'
 */

#include <gio/gio.h>
#include <stdlib.h>

#ifdef G_OS_UNIX
#include <gio/gunixfdlist.h>
/* For STDOUT_FILENO */
#include <unistd.h>
#endif

/* ---------------------------------------------------------------------------------------------------- */

static GDBusNodeInfo *introspection_data = NULL;

/* Introspection data for the service we are exporting */
static const gchar introspection_xml[] =
  "<node>"
  "  <interface name='org.example.FdErrorTest'>"
  "    <method name='Test'>"
  "      <arg type='i' name='len' direction='in'/>"
  "      <arg type='s' name='str' direction='out'/>"
  "    </method>"
  "  </interface>"
  "</node>";

/* ---------------------------------------------------------------------------------------------------- */

static void
handle_method_call (GDBusConnection       *connection,
                    const gchar           *sender,
                    const gchar           *object_path,
                    const gchar           *interface_name,
                    const gchar           *method_name,
                    GVariant              *parameters,
                    GDBusMethodInvocation *invocation,
                    gpointer               user_data)
{
  if (g_strcmp0 (method_name, "Test") == 0)
    {
      GDBusMessage *reply;
      GUnixFDList *fd_list;
      gchar *long_string;
      GError *error;
      gint param_len;

      g_variant_get (parameters, "(i)", &param_len);

      fd_list = g_unix_fd_list_new ();
      error = NULL;
      g_unix_fd_list_append (fd_list, STDOUT_FILENO, &error);
      g_assert_no_error (error);

      long_string = g_strnfill (param_len, '#');

      reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation));
      g_dbus_message_set_unix_fd_list (reply, fd_list);
      g_dbus_message_set_body (reply, g_variant_new ("(s)", long_string));

      error = NULL;
      g_dbus_connection_send_message (connection,
                                      reply,
                                      G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                      NULL,
                                      &error);
      g_assert_no_error (error);

      g_free (long_string);
      g_object_unref (reply);
      g_object_unref (fd_list);
      g_object_unref (invocation);
    }
}

static GVariant *
handle_get_property (GDBusConnection  *connection,
                     const gchar      *sender,
                     const gchar      *object_path,
                     const gchar      *interface_name,
                     const gchar      *property_name,
                     GError          **error,
                     gpointer          user_data)
{
  return NULL;
}

static gboolean
handle_set_property (GDBusConnection  *connection,
                     const gchar      *sender,
                     const gchar      *object_path,
                     const gchar      *interface_name,
                     const gchar      *property_name,
                     GVariant         *value,
                     GError          **error,
                     gpointer          user_data)
{
  return FALSE;
}


/* for now */
static const GDBusInterfaceVTable interface_vtable =
{
  handle_method_call,
  handle_get_property,
  handle_set_property
};

/* ---------------------------------------------------------------------------------------------------- */

static void
on_bus_acquired (GDBusConnection *connection,
                 const gchar     *name,
                 gpointer         user_data)
{
  guint registration_id;

  g_object_set (connection,
                "exit-on-close", TRUE,
                NULL);

  registration_id = g_dbus_connection_register_object (connection,
                                                       "/org/example/FdErrorTest",
                                                       introspection_data->interfaces[0],
                                                       &interface_vtable,
                                                       NULL,  /* user_data */
                                                       NULL,  /* user_data_free_func */
                                                       NULL); /* GError** */
  g_assert (registration_id > 0);
}

int
main (int argc, char *argv[])
{
  guint owner_id;
  GMainLoop *loop;

  if (!GLIB_CHECK_VERSION (2, 36, 0))
    g_type_init ();

  introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
  g_assert (introspection_data != NULL);

  owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
                             "org.example.FdErrorTest",
                             G_BUS_NAME_OWNER_FLAGS_NONE,
                             on_bus_acquired,
                             NULL,  /* name_acquired_handler */
                             NULL,  /* name_lost_handler */
                             NULL,  /* user_data */
                             NULL); /* user_data_free_func */

  loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (loop);

  g_bus_unown_name (owner_id);

  g_dbus_node_info_unref (introspection_data);

  return 0;
}
