/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
 *
 * Copyright (C) 2004-2006 William Jon McCann <mccann@jhu.edu>
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA
 * 02110-1335, USA.
 *
 * Authors: William Jon McCann <mccann@jhu.edu>
 *
 */

#include "config.h"

#include <stdlib.h>
#include <locale.h>

#include <glib.h>
#include <gio/gio.h>
#include <glib/gi18n.h>

#include "bus.h"

static gboolean do_quit       = FALSE;
static gboolean do_lock       = FALSE;
static gboolean do_activate   = FALSE;
static gboolean do_deactivate = FALSE;
static gboolean do_version    = FALSE;

static gboolean do_query      = FALSE;
static gboolean do_time       = FALSE;

static gchar   *away_message  = "";

static GOptionEntry entries [] = {
        { "exit", 0, 0, G_OPTION_ARG_NONE, &do_quit,
          N_("Causes the screensaver to exit gracefully"), NULL },
        { "query", 'q', 0, G_OPTION_ARG_NONE, &do_query,
          N_("Query the state of the screensaver"), NULL },
        { "time", 't', 0, G_OPTION_ARG_NONE, &do_time,
          N_("Query the length of time the screensaver has been active"), NULL },
        { "lock", 'l', 0, G_OPTION_ARG_NONE, &do_lock,
          N_("Tells the running screensaver process to lock the screen immediately"), NULL },
        { "activate", 'a', 0, G_OPTION_ARG_NONE, &do_activate,
          N_("Turn the screensaver on (blank the screen)"), NULL },
        { "deactivate", 'd', 0, G_OPTION_ARG_NONE, &do_deactivate,
          N_("If the screensaver is active then deactivate it (un-blank the screen)"), NULL },
        { "version", 'V', 0, G_OPTION_ARG_NONE, &do_version,
          N_("Version of this application"), NULL },
        { "away-message", 'm', 0, G_OPTION_ARG_STRING, &away_message,
          N_("Message to be displayed in lock screen"), NULL},
        { NULL }
};

static GMainLoop *loop = NULL;

static GDBusMessage *
screensaver_send_message_bool (GDBusConnection *connection,
                               const char      *name,
                               gboolean         value)
{
        GDBusMessage *message, *reply;
        GError       *error;

        g_return_val_if_fail (connection != NULL, NULL);
        g_return_val_if_fail (name != NULL, NULL);

        message = g_dbus_message_new_method_call (GS_SERVICE,
                                                  GS_PATH,
                                                  GS_INTERFACE,
                                                  name);
        if (message == NULL) {
                g_warning ("Couldn't allocate the dbus message");
                return NULL;
        }

        g_dbus_message_set_body (message, g_variant_new ("(b)", value));

        error = NULL;
        reply = g_dbus_connection_send_message_with_reply_sync (connection,
                                                                message,
                                                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                                                -1,
                                                                NULL,
                                                                NULL,
                                                                &error);
        if (error != NULL) {
                g_warning ("unable to send message: %s", error->message);
                g_clear_error (&error);
        }

        g_dbus_connection_flush_sync (connection, NULL, &error);
        if (error != NULL) {
                g_warning ("unable to flush message queue: %s", error->message);
                g_clear_error (&error);
        }

        g_object_unref (message);

        return reply;
}

static GDBusMessage *
screensaver_send_message_string (GDBusConnection *connection,
                                 const char      *name,
                                 gchar           *value)
{
        GDBusMessage *message, *reply;
        GError       *error;

        g_return_val_if_fail (connection != NULL, NULL);
        g_return_val_if_fail (name != NULL, NULL);

        message = g_dbus_message_new_method_call (GS_SERVICE,
                                                  GS_PATH,
                                                  GS_INTERFACE,
                                                  name);
        if (message == NULL) {
                g_warning ("Couldn't allocate the dbus message");
                return NULL;
        }

        g_dbus_message_set_body (message, g_variant_new ("(s)", value));

        error = NULL;
        reply = g_dbus_connection_send_message_with_reply_sync (connection,
                                                                message,
                                                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                                                -1,
                                                                NULL,
                                                                NULL,
                                                                &error);
        if (error != NULL) {
                g_warning ("unable to send message: %s", error->message);
                g_clear_error (&error);
        }

        g_dbus_connection_flush_sync (connection, NULL, &error);
        if (error != NULL) {
                g_warning ("unable to flush message queue: %s", error->message);
                g_clear_error (&error);
        }

        g_object_unref (message);

        return reply;
}

static GDBusMessage *
screensaver_send_message_void (GDBusConnection *connection,
                               const char      *name,
                               gboolean         expect_reply)
{
        GDBusMessage *message, *reply;
        GError       *error;

        g_return_val_if_fail (connection != NULL, NULL);
        g_return_val_if_fail (name != NULL, NULL);

        message = g_dbus_message_new_method_call (GS_SERVICE,
                                                  GS_PATH,
                                                  GS_INTERFACE,
                                                  name);
        if (message == NULL) {
                g_warning ("Couldn't allocate the dbus message");
                return NULL;
        }

        error = NULL;

        if (! expect_reply) {
                reply = NULL;

                g_dbus_connection_send_message (connection,
                                                message,
                                                G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                                NULL,
                                                &error);

        } else {
                reply = g_dbus_connection_send_message_with_reply_sync (connection,
                                                                        message,
                                                                        G_DBUS_SEND_MESSAGE_FLAGS_NONE,
                                                                        -1,
                                                                        NULL,
                                                                        NULL,
                                                                        &error);
        }

        if (error != NULL) {
                g_warning ("unable to send message: %s", error->message);
                g_clear_error (&error);
        }

        g_dbus_connection_flush_sync (connection, NULL, &error);
        if (error != NULL) {
                g_warning ("unable to flush message queue: %s", error->message);
                g_clear_error (&error);
        }

        g_object_unref (message);

        return reply;
}

static gboolean
screensaver_is_running (GDBusConnection *connection)
{
        GVariant *reply;
        gboolean exists = FALSE;

        g_return_val_if_fail (connection != NULL, FALSE);

        reply = g_dbus_connection_call_sync (connection,
                                             DBUS_SERVICE,
                                             DBUS_PATH,
                                             DBUS_INTERFACE,
                                             "GetNameOwner",
                                             g_variant_new ("(s)", GS_SERVICE),
                                             NULL,
                                             G_DBUS_CALL_FLAGS_NO_AUTO_START,
                                             -1,
                                             NULL,
                                             NULL);
        if (reply != NULL) {
                exists = TRUE;
                g_variant_unref (reply);
        }

        return exists;
}

static gboolean
do_command (GDBusConnection *connection)
{
        GDBusMessage *reply;

        if (do_quit) {
                reply = screensaver_send_message_void (connection, "Quit", FALSE);
                goto done;
        }

        if (do_query) {
                GVariant     *body;
                gboolean      v;

                if (! screensaver_is_running (connection)) {
                        g_message ("Screensaver is not running!");
                        goto done;
                }

                reply = screensaver_send_message_void (connection, "GetActive", TRUE);
                if (reply == NULL) {
                        g_message ("Did not receive a reply from the screensaver.");
                        goto done;
                }

                body = g_dbus_message_get_body (reply);
                g_variant_get (body, "(b)", &v);
                g_object_unref (reply);

                if (v)  {
                        g_print (_("The screensaver is active\n"));
                } else {
                        g_print (_("The screensaver is inactive\n"));
                }
        }

        if (do_time) {
                GVariant *body;
                gboolean  v;
                gint32    t;

                reply = screensaver_send_message_void (connection, "GetActive", TRUE);
                if (reply == NULL) {
                        g_message ("Did not receive a reply from the screensaver.");
                        goto done;
                }

                body = g_dbus_message_get_body (reply);
                g_variant_get (body, "(b)", &v);
                g_object_unref (reply);

                if (v) {
                        reply = screensaver_send_message_void (connection, "GetActiveTime", TRUE);
                        if (reply == NULL) {
                                g_message ("Did not receive a reply from the screensaver.");
                                goto done;
                        }

                        body = g_dbus_message_get_body (reply);
                        g_variant_get (body, "(u)", &t);
                        g_object_unref (reply);

                        g_print (ngettext ("The screensaver has been active for %d second.\n", "The screensaver has been active for %d seconds.\n", t), t);
                } else {
                        g_print (_("The screensaver is not currently active.\n"));
                }
        }

        if (do_lock) {
                reply = screensaver_send_message_string (connection, "Lock", away_message);
                if (reply == NULL) {
                        g_message ("Did not receive a reply from the screensaver.");
                        goto done;
                }
                g_object_unref (reply);
        }

        if (do_activate) {
                reply = screensaver_send_message_bool (connection, "SetActive", TRUE);
                if (reply == NULL) {
                        g_message ("Did not receive a reply from the screensaver.");
                        goto done;
                }
                g_object_unref (reply);
        }

        if (do_deactivate) {
                reply = screensaver_send_message_bool (connection, "SetActive", FALSE);
                if (reply == NULL) {
                        g_message ("Did not receive a reply from the screensaver.");
                        goto done;
                }
                g_object_unref (reply);
        }

 done:
        g_main_loop_quit (loop);
        return FALSE;
}

int
main (int    argc,
      char **argv)
{
        GDBusConnection *connection;
        GOptionContext  *context;
        gboolean         retval;
        GError          *error;

#ifdef ENABLE_NLS
        bindtextdomain (GETTEXT_PACKAGE, "/usr/share/locale");
# ifdef HAVE_BIND_TEXTDOMAIN_CODESET
        bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
# endif
        textdomain (GETTEXT_PACKAGE);
#endif

        g_set_prgname (argv[0]);

        if (setlocale (LC_ALL, "") == NULL) {
                g_warning ("Locale not understood by C library, internationalization will not work\n");
        }

        error = NULL;
        context = g_option_context_new (NULL);
        g_option_context_add_main_entries (context, entries, NULL);
        retval = g_option_context_parse (context, &argc, &argv, &error);

        g_option_context_free (context);

        if (! retval) {
                g_warning ("%s", error->message);
                g_error_free (error);
                return EXIT_FAILURE;
        }

        if (do_version) {
                g_print ("%s %s\n", argv [0], VERSION);
                return EXIT_SUCCESS;
        }

        connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
        if (connection == NULL) {
                g_message ("Failed to get session bus: %s", error->message);
                g_error_free (error);
                return EXIT_FAILURE;
        }

        g_idle_add ((GSourceFunc) do_command, connection);

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

        g_object_unref (connection);

        return EXIT_SUCCESS;
}

