/*
 *
 * * Copyright (C) 2008-2011 Ali <aliov@xfce.org>
 *
 * 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

#include <gdk/gdk.h>

#include <libxfce4util/libxfce4util.h>

#include "xfpm-common.h"

#include "xfpm-dpms.h"
#include "xfpm-xfconf.h"
#include "xfpm-config.h"
#include "xfpm-debug.h"


static void xfpm_dpms_finalize   (GObject *object);

struct XfpmDpmsPrivate
{
  XfpmXfconf      *conf;

  gboolean         dpms_capable;
  gboolean         inhibited;

  gboolean         on_battery;

  gulong           switch_off_timeout_id;
  gulong           switch_on_timeout_id;
};

G_DEFINE_TYPE_WITH_PRIVATE (XfpmDpms, xfpm_dpms, G_TYPE_OBJECT)

static void
xfpm_dpms_set_timeouts (XfpmDpms *dpms, guint16 standby, guint16 suspend, guint off)
{
  CARD16 x_standby = 0 , x_suspend = 0, x_off = 0;

  DPMSGetTimeouts (gdk_x11_get_default_xdisplay(), &x_standby, &x_suspend, &x_off);

  if ( standby != x_standby || suspend != x_suspend || off != x_off )
  {
    XFPM_DEBUG ("Settings dpms: standby=%d suspend=%d off=%d\n", standby, suspend, off);
    DPMSSetTimeouts (gdk_x11_get_default_xdisplay(), standby,
                     suspend,
                     off );
  }
}

/*
 * Disable DPMS
 */
static void
xfpm_dpms_disable (XfpmDpms *dpms)
{
  BOOL state;
  CARD16 power_level;

  if (!DPMSInfo (gdk_x11_get_default_xdisplay(), &power_level, &state) )
    g_warning ("Cannot get DPMSInfo");

  if ( state )
    DPMSDisable (gdk_x11_get_default_xdisplay());
}

/*
 * Enable DPMS
 */
static void
xfpm_dpms_enable (XfpmDpms *dpms)
{
  BOOL state;
  CARD16 power_level;

  if (!DPMSInfo (gdk_x11_get_default_xdisplay(), &power_level, &state) )
    g_warning ("Cannot get DPMSInfo");

  if ( !state )
    DPMSEnable (gdk_x11_get_default_xdisplay());
}

static void
xfpm_dpms_get_enabled (XfpmDpms *dpms, gboolean *dpms_enabled)
{
  g_object_get (G_OBJECT (dpms->priv->conf),
                DPMS_ENABLED_CFG, dpms_enabled,
                NULL);
}

static void
xfpm_dpms_get_sleep_mode (XfpmDpms *dpms, gboolean *ret_standby_mode)
{
  gchar *sleep_mode;

  g_object_get (G_OBJECT (dpms->priv->conf),
                DPMS_SLEEP_MODE, &sleep_mode,
                NULL);

  if ( !g_strcmp0 (sleep_mode, "Standby"))
    *ret_standby_mode = TRUE;
  else
    *ret_standby_mode = FALSE;

  g_free (sleep_mode);
}

static void
xfpm_dpms_get_configuration_timeouts (XfpmDpms *dpms, guint16 *ret_sleep, guint16 *ret_off )
{
  guint sleep_time, off_time;

  g_object_get (G_OBJECT (dpms->priv->conf),
                dpms->priv->on_battery ? ON_BATT_DPMS_SLEEP : ON_AC_DPMS_SLEEP, &sleep_time,
                dpms->priv->on_battery ? ON_BATT_DPMS_OFF : ON_AC_DPMS_OFF, &off_time,
                NULL);

  *ret_sleep = sleep_time * 60;
  *ret_off =  off_time * 60;
}

void
xfpm_dpms_refresh (XfpmDpms *dpms)
{
  gboolean enabled;
  guint16 off_timeout;
  guint16 sleep_timeout;
  gboolean sleep_mode;

  if ( dpms->priv->inhibited)
  {
    xfpm_dpms_disable (dpms);
    return;
  }

  xfpm_dpms_get_enabled (dpms, &enabled);

  if ( !enabled )
  {
    xfpm_dpms_disable (dpms);
    return;
  }

  xfpm_dpms_enable (dpms);
  xfpm_dpms_get_configuration_timeouts (dpms, &sleep_timeout, &off_timeout);
  xfpm_dpms_get_sleep_mode (dpms, &sleep_mode);

  if (sleep_mode == TRUE )
  {
    xfpm_dpms_set_timeouts     (dpms,
                                sleep_timeout,
                                0,
                                off_timeout);
  }
  else
  {
    xfpm_dpms_set_timeouts     (dpms,
                                0,
                                sleep_timeout,
                                off_timeout );
  }
}

static void
xfpm_dpms_settings_changed_cb (GObject *obj, GParamSpec *spec, XfpmDpms *dpms)
{
  if ( g_str_has_prefix (spec->name, "dpms"))
  {
    XFPM_DEBUG ("Configuration changed");
    xfpm_dpms_refresh (dpms);
  }
}

static void
xfpm_dpms_class_init(XfpmDpmsClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS(klass);

  object_class->finalize = xfpm_dpms_finalize;
}

/*
 * Check if the display is DPMS capabale if not do nothing.
 */
static void
xfpm_dpms_init(XfpmDpms *dpms)
{
  dpms->priv = xfpm_dpms_get_instance_private(dpms);

  dpms->priv->dpms_capable = DPMSCapable (gdk_x11_get_default_xdisplay());
  dpms->priv->switch_off_timeout_id = 0;
  dpms->priv->switch_on_timeout_id = 0;

  if ( dpms->priv->dpms_capable )
  {
    dpms->priv->conf    = xfpm_xfconf_new  ();

    g_signal_connect (dpms->priv->conf, "notify",
                      G_CALLBACK (xfpm_dpms_settings_changed_cb), dpms);

    xfpm_dpms_refresh (dpms);
  }
  else
  {
    g_warning ("Monitor is not DPMS capable");
  }
}

static void
xfpm_dpms_finalize(GObject *object)
{
  XfpmDpms *dpms;

  dpms = XFPM_DPMS (object);

  g_object_unref (dpms->priv->conf);

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

XfpmDpms *
xfpm_dpms_new (void)
{
  static gpointer xfpm_dpms_object = NULL;

  if ( G_LIKELY (xfpm_dpms_object != NULL ) )
  {
    g_object_ref (xfpm_dpms_object);
  }
  else
  {
    xfpm_dpms_object = g_object_new (XFPM_TYPE_DPMS, NULL);
    g_object_add_weak_pointer (xfpm_dpms_object, &xfpm_dpms_object);
  }

  return XFPM_DPMS (xfpm_dpms_object);
}

gboolean
xfpm_dpms_capable (XfpmDpms *dpms)
{
  g_return_val_if_fail (XFPM_IS_DPMS(dpms), FALSE);

  return dpms->priv->dpms_capable;
}

void
xfpm_dpms_force_level (XfpmDpms *dpms, CARD16 level)
{
  CARD16 current_level;
  BOOL current_state;

  XFPM_DEBUG ("start");

  if ( !dpms->priv->dpms_capable )
    goto out;

  if ( G_UNLIKELY (!DPMSInfo (gdk_x11_get_default_xdisplay (), &current_level, &current_state)) )
  {
    g_warning ("Cannot get DPMSInfo");
    goto out;
  }

  if ( !current_state )
  {
    XFPM_DEBUG ("DPMS is disabled");
    goto out;
  }

  if ( current_level != level )
  {
    XFPM_DEBUG ("Forcing DPMS mode %d", level);

    if ( !DPMSForceLevel (gdk_x11_get_default_xdisplay (), level ) )
    {
      g_warning ("Cannot set Force DPMS level %d", level);
      goto out;
    }

    if ( level == DPMSModeOn )
      XResetScreenSaver (gdk_x11_get_default_xdisplay ());

    XSync (gdk_x11_get_default_xdisplay (), FALSE);
  }
  else
  {
    XFPM_DEBUG ("No need to change DPMS mode, current_level=%d requested_level=%d", current_level, level);
  }

  out:
    ;
}

gboolean
xfpm_dpms_is_inhibited (XfpmDpms *dpms)
{
  return dpms->priv->inhibited;
}

void
xfpm_dpms_inhibit (XfpmDpms *dpms, gboolean inhibit)
{
  if ( dpms->priv->inhibited == inhibit )
    return;

  dpms->priv->inhibited = inhibit;
  xfpm_dpms_refresh (dpms);
  XFPM_DEBUG ("dpms inhibited %s", inhibit ? "TRUE" : "FALSE");
}

void
xfpm_dpms_set_on_battery (XfpmDpms *dpms, gboolean on_battery)
{
  if ( dpms->priv->on_battery == on_battery )
    return;

  dpms->priv->on_battery = on_battery;
  xfpm_dpms_refresh (dpms);
  XFPM_DEBUG ("dpms on battery %s", on_battery ? "TRUE" : "FALSE");
}
