/*
 * * Copyright (C) 2009-2011 Ali <aliov@xfce.org>
 * * Copyright (C) 2019 Kacper Piwiński
 *
 * Licensed under the GNU General Public License Version 2
 *
 * 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, Fifth Floor, Boston, MA  02110-1301  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif

#include <upower.h>
#include <gdk/gdkx.h>

#include <libxfce4util/libxfce4util.h>
#include <libxfce4ui/libxfce4ui.h>

#include "xfpm-power.h"
#include "xfpm-dbus.h"
#include "xfpm-dpms.h"
#include "xfpm-battery.h"
#include "xfpm-xfconf.h"
#include "xfpm-notify.h"
#include "xfpm-errors.h"
#include "xfpm-console-kit.h"
#include "xfpm-inhibit.h"
#include "xfpm-polkit.h"
#include "xfpm-network-manager.h"
#include "xfpm-icons.h"
#include "xfpm-common.h"
#include "xfpm-power-common.h"
#include "xfpm-config.h"
#include "xfpm-debug.h"
#include "xfpm-enum-types.h"
#include "egg-idletime.h"
#include "xfpm-systemd.h"
#include "xfpm-suspend.h"
#include "xfpm-brightness.h"
#include "xfce-screensaver.h"

static void xfpm_power_finalize     (GObject *object);

static void xfpm_power_get_property (GObject *object,
                                     guint prop_id,
                                     GValue *value,
                                     GParamSpec *pspec);

static void xfpm_power_set_property (GObject *object,
                                     guint prop_id,
                                     const GValue *value,
                                     GParamSpec *pspec);

static void xfpm_power_change_presentation_mode (XfpmPower *power,
                                                 gboolean presentation_mode);

static void xfpm_update_blank_time (XfpmPower *power);

static void xfpm_power_dbus_class_init (XfpmPowerClass * klass);
static void xfpm_power_dbus_init (XfpmPower *power);

struct XfpmPowerPrivate
{
  GDBusConnection  *bus;

  UpClient         *upower;

  GHashTable       *hash;


  XfpmSystemd      *systemd;
  XfpmConsoleKit   *console;
  XfpmInhibit      *inhibit;
  XfpmXfconf       *conf;

  XfpmBatteryCharge overall_state;
  gboolean          critical_action_done;

  XfpmDpms         *dpms;
  gboolean          presentation_mode;
  gint              on_ac_blank;
  gint              on_battery_blank;
  EggIdletime      *idletime;

  gboolean          inhibited;
  gboolean          screensaver_inhibited;
  XfceScreenSaver  *screensaver;

  XfpmNotify       *notify;
#ifdef ENABLE_POLKIT
  XfpmPolkit       *polkit;
#endif
  gboolean          auth_suspend;
  gboolean          auth_hibernate;

  /* Properties */
  gboolean          on_low_battery;
  gboolean          lid_is_present;
  gboolean          lid_is_closed;
  gboolean          on_battery;
  gchar            *daemon_version;
  gboolean          can_suspend;
  gboolean          can_hibernate;

  /**
   * Warning dialog to use when notification daemon
   * doesn't support actions.
   **/
  GtkWidget        *dialog;
};

enum
{
  PROP_0,
  PROP_ON_LOW_BATTERY,
  PROP_ON_BATTERY,
  PROP_AUTH_SUSPEND,
  PROP_AUTH_HIBERNATE,
  PROP_CAN_SUSPEND,
  PROP_CAN_HIBERNATE,
  PROP_HAS_LID,
  PROP_PRESENTATION_MODE,
  PROP_ON_AC_BLANK,
  PROP_ON_BATTERY_BLANK,
  N_PROPERTIES
};

enum
{
  ON_BATTERY_CHANGED,
  LOW_BATTERY_CHANGED,
  LID_CHANGED,
  WAKING_UP,
  SLEEPING,
  ASK_SHUTDOWN,
  SHUTDOWN,
  LAST_SIGNAL
};

static guint signals [LAST_SIGNAL] = { 0 };


G_DEFINE_TYPE_WITH_PRIVATE (XfpmPower, xfpm_power, G_TYPE_OBJECT)


/* This checks if consolekit returns TRUE for either suspend or
 * hibernate showing support. This means that ConsoleKit2 is running
 * (and the system is capable of those actions).
 */
static gboolean
check_for_consolekit2 (XfpmPower *power)
{
  XfpmConsoleKit *console;
  gboolean can_suspend, can_hibernate;

  g_return_val_if_fail (XFPM_IS_POWER (power), FALSE);

  if (power->priv->console == NULL)
    return FALSE;

  console = power->priv->console;

  g_object_get (G_OBJECT (console),
                "can-suspend", &can_suspend,
                NULL);
  g_object_get (G_OBJECT (console),
                "can-hibernate", &can_hibernate,
                NULL);

    /* ConsoleKit2 supports suspend and hibernate */
  if (can_suspend || can_hibernate)
  {
    return TRUE;
  }

  return FALSE;
}

#ifdef ENABLE_POLKIT
static void
xfpm_power_check_polkit_auth (XfpmPower *power)
{
  const char *suspend = NULL, *hibernate = NULL;
  if (LOGIND_RUNNING())
  {
    XFPM_DEBUG ("using logind suspend backend");
    suspend   = POLKIT_AUTH_SUSPEND_LOGIND;
    hibernate = POLKIT_AUTH_HIBERNATE_LOGIND;
  }
  else
  {
    if (power->priv->console != NULL)
    {
      /* ConsoleKit2 supports suspend and hibernate */
      if (check_for_consolekit2 (power))
      {
        XFPM_DEBUG ("using consolekit2 suspend backend");
        suspend   = POLKIT_AUTH_SUSPEND_CONSOLEKIT2;
        hibernate = POLKIT_AUTH_HIBERNATE_CONSOLEKIT2;
      }
      else
      {
        XFPM_DEBUG ("using xfpm internal suspend backend");
        suspend   = POLKIT_AUTH_SUSPEND_XFPM;
        hibernate = POLKIT_AUTH_HIBERNATE_XFPM;
      }
    }
  }
  power->priv->auth_suspend = xfpm_polkit_check_auth (power->priv->polkit,
                                                      suspend);

  power->priv->auth_hibernate = xfpm_polkit_check_auth (power->priv->polkit,
                                                        hibernate);
}
#endif

static void
xfpm_power_check_power (XfpmPower *power, gboolean on_battery)
{
  if (on_battery != power->priv->on_battery )
  {
    GList *list;
    guint len, i;
    g_signal_emit (G_OBJECT (power), signals [ON_BATTERY_CHANGED], 0, on_battery);

    xfpm_dpms_set_on_battery (power->priv->dpms, on_battery);

      /* Dismiss critical notifications on battery state changes */
    xfpm_notify_close_critical (power->priv->notify);

    power->priv->on_battery = on_battery;
    list = g_hash_table_get_values (power->priv->hash);
    len = g_list_length (list);
    for ( i = 0; i < len; i++)
    {
      g_object_set (G_OBJECT (g_list_nth_data (list, i)),
                    "ac-online", !on_battery,
                    NULL);
      xfpm_update_blank_time (power);
    }
  }
}

static void
xfpm_power_check_lid (XfpmPower *power, gboolean present, gboolean closed)
{
  power->priv->lid_is_present = present;

  if (power->priv->lid_is_present)
  {
    if (closed != power->priv->lid_is_closed )
    {
      power->priv->lid_is_closed = closed;
      g_signal_emit (G_OBJECT (power), signals [LID_CHANGED], 0, power->priv->lid_is_closed);
    }
  }
}

/*
 * Get the properties on org.freedesktop.DeviceKit.Power
 *
 * DaemonVersion      's'
 * CanSuspend'        'b'
 * CanHibernate'      'b'
 * OnBattery'         'b'
 * OnLowBattery'      'b'
 * LidIsClosed'       'b'
 * LidIsPresent'      'b'
 */
static void
xfpm_power_get_properties (XfpmPower *power)
{
  gboolean on_battery;
  gboolean lid_is_closed;
  gboolean lid_is_present;

  if ( LOGIND_RUNNING () )
  {
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-suspend", &power->priv->can_suspend,
                  NULL);
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-hibernate", &power->priv->can_hibernate,
                  NULL);
  }
  else
  {
    if (check_for_consolekit2 (power))
    {
      g_object_get (G_OBJECT (power->priv->console),
        "can-suspend", &power->priv->can_suspend,
        NULL);
      g_object_get (G_OBJECT (power->priv->console),
        "can-hibernate", &power->priv->can_hibernate,
        NULL);
      }
    else
    {
      power->priv->can_suspend   = xfpm_suspend_can_suspend ();
      power->priv->can_hibernate = xfpm_suspend_can_hibernate ();
    }
  }

  g_object_get (power->priv->upower,
                "on-battery", &on_battery,
                "lid-is-closed", &lid_is_closed,
                "lid-is-present", &lid_is_present,
                NULL);
  xfpm_power_check_lid (power, lid_is_present, lid_is_closed);
  xfpm_power_check_power (power, on_battery);
}

static void
xfpm_power_report_error (XfpmPower *power, const gchar *error, const gchar *icon_name)
{
  GtkStatusIcon *battery = NULL;
  guint i, len;
  GList *list;

  list = g_hash_table_get_values (power->priv->hash);
  len = g_list_length (list);

  for ( i = 0; i < len; i++)
  {
    UpDeviceKind type;
    battery = g_list_nth_data (list, i);
    type = xfpm_battery_get_device_type (XFPM_BATTERY (battery));
    if ( type == UP_DEVICE_KIND_BATTERY ||
         type == UP_DEVICE_KIND_UPS )
      break;
  }

  xfpm_notify_show_notification (power->priv->notify,
                                 _("Power Manager"),
                                 error,
                                 icon_name,
                                 XFPM_NOTIFY_CRITICAL);
}

static void
xfpm_power_sleep (XfpmPower *power, const gchar *sleep_time, gboolean force)
{
  GError *error = NULL;
  gboolean lock_screen;
#ifdef WITH_NETWORK_MANAGER
  gboolean network_manager_sleep;
#endif
  XfpmBrightness *brightness;
  gint32 brightness_level;

  if ( power->priv->inhibited && force == FALSE)
  {
    GtkWidget *dialog;
    gboolean ret;

    dialog = gtk_message_dialog_new (NULL,
                                     GTK_DIALOG_MODAL,
                                     GTK_MESSAGE_QUESTION,
                                     GTK_BUTTONS_YES_NO,
                                     _("An application is currently disabling the automatic sleep. "
                                       "Doing this action now may damage the working state of this application.\n"
                                       "Are you sure you want to hibernate the system?"));
  ret = gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);

  if ( !ret || ret == GTK_RESPONSE_NO)
    return;
  }

  g_signal_emit (G_OBJECT (power), signals [SLEEPING], 0);
    /* Get the current brightness level so we can use it after we suspend */
  brightness = xfpm_brightness_new();
  xfpm_brightness_setup (brightness);
  xfpm_brightness_get_level (brightness, &brightness_level);

#ifdef WITH_NETWORK_MANAGER
  g_object_get (G_OBJECT (power->priv->conf),
                NETWORK_MANAGER_SLEEP, &network_manager_sleep,
                NULL);

  if ( network_manager_sleep )
  {
    xfpm_network_manager_sleep (TRUE);
  }
#endif

  g_object_get (G_OBJECT (power->priv->conf),
                LOCK_SCREEN_ON_SLEEP, &lock_screen,
                NULL);

  if ( lock_screen )
  {
#ifdef WITH_NETWORK_MANAGER
    if ( network_manager_sleep )
    {
  /* 2 seconds, to give network manager time to sleep */
      g_usleep (2000000);
    }
#endif
    if (!xfce_screensaver_lock (power->priv->screensaver))
    {
      GtkWidget *dialog;
      gboolean ret;

      dialog = gtk_message_dialog_new (NULL,
                                       GTK_DIALOG_MODAL,
                                       GTK_MESSAGE_QUESTION,
                                       GTK_BUTTONS_YES_NO,
                                       _("None of the screen lock tools ran "
                                         "successfully, the screen will not "
                                         "be locked.\n"
                                         "Do you still want to continue to "
                                         "suspend the system?"));
      ret = gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);

      if ( !ret || ret == GTK_RESPONSE_NO)
        return;
      }
    }

    /* This is fun, here's the order of operations:
     * - if the Logind is running then use it
     * - if UPower < 0.99.0 then use it (don't make changes on the user unless forced)
     * - if ConsoleKit2 is running then use it
     * - if everything else fails use our built-in fallback
     */
  if ( LOGIND_RUNNING () )
  {
    xfpm_systemd_sleep (power->priv->systemd, sleep_time, &error);
  }
  else
  {
    if (!g_strcmp0 (sleep_time, "Hibernate"))
    {
      if (check_for_consolekit2 (power))
      {
        xfpm_console_kit_hibernate (power->priv->console, &error);
      }
      else
      {
        xfpm_suspend_try_action (XFPM_HIBERNATE);
      }
    }
    else
    {
      if (check_for_consolekit2 (power))
      {
        xfpm_console_kit_suspend (power->priv->console, &error);
      }
      else
      {
        xfpm_suspend_try_action (XFPM_SUSPEND);
      }
    }
  }

  if ( error )
  {
    if ( g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NO_REPLY) )
    {
      XFPM_DEBUG ("D-Bus time out, but should be harmless");
    }
    else
    {
      xfpm_power_report_error (power, error->message, "dialog-error");
      g_error_free (error);
    }
  }

  g_signal_emit (G_OBJECT (power), signals [WAKING_UP], 0);
    /* Check/update any changes while we slept */
  xfpm_power_get_properties (power);
    /* Restore the brightness level from before we suspended */
  xfpm_brightness_set_level (brightness, brightness_level);

#ifdef WITH_NETWORK_MANAGER
  if ( network_manager_sleep )
  {
    xfpm_network_manager_sleep (FALSE);
  }
#endif
}

static void
xfpm_power_hibernate_clicked (XfpmPower *power)
{
  gtk_widget_destroy (power->priv->dialog );
  power->priv->dialog = NULL;
  xfpm_power_sleep (power, "Hibernate", TRUE);
}

static void
xfpm_power_suspend_clicked (XfpmPower *power)
{
  gtk_widget_destroy (power->priv->dialog );
  power->priv->dialog = NULL;
  xfpm_power_sleep (power, "Suspend", TRUE);
}

static void
xfpm_power_shutdown_clicked (XfpmPower *power)
{
  gtk_widget_destroy (power->priv->dialog );
  power->priv->dialog = NULL;
  g_signal_emit (G_OBJECT (power), signals [SHUTDOWN], 0);
}

static XfpmBatteryCharge
xfpm_power_get_current_charge_state (XfpmPower *power)
{
  GList *list;
  guint len, i;
  gboolean power_supply;
  XfpmBatteryCharge max_charge_status = XFPM_BATTERY_CHARGE_UNKNOWN;

  list = g_hash_table_get_values (power->priv->hash);
  len = g_list_length (list);

  for ( i = 0; i < len; i++ )
  {
    XfpmBatteryCharge battery_charge;
    UpDeviceKind type;

    g_object_get (G_OBJECT (g_list_nth_data (list, i)),
                    "charge-status", &battery_charge,
                    "device-type", &type,
                    "ac-online", &power_supply,
                    NULL);
    if ( type != UP_DEVICE_KIND_BATTERY &&
         type != UP_DEVICE_KIND_UPS &&
         power_supply != TRUE)
      continue;

    max_charge_status = MAX (max_charge_status, battery_charge);
  }

  return max_charge_status;
}

static void
xfpm_power_notify_action_callback (NotifyNotification *n, gchar *action, XfpmPower *power)
{
  if ( !g_strcmp0 (action, "Shutdown") )
    g_signal_emit (G_OBJECT (power), signals [SHUTDOWN], 0);
  else
    xfpm_power_sleep (power, action, TRUE);
}

static void
xfpm_power_add_actions_to_notification (XfpmPower *power, NotifyNotification *n)
{
  gboolean can_shutdown;

  if ( LOGIND_RUNNING () )
  {
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-shutdown", &can_shutdown,
                  NULL);
  }
  else
  {
    g_object_get (G_OBJECT (power->priv->console),
                  "can-shutdown", &can_shutdown,
                  NULL);
  }

  if ( power->priv->can_hibernate && power->priv->auth_hibernate )
  {
    xfpm_notify_add_action_to_notification(
         power->priv->notify,
         n,
         "Hibernate",
         _("Hibernate the system"),
         (NotifyActionCallback)xfpm_power_notify_action_callback,
         power);
  }

  if ( power->priv->can_suspend && power->priv->auth_suspend )
  {
    xfpm_notify_add_action_to_notification(
         power->priv->notify,
         n,
         "Suspend",
         _("Suspend the system"),
         (NotifyActionCallback)xfpm_power_notify_action_callback,
         power);
  }

  if ( can_shutdown )
  xfpm_notify_add_action_to_notification (power->priv->notify,
                                          n,
                                          "Shutdown",
                                          _("Shutdown the system"),
                                          (NotifyActionCallback)xfpm_power_notify_action_callback,
                                          power);
}

static void
xfpm_power_show_critical_action_notification (XfpmPower *power, XfpmBattery *battery)
{
  const gchar *message;
  NotifyNotification *n;

  message = _("System is running on low power. "\
               "Save your work to avoid losing data");

  n =
  xfpm_notify_new_notification (power->priv->notify,
                                _("Power Manager"),
                                message,
                                xfpm_battery_get_icon_name (battery),
                                XFPM_NOTIFY_CRITICAL);

  xfpm_power_add_actions_to_notification (power, n);
  xfpm_notify_critical (power->priv->notify, n);

}

static void
xfpm_power_close_critical_dialog (XfpmPower *power)
{
  gtk_widget_destroy (power->priv->dialog);
  power->priv->dialog = NULL;
}

static void
xfpm_power_show_critical_action_gtk (XfpmPower *power)
{
  GtkWidget *dialog;
  GtkWidget *content_area;
  GtkWidget *cancel;
  const gchar *message;
  gboolean can_shutdown;

  if ( LOGIND_RUNNING () )
  {
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-shutdown", &can_shutdown,
                  NULL);
  }
  else
  {
    g_object_get (G_OBJECT (power->priv->console),
                  "can-shutdown", &can_shutdown,
                  NULL);
  }

  message = _("System is running on low power. "\
              "Save your work to avoid losing data");

  dialog = gtk_dialog_new_with_buttons (_("Power Manager"), NULL, GTK_DIALOG_MODAL,
                                        NULL, NULL);

  gtk_dialog_set_default_response (GTK_DIALOG (dialog),
                                   GTK_RESPONSE_CANCEL);

  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));

  gtk_box_pack_start (GTK_BOX (content_area), gtk_label_new (message),
                      TRUE, TRUE, 8);

  if ( power->priv->can_hibernate && power->priv->auth_hibernate )
  {
    GtkWidget *hibernate;
    hibernate = gtk_button_new_with_label (_("Hibernate"));
    gtk_dialog_add_action_widget (GTK_DIALOG (dialog), hibernate, GTK_RESPONSE_NONE);

    g_signal_connect_swapped (hibernate, "clicked",
                              G_CALLBACK (xfpm_power_hibernate_clicked), power);
  }

  if ( power->priv->can_suspend && power->priv->auth_suspend )
  {
    GtkWidget *suspend;

    suspend = gtk_button_new_with_label (_("Suspend"));
    gtk_dialog_add_action_widget (GTK_DIALOG (dialog), suspend, GTK_RESPONSE_NONE);

    g_signal_connect_swapped (suspend, "clicked",
                              G_CALLBACK (xfpm_power_suspend_clicked), power);
    }

  if ( can_shutdown )
  {
    GtkWidget *shutdown;

    shutdown = gtk_button_new_with_label (_("Shutdown"));
    gtk_dialog_add_action_widget (GTK_DIALOG (dialog), shutdown, GTK_RESPONSE_NONE);

    g_signal_connect_swapped (shutdown, "clicked",
                              G_CALLBACK (xfpm_power_shutdown_clicked), power);
  }

  cancel = gtk_button_new_with_label (_("_Cancel"));
  gtk_dialog_add_action_widget (GTK_DIALOG (dialog), cancel, GTK_RESPONSE_NONE);

  g_signal_connect_swapped (cancel, "clicked",
                            G_CALLBACK (xfpm_power_close_critical_dialog), power);

  g_signal_connect_swapped (dialog, "destroy",
                            G_CALLBACK (xfpm_power_close_critical_dialog), power);
  if ( power->priv->dialog )
  {
    gtk_widget_destroy (power->priv->dialog);
    power->priv->dialog = NULL;
  }
  power->priv->dialog = dialog;
  gtk_widget_show_all (dialog);
}

static void
xfpm_power_show_critical_action (XfpmPower *power, XfpmBattery *battery)
{
  gboolean supports_actions;

  g_object_get (G_OBJECT (power->priv->notify),
                "actions", &supports_actions,
                NULL);

  if ( supports_actions )
    xfpm_power_show_critical_action_notification (power, battery);
  else
    xfpm_power_show_critical_action_gtk (power);
}

static void
xfpm_power_process_critical_action (XfpmPower *power, XfpmShutdownRequest req)
{
  if ( req == XFPM_ASK )
    g_signal_emit (G_OBJECT (power), signals [ASK_SHUTDOWN], 0);
  else if ( req == XFPM_DO_SUSPEND )
    xfpm_power_sleep (power, "Suspend", TRUE);
  else if ( req == XFPM_DO_HIBERNATE )
    xfpm_power_sleep (power, "Hibernate", TRUE);
  else if ( req == XFPM_DO_SHUTDOWN )
    g_signal_emit (G_OBJECT (power), signals [SHUTDOWN], 0);
}

static void
xfpm_power_system_on_critical_power (XfpmPower *power, XfpmBattery *battery)
{
  XfpmShutdownRequest critical_action;

  g_object_get (G_OBJECT (power->priv->conf),
                CRITICAL_BATT_ACTION_CFG, &critical_action,
                NULL);

  XFPM_DEBUG ("System is running on low power");
  XFPM_DEBUG_ENUM (critical_action, XFPM_TYPE_SHUTDOWN_REQUEST, "Critical battery action");

  if ( critical_action == XFPM_DO_NOTHING )
  {
    xfpm_power_show_critical_action (power, battery);
  }
  else
  {
    if (power->priv->critical_action_done == FALSE)
    {
      power->priv->critical_action_done = TRUE;
      xfpm_power_process_critical_action (power, critical_action);
    }
    else
    {
      xfpm_power_show_critical_action (power, battery);
    }
  }
}

static void
xfpm_power_battery_charge_changed_cb (XfpmBattery *battery, XfpmPower *power)
{
  gboolean notify;
  XfpmBatteryCharge battery_charge;
  XfpmBatteryCharge current_charge;

  battery_charge = xfpm_battery_get_charge (battery);
  current_charge = xfpm_power_get_current_charge_state (power);

  XFPM_DEBUG_ENUM (current_charge, XFPM_TYPE_BATTERY_CHARGE, "Current system charge status");

  if (current_charge == power->priv->overall_state)
    return;

  if (current_charge >= XFPM_BATTERY_CHARGE_LOW)
    power->priv->critical_action_done = FALSE;

  power->priv->overall_state = current_charge;

  if ( current_charge == XFPM_BATTERY_CHARGE_CRITICAL && power->priv->on_battery)
  {
    xfpm_power_system_on_critical_power (power, battery);

    power->priv->on_low_battery = TRUE;
    g_signal_emit (G_OBJECT (power), signals [LOW_BATTERY_CHANGED], 0, power->priv->on_low_battery);
    return;
  }

  if ( power->priv->on_low_battery )
  {
    power->priv->on_low_battery = FALSE;
    g_signal_emit (G_OBJECT (power), signals [LOW_BATTERY_CHANGED], 0, power->priv->on_low_battery);
  }

  g_object_get (G_OBJECT (power->priv->conf),
                GENERAL_NOTIFICATION_CFG, &notify,
                NULL);

  if ( power->priv->on_battery )
  {
    if ( current_charge == XFPM_BATTERY_CHARGE_LOW )
    {
      if ( notify )
        xfpm_notify_show_notification (power->priv->notify,
                       _("Power Manager"),
                       _("System is running on low power"),
                       xfpm_battery_get_icon_name (battery),
                       XFPM_NOTIFY_NORMAL);

     }
    else if ( battery_charge == XFPM_BATTERY_CHARGE_LOW )
    {
      if ( notify )
      {
        gchar *msg;
        gchar *time_str;

        const gchar *battery_name = xfpm_battery_get_battery_name (battery);

        time_str = xfpm_battery_get_time_left (battery);

        msg = g_strdup_printf (_("Your %s charge level is low\nEstimated time left %s"), battery_name, time_str);


        xfpm_notify_show_notification (power->priv->notify,
                       _("Power Manager"),
                       msg,
                       xfpm_battery_get_icon_name (battery),
                       XFPM_NOTIFY_NORMAL);
        g_free (msg);
        g_free (time_str);
      }
    }
  }

    /*Current charge is okay now, then close the dialog*/
  if ( power->priv->dialog )
  {
    gtk_widget_destroy (power->priv->dialog);
    power->priv->dialog = NULL;
  }
}

static void
xfpm_power_add_device (UpDevice *device, XfpmPower *power)
{
  guint device_type = UP_DEVICE_KIND_UNKNOWN;
  const gchar *object_path = up_device_get_object_path(device);

    /* hack, this depends on XFPM_DEVICE_TYPE_* being in sync with UP_DEVICE_KIND_* */
  g_object_get (device,
                "kind", &device_type,
                NULL);

  XFPM_DEBUG ("'%s' device added", up_device_kind_to_string(device_type));

  if ( device_type == UP_DEVICE_KIND_BATTERY  ||
       device_type == UP_DEVICE_KIND_UPS  ||
       device_type == UP_DEVICE_KIND_MOUSE  ||
       device_type == UP_DEVICE_KIND_KEYBOARD ||
       device_type == UP_DEVICE_KIND_PHONE)
  {
    GtkWidget *battery;
    XFPM_DEBUG( "Battery device type '%s' detected at: %s",
                up_device_kind_to_string(device_type), object_path);
    battery = xfpm_battery_new ();

    xfpm_battery_monitor_device (XFPM_BATTERY (battery),
                                 object_path,
                                 device_type);
    g_hash_table_insert (power->priv->hash, g_strdup (object_path), battery);

    g_signal_connect (battery, "battery-charge-changed",
                      G_CALLBACK (xfpm_power_battery_charge_changed_cb), power);
  }
}

static void
xfpm_power_get_power_devices (XfpmPower *power)
{
  GPtrArray *array = NULL;
  guint i;

#if UP_CHECK_VERSION(0, 99, 8)
  array = up_client_get_devices2 (power->priv->upower);
#else
  array = up_client_get_devices (power->priv->upower);
#endif

  if ( array )
  {
    for ( i = 0; i < array->len; i++)
    {
      UpDevice *device = g_ptr_array_index (array, i);
      const gchar *object_path = up_device_get_object_path(device);
      XFPM_DEBUG ("Power device detected at : %s", object_path);
      xfpm_power_add_device (device, power);
    }
  g_ptr_array_free (array, TRUE);
  }
}

static void
xfpm_power_remove_device (XfpmPower *power, const gchar *object_path)
{
  g_hash_table_remove (power->priv->hash, object_path);
}

static void
xfpm_power_inhibit_changed_cb (XfpmInhibit *inhibit, gboolean is_inhibit, XfpmPower *power)
{
  if (power->priv->inhibited != is_inhibit)
  {
    power->priv->inhibited = is_inhibit;

    XFPM_DEBUG ("is_inhibit %s, screensaver_inhibited %s, presentation_mode %s",
                power->priv->inhibited ? "TRUE" : "FALSE",
                power->priv->screensaver_inhibited ? "TRUE" : "FALSE",
                power->priv->presentation_mode ? "TRUE" : "FALSE");

    /* If we are inhibited make sure we inhibit the screensaver too */
    if (is_inhibit)
    {
      if (!power->priv->screensaver_inhibited)
      {
        xfce_screensaver_inhibit (power->priv->screensaver, TRUE);
        power->priv->screensaver_inhibited = TRUE;
      }
    }
    else
    {
      /* Or make sure we remove the screensaver inhibit */
      if (power->priv->screensaver_inhibited && !power->priv->presentation_mode)
      {
        xfce_screensaver_inhibit (power->priv->screensaver, FALSE);
        power->priv->screensaver_inhibited = FALSE;
      }
    }
  }

  XFPM_DEBUG ("is_inhibit %s, screensaver_inhibited %s, presentation_mode %s",
  power->priv->inhibited ? "TRUE" : "FALSE",
  power->priv->screensaver_inhibited ? "TRUE" : "FALSE",
  power->priv->presentation_mode ? "TRUE" : "FALSE");
}

static void
xfpm_power_changed_cb (UpClient *upower,
                       GParamSpec *pspec,
                       XfpmPower *power)
{
  xfpm_power_get_properties (power);
}

static void
xfpm_power_device_added_cb (UpClient *upower, UpDevice *device, XfpmPower *power)
{
  xfpm_power_add_device (device, power);
}

static void
xfpm_power_device_removed_cb (UpClient *upower, const gchar *object_path, XfpmPower *power)
{
  xfpm_power_remove_device (power, object_path);
}

#ifdef ENABLE_POLKIT
static void
xfpm_power_polkit_auth_changed_cb (XfpmPower *power)
{
  XFPM_DEBUG ("Auth configuration changed");
  xfpm_power_check_polkit_auth (power);
}
#endif

static void
xfpm_power_class_init (XfpmPowerClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = xfpm_power_finalize;

  object_class->get_property = xfpm_power_get_property;
  object_class->set_property = xfpm_power_set_property;

  signals [ON_BATTERY_CHANGED] =
        g_signal_new ("on-battery-changed",
                      XFPM_TYPE_POWER,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(XfpmPowerClass, on_battery_changed),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__BOOLEAN,
                      G_TYPE_NONE, 1, G_TYPE_BOOLEAN);

  signals [LOW_BATTERY_CHANGED] =
        g_signal_new ("low-battery-changed",
                      XFPM_TYPE_POWER,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(XfpmPowerClass, low_battery_changed),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__BOOLEAN,
                      G_TYPE_NONE, 1, G_TYPE_BOOLEAN);

  signals [LID_CHANGED] =
        g_signal_new ("lid-changed",
                      XFPM_TYPE_POWER,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(XfpmPowerClass, lid_changed),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__BOOLEAN,
                      G_TYPE_NONE, 1, G_TYPE_BOOLEAN);

  signals [WAKING_UP] =
        g_signal_new ("waking-up",
                      XFPM_TYPE_POWER,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(XfpmPowerClass, waking_up),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__VOID,
                      G_TYPE_NONE, 0, G_TYPE_NONE);

  signals [SLEEPING] =
        g_signal_new ("sleeping",
                      XFPM_TYPE_POWER,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(XfpmPowerClass, sleeping),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__VOID,
                      G_TYPE_NONE, 0, G_TYPE_NONE);

  signals [ASK_SHUTDOWN] =
        g_signal_new ("ask-shutdown",
                      XFPM_TYPE_POWER,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(XfpmPowerClass, ask_shutdown),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__VOID,
                      G_TYPE_NONE, 0, G_TYPE_NONE);

  signals [SHUTDOWN] =
        g_signal_new ("shutdown",
                      XFPM_TYPE_POWER,
                      G_SIGNAL_RUN_LAST,
                      G_STRUCT_OFFSET(XfpmPowerClass, shutdown),
                      NULL, NULL,
                      g_cclosure_marshal_VOID__VOID,
                      G_TYPE_NONE, 0, G_TYPE_NONE);

#define XFPM_PARAM_FLAGS  (  G_PARAM_READWRITE \
                           | G_PARAM_CONSTRUCT \
                           | G_PARAM_STATIC_NAME \
                           | G_PARAM_STATIC_NICK \
                           | G_PARAM_STATIC_BLURB)

  g_object_class_install_property (object_class,
                                   PROP_ON_BATTERY,
                                   g_param_spec_boolean ("on-battery",
                                                         NULL, NULL,
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_ON_LOW_BATTERY,
                                   g_param_spec_boolean ("on-low-battery",
                                                         NULL, NULL,
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_AUTH_SUSPEND,
                                   g_param_spec_boolean ("auth-suspend",
                                                         NULL, NULL,
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_AUTH_HIBERNATE,
                                   g_param_spec_boolean ("auth-hibernate",
                                                         NULL, NULL,
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_CAN_HIBERNATE,
                                   g_param_spec_boolean ("can-hibernate",
                                                         NULL, NULL,
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_CAN_SUSPEND,
                                   g_param_spec_boolean ("can-suspend",
                                                         NULL, NULL,
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_HAS_LID,
                                   g_param_spec_boolean ("has-lid",
                                                         NULL, NULL,
                                                         FALSE,
                                                         G_PARAM_READABLE));

  g_object_class_install_property (object_class,
                                   PROP_PRESENTATION_MODE,
                                   g_param_spec_boolean (PRESENTATION_MODE,
                                                         NULL, NULL,
                                                         FALSE,
                                                         XFPM_PARAM_FLAGS));

  g_object_class_install_property (object_class,
                                   PROP_ON_AC_BLANK,
                                   g_param_spec_int (ON_AC_BLANK,
                                                    NULL, NULL,
                                                    0,
                                                    G_MAXINT16,
                                                    10,
                                                    XFPM_PARAM_FLAGS));

  g_object_class_install_property (object_class,
                                   PROP_ON_BATTERY_BLANK,
                                   g_param_spec_int (ON_BATTERY_BLANK,
                                                    NULL, NULL,
                                                    0,
                                                    G_MAXINT16,
                                                    10,
                                                    XFPM_PARAM_FLAGS));
#undef XFPM_PARAM_FLAGS

  xfpm_power_dbus_class_init (klass);
}

static void
xfpm_power_init (XfpmPower *power)
{
  GError *error = NULL;

  power->priv = xfpm_power_get_instance_private (power);

  power->priv->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
  power->priv->lid_is_present  = FALSE;
  power->priv->lid_is_closed   = FALSE;
  power->priv->on_battery      = FALSE;
  power->priv->on_low_battery  = FALSE;
  power->priv->daemon_version  = NULL;
  power->priv->can_suspend     = FALSE;
  power->priv->can_hibernate   = FALSE;
  power->priv->auth_hibernate  = TRUE;
  power->priv->auth_suspend    = TRUE;
  power->priv->dialog          = NULL;
  power->priv->overall_state   = XFPM_BATTERY_CHARGE_OK;
  power->priv->critical_action_done = FALSE;

  power->priv->dpms                 = xfpm_dpms_new ();

  power->priv->presentation_mode    = FALSE;
  power->priv->on_ac_blank          = 15;
  power->priv->on_battery_blank     = 10;

  power->priv->inhibit = xfpm_inhibit_new ();
  power->priv->notify  = xfpm_notify_new ();
  power->priv->conf    = xfpm_xfconf_new ();
  power->priv->upower  = up_client_new ();
  power->priv->screensaver = xfce_screensaver_new ();

  power->priv->systemd = NULL;
  power->priv->console = NULL;
  if ( LOGIND_RUNNING () )
    power->priv->systemd = xfpm_systemd_new ();
  else
    power->priv->console = xfpm_console_kit_new ();

#ifdef ENABLE_POLKIT
  power->priv->polkit  = xfpm_polkit_get ();
  g_signal_connect_swapped (power->priv->polkit, "auth-changed",
                            G_CALLBACK (xfpm_power_polkit_auth_changed_cb), power);
#endif

  g_signal_connect (power->priv->inhibit, "has-inhibit-changed",
                    G_CALLBACK (xfpm_power_inhibit_changed_cb), power);

  power->priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);

  if ( error )
  {
    g_critical ("Unable to connect to the system bus : %s", error->message);
    g_error_free (error);
    goto out;
  }

  g_signal_connect (power->priv->upower, "device-added", G_CALLBACK (xfpm_power_device_added_cb), power);
  g_signal_connect (power->priv->upower, "device-removed", G_CALLBACK (xfpm_power_device_removed_cb), power);
  g_signal_connect (power->priv->upower, "notify", G_CALLBACK (xfpm_power_changed_cb), power);

  xfpm_power_get_power_devices (power);
  xfpm_power_get_properties (power);
#ifdef ENABLE_POLKIT
  xfpm_power_check_polkit_auth (power);
#endif

out:
  xfpm_power_dbus_init (power);

  /*
   * Emit org.freedesktop.PowerManagement session signals on startup
   */
  g_signal_emit (G_OBJECT (power), signals [ON_BATTERY_CHANGED], 0, power->priv->on_battery);
}

static void
xfpm_power_get_property (GObject *object,
                         guint prop_id,
                         GValue *value,
                         GParamSpec *pspec)
{
  XfpmPower *power;
  power = XFPM_POWER (object);

  switch (prop_id)
  {
    case PROP_ON_BATTERY:
      g_value_set_boolean (value, power->priv->on_battery);
      break;
    case PROP_AUTH_HIBERNATE:
      g_value_set_boolean (value, power->priv->auth_hibernate);
      break;
    case PROP_AUTH_SUSPEND:
      g_value_set_boolean (value, power->priv->auth_suspend);
      break;
    case PROP_CAN_SUSPEND:
      g_value_set_boolean (value, power->priv->can_suspend);
      break;
    case PROP_CAN_HIBERNATE:
      g_value_set_boolean (value, power->priv->can_hibernate);
      break;
    case PROP_HAS_LID:
      g_value_set_boolean (value, power->priv->lid_is_present);
      break;
    case PROP_PRESENTATION_MODE:
      g_value_set_boolean (value, power->priv->presentation_mode);
      break;
    case PROP_ON_AC_BLANK:
      g_value_set_int (value, power->priv->on_ac_blank);
      break;
    case PROP_ON_BATTERY_BLANK:
      g_value_set_int (value, power->priv->on_battery_blank);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
xfpm_power_set_property (GObject *object,
                         guint prop_id,
                         const GValue *value,
                         GParamSpec *pspec)
{
  XfpmPower *power = XFPM_POWER (object);
  gint on_ac_blank, on_battery_blank;

  switch (prop_id)
  {
    case PROP_PRESENTATION_MODE:
      xfpm_power_change_presentation_mode (power, g_value_get_boolean (value));
      break;
    case PROP_ON_AC_BLANK:
      on_ac_blank = g_value_get_int (value);
      power->priv->on_ac_blank = on_ac_blank;
      if (!power->priv->on_battery)
        xfpm_update_blank_time (power);
      break;
    case PROP_ON_BATTERY_BLANK:
      on_battery_blank = g_value_get_int (value);
      power->priv->on_battery_blank = on_battery_blank;
      if (power->priv->on_battery)
        xfpm_update_blank_time (power);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
xfpm_power_finalize (GObject *object)
{
  XfpmPower *power;

  power = XFPM_POWER (object);

  g_free (power->priv->daemon_version);

  g_object_unref (power->priv->inhibit);
  g_object_unref (power->priv->notify);
  g_object_unref (power->priv->conf);
  g_object_unref (power->priv->screensaver);

  if ( power->priv->systemd != NULL )
    g_object_unref (power->priv->systemd);
  if ( power->priv->console != NULL )
    g_object_unref (power->priv->console);

  g_object_unref (power->priv->bus);

  g_hash_table_destroy (power->priv->hash);

#ifdef ENABLE_POLKIT
  g_object_unref (power->priv->polkit);
#endif

  g_object_unref(power->priv->dpms);

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

static XfpmPower*
xfpm_power_new (void)
{
  XfpmPower *power = XFPM_POWER(g_object_new (XFPM_TYPE_POWER, NULL));

  xfconf_g_property_bind (xfpm_xfconf_get_channel(power->priv->conf),
                          XFPM_PROPERTIES_PREFIX PRESENTATION_MODE, G_TYPE_BOOLEAN,
                          G_OBJECT(power), PRESENTATION_MODE);

  xfconf_g_property_bind (xfpm_xfconf_get_channel(power->priv->conf),
                          XFPM_PROPERTIES_PREFIX ON_BATTERY_BLANK, G_TYPE_INT,
                          G_OBJECT (power), ON_BATTERY_BLANK);

  xfconf_g_property_bind (xfpm_xfconf_get_channel(power->priv->conf),
                          XFPM_PROPERTIES_PREFIX ON_AC_BLANK, G_TYPE_INT,
                          G_OBJECT (power), ON_AC_BLANK);

  return power;
}

XfpmPower *
xfpm_power_get (void)
{
  static gpointer xfpm_power_object = NULL;

  if ( G_LIKELY (xfpm_power_object != NULL ) )
  {
    g_object_ref (xfpm_power_object);
  }
  else
  {
    xfpm_power_object = xfpm_power_new ();
    g_object_add_weak_pointer (xfpm_power_object, &xfpm_power_object);
  }

  return XFPM_POWER (xfpm_power_object);
}

void xfpm_power_suspend (XfpmPower *power, gboolean force)
{
  xfpm_power_sleep (power, "Suspend", force);
}

void xfpm_power_hibernate (XfpmPower *power, gboolean force)
{
  xfpm_power_sleep (power, "Hibernate", force);
}

gboolean xfpm_power_has_battery (XfpmPower *power)
{
  GtkStatusIcon *battery = NULL;
  guint i, len;
  GList *list;

  gboolean ret = FALSE;

  list = g_hash_table_get_values (power->priv->hash);
  len = g_list_length (list);

  for ( i = 0; i < len; i++)
  {
    UpDeviceKind type;
    battery = g_list_nth_data (list, i);
    type = xfpm_battery_get_device_type (XFPM_BATTERY (battery));
    if ( type == UP_DEVICE_KIND_BATTERY ||
         type == UP_DEVICE_KIND_UPS )
    {
      ret = TRUE;
      break;
    }
  }

  return ret;
}

static void
xfpm_update_blank_time (XfpmPower *power)
{
  int prev_timeout, prev_interval, prev_prefer_blanking, prev_allow_exposures;
  Display* display = gdk_x11_display_get_xdisplay(gdk_display_get_default ());
  guint screensaver_timeout;

  if (power->priv->on_battery)
    screensaver_timeout = power->priv->on_battery_blank;
  else
    screensaver_timeout = power->priv->on_ac_blank;

    /* Presentation mode disables blanking */
  if (power->priv->presentation_mode)
    screensaver_timeout = 0;

  screensaver_timeout = screensaver_timeout * 60;

  XGetScreenSaver(display, &prev_timeout, &prev_interval, &prev_prefer_blanking, &prev_allow_exposures);
  XFPM_DEBUG ("Prev Timeout: %d / New Timeout: %d", prev_timeout, screensaver_timeout);
  XSetScreenSaver(display, screensaver_timeout, prev_interval, prev_prefer_blanking, prev_allow_exposures);
  XSync (display, FALSE);
}

static void
xfpm_power_change_presentation_mode (XfpmPower *power, gboolean presentation_mode)
{
    /* no change, exit */
  if (power->priv->presentation_mode == presentation_mode)
    return;

  power->priv->presentation_mode = presentation_mode;

  /* presentation mode inhibits dpms */
  xfpm_dpms_inhibit (power->priv->dpms, presentation_mode);

  XFPM_DEBUG ("is_inhibit %s, screensaver_inhibited %s, presentation_mode %s",
  power->priv->inhibited ? "TRUE" : "FALSE",
  power->priv->screensaver_inhibited ? "TRUE" : "FALSE",
  power->priv->presentation_mode ? "TRUE" : "FALSE");

  if (presentation_mode)
  {
  /* presentation mode inhibits the screensaver */
    if (!power->priv->screensaver_inhibited)
    {
      xfce_screensaver_inhibit (power->priv->screensaver, TRUE);
      power->priv->screensaver_inhibited = TRUE;
    }
  }
  else
  {
    EggIdletime *idletime;

    /* make sure we remove the screensaver inhibit */
    if (power->priv->screensaver_inhibited && !power->priv->inhibited)
    {
      xfce_screensaver_inhibit (power->priv->screensaver, FALSE);
      power->priv->screensaver_inhibited = FALSE;
    }

    /* reset the timers */
    idletime = egg_idletime_new ();
    egg_idletime_alarm_reset_all (idletime);

    g_object_unref (idletime);
  }

  XFPM_DEBUG ("is_inhibit %s, screensaver_inhibited %s, presentation_mode %s",
  power->priv->inhibited ? "TRUE" : "FALSE",
  power->priv->screensaver_inhibited ? "TRUE" : "FALSE",
  power->priv->presentation_mode ? "TRUE" : "FALSE");

  xfpm_update_blank_time (power);
}

gboolean
xfpm_power_is_in_presentation_mode (XfpmPower *power)
{
  g_return_val_if_fail (XFPM_IS_POWER (power), FALSE);

  return power->priv->presentation_mode || power->priv->inhibited;
}


/*
 *
 * DBus server implementation for org.freedesktop.PowerManagement
 *
 */
static gboolean xfpm_power_dbus_shutdown (XfpmPower *power,
                                          GDBusMethodInvocation *invocation,
                                          gpointer user_data);

static gboolean xfpm_power_dbus_reboot   (XfpmPower *power,
                                          GDBusMethodInvocation *invocation,
                                          gpointer user_data);

static gboolean xfpm_power_dbus_hibernate (XfpmPower * power,
                                           GDBusMethodInvocation *invocation,
                                           gpointer user_data);

static gboolean xfpm_power_dbus_suspend (XfpmPower * power,
                                         GDBusMethodInvocation *invocation,
                                         gpointer user_data);

static gboolean xfpm_power_dbus_can_reboot (XfpmPower * power,
                                            GDBusMethodInvocation *invocation,
                                            gpointer user_data);

static gboolean xfpm_power_dbus_can_shutdown (XfpmPower * power,
                                              GDBusMethodInvocation *invocation,
                                              gpointer user_data);

static gboolean xfpm_power_dbus_can_hibernate (XfpmPower * power,
                                               GDBusMethodInvocation *invocation,
                                               gpointer user_data);

static gboolean xfpm_power_dbus_can_suspend (XfpmPower * power,
                                             GDBusMethodInvocation *invocation,
                                             gpointer user_data);

static gboolean xfpm_power_dbus_get_on_battery (XfpmPower * power,
                                                GDBusMethodInvocation *invocation,
                                                gpointer user_data);

static gboolean xfpm_power_dbus_get_low_battery (XfpmPower * power,
                                                 GDBusMethodInvocation *invocation,
                                                 gpointer user_data);

#include "org.freedesktop.PowerManagement.h"

static void
xfpm_power_dbus_class_init (XfpmPowerClass * klass)
{
}

static void
xfpm_power_dbus_init (XfpmPower *power)
{
  GDBusConnection *bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
  XfpmPowerManagement *power_dbus;

  TRACE ("entering");

  power_dbus = xfpm_power_management_skeleton_new ();
  g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (power_dbus),
                                    bus,
                                    "/org/freedesktop/PowerManagement",
                                    NULL);

  g_signal_connect_swapped (power_dbus,
                            "handle-shutdown",
                            G_CALLBACK (xfpm_power_dbus_shutdown),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-reboot",
                            G_CALLBACK (xfpm_power_dbus_reboot),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-hibernate",
                            G_CALLBACK (xfpm_power_dbus_hibernate),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-suspend",
                            G_CALLBACK (xfpm_power_dbus_suspend),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-can-reboot",
                            G_CALLBACK (xfpm_power_dbus_can_reboot),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-can-shutdown",
                            G_CALLBACK (xfpm_power_dbus_can_shutdown),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-can-hibernate",
                            G_CALLBACK (xfpm_power_dbus_can_hibernate),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-can-suspend",
                            G_CALLBACK (xfpm_power_dbus_can_suspend),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-get-on-battery",
                            G_CALLBACK (xfpm_power_dbus_get_on_battery),
                            power);
  g_signal_connect_swapped (power_dbus,
                            "handle-get-low-battery",
                            G_CALLBACK (xfpm_power_dbus_get_low_battery),
                            power);
}

static gboolean xfpm_power_dbus_shutdown (XfpmPower *power,
            GDBusMethodInvocation *invocation,
            gpointer user_data)
{
  GError *error = NULL;
  gboolean can_reboot;

  if ( LOGIND_RUNNING () )
  {
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-shutdown", &can_reboot,
                  NULL);
  }
  else
  {
    g_object_get (G_OBJECT (power->priv->console),
                  "can-shutdown", &can_reboot,
                  NULL);
  }

  if ( !can_reboot)
  {
    g_dbus_method_invocation_return_error (invocation,
                                           XFPM_ERROR,
                                           XFPM_ERROR_PERMISSION_DENIED,
                                           _("Permission denied"));
    return TRUE;
  }

  if ( LOGIND_RUNNING () )
    xfpm_systemd_shutdown (power->priv->systemd, &error);
  else
    xfpm_console_kit_shutdown (power->priv->console, &error);

  if (error)
  {
    g_dbus_method_invocation_return_gerror (invocation, error);
    g_error_free (error);
  }
  else
  {
    xfpm_power_management_complete_shutdown (user_data, invocation);
  }

  return TRUE;
}

static gboolean
xfpm_power_dbus_reboot   (XfpmPower *power,
                          GDBusMethodInvocation *invocation,
                          gpointer user_data)
{
  GError *error = NULL;
  gboolean can_reboot;

  if ( LOGIND_RUNNING () )
  {
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-restart", &can_reboot,
                  NULL);
  }
  else
  {
    g_object_get (G_OBJECT (power->priv->console),
                  "can-restart", &can_reboot,
                  NULL);
  }

  if ( !can_reboot)
  {
    g_dbus_method_invocation_return_error (invocation,
                                           XFPM_ERROR,
                                           XFPM_ERROR_PERMISSION_DENIED,
                                           _("Permission denied"));
    return TRUE;
  }

  if ( LOGIND_RUNNING () )
    xfpm_systemd_reboot (power->priv->systemd, &error);
  else
    xfpm_console_kit_reboot (power->priv->console, &error);

  if (error)
  {
    g_dbus_method_invocation_return_gerror (invocation, error);
    g_error_free (error);
  }
  else
  {
    xfpm_power_management_complete_reboot (user_data, invocation);
  }

  return TRUE;
}

static gboolean
xfpm_power_dbus_hibernate (XfpmPower * power,
                           GDBusMethodInvocation *invocation,
                           gpointer user_data)
{
  if ( !power->priv->auth_hibernate )
  {
    g_dbus_method_invocation_return_error (invocation,
                                           XFPM_ERROR,
                                           XFPM_ERROR_PERMISSION_DENIED,
                                           _("Permission denied"));
    return TRUE;
  }

  if (!power->priv->can_hibernate )
  {
    g_dbus_method_invocation_return_error (invocation,
                                           XFPM_ERROR,
                                           XFPM_ERROR_NO_HARDWARE_SUPPORT,
                                           _("Suspend not supported"));
    return TRUE;
  }

  xfpm_power_sleep (power, "Hibernate", FALSE);

  xfpm_power_management_complete_hibernate (user_data, invocation);

  return TRUE;
}

static gboolean
xfpm_power_dbus_suspend (XfpmPower * power,
                         GDBusMethodInvocation *invocation,
                         gpointer user_data)
{
  if ( !power->priv->auth_suspend )
  {
    g_dbus_method_invocation_return_error (invocation,
                                           XFPM_ERROR,
                                           XFPM_ERROR_PERMISSION_DENIED,
                                           _("Permission denied"));
    return TRUE;
  }

  if (!power->priv->can_suspend )
  {
    g_dbus_method_invocation_return_error (invocation,
                                           XFPM_ERROR,
                                           XFPM_ERROR_NO_HARDWARE_SUPPORT,
                                           _("Suspend not supported"));
    return TRUE;
  }

  xfpm_power_sleep (power, "Suspend", FALSE);

  xfpm_power_management_complete_suspend (user_data, invocation);

  return TRUE;
}

static gboolean
xfpm_power_dbus_can_reboot (XfpmPower * power,
                            GDBusMethodInvocation *invocation,
                            gpointer user_data)
{
  gboolean can_reboot;

  if ( LOGIND_RUNNING () )
  {
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-reboot", &can_reboot,
                  NULL);
  }
  else
  {
    g_object_get (G_OBJECT (power->priv->console),
                  "can-reboot", &can_reboot,
                  NULL);
  }

  xfpm_power_management_complete_can_reboot (user_data,
                                             invocation,
                                             can_reboot);

  return TRUE;
}

static gboolean
xfpm_power_dbus_can_shutdown (XfpmPower * power,
                              GDBusMethodInvocation *invocation,
                              gpointer user_data)
{
  gboolean can_shutdown;

  if ( LOGIND_RUNNING () )
  {
    g_object_get (G_OBJECT (power->priv->systemd),
                  "can-shutdown", &can_shutdown,
                  NULL);
  }
  else
  {
    g_object_get (G_OBJECT (power->priv->console),
                  "can-shutdown", &can_shutdown,
                  NULL);
  }

  xfpm_power_management_complete_can_shutdown (user_data,
                                               invocation,
                                               can_shutdown);

  return TRUE;
}

static gboolean
xfpm_power_dbus_can_hibernate (XfpmPower * power,
                               GDBusMethodInvocation *invocation,
                               gpointer user_data)
{
  xfpm_power_management_complete_can_hibernate (user_data,
                                                invocation,
                                                power->priv->can_hibernate);
  return TRUE;
}

static gboolean
xfpm_power_dbus_can_suspend (XfpmPower * power,
                             GDBusMethodInvocation *invocation,
                             gpointer user_data)
{
  xfpm_power_management_complete_can_suspend (user_data,
                                              invocation,
                                              power->priv->can_suspend);

  return TRUE;
}

static gboolean
xfpm_power_dbus_get_on_battery (XfpmPower * power,
                                GDBusMethodInvocation *invocation,
                                gpointer user_data)
{
  xfpm_power_management_complete_get_on_battery (user_data,
                                                 invocation,
                                                 power->priv->on_battery);

  return TRUE;
}

static gboolean
xfpm_power_dbus_get_low_battery (XfpmPower * power,
                                 GDBusMethodInvocation *invocation,
                                 gpointer user_data)
{
  xfpm_power_management_complete_get_low_battery (user_data,
                                                  invocation,
                                                  power->priv->on_low_battery);

  return TRUE;
}
