/*
 * * Copyright (C) 2013 Sonal Santan <sonal.santan@gmail.com>
 *
 * 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
 */

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

#include <libxfce4util/libxfce4util.h>

#include "xfpm-kbd-backlight.h"
#include "xfpm-button.h"
#include "xfpm-notify.h"
#include "xfpm-power.h"

static void xfpm_kbd_backlight_finalize     (GObject *object);

struct XfpmKbdBacklightPrivate
{
    XfpmPower       *power;
    XfpmButton      *button;

    GDBusConnection *bus;
    GDBusProxy      *proxy;

    gboolean         dimmed;
    gboolean         on_battery;
    gint32           max_level;
    gint             min_level;
    gint             step;

    XfpmNotify      *notify;
    NotifyNotification *n;
};

G_DEFINE_TYPE_WITH_PRIVATE (XfpmKbdBacklight, xfpm_kbd_backlight, G_TYPE_OBJECT)


static gint
calculate_step( gint max_level )
{
    if ( max_level < 20 )
        return 1;
    else
        return max_level / 20;
}


static void
xfpm_kbd_backlight_on_battery_changed_cb (XfpmPower *power, gboolean on_battery, XfpmKbdBacklight *backlight)
{
    backlight->priv->on_battery = on_battery;
}


static void
xfpm_kbd_backlight_init_max_level (XfpmKbdBacklight *backlight)
{
    GError *error = NULL;
    GVariant *var;

    var = g_dbus_proxy_call_sync (backlight->priv->proxy, "GetMaxBrightness",
                                  NULL,
                                  G_DBUS_CALL_FLAGS_NONE,
                                  -1, NULL,
                                  &error);

    if (var)
    {
	g_variant_get (var,
		       "(i)",
		       &backlight->priv->max_level);
	g_variant_unref (var);
    }

    if ( error )
    {
        g_warning ("Failed to get keyboard max brightness level : %s", error->message);
        g_error_free (error);
    }
}


static void
xfpm_kbd_backlight_show_notification (XfpmKbdBacklight *self, gfloat value)
{
    gchar *summary;

    if ( self->priv->n == NULL )
    {
        /* generate a human-readable summary for the notification */
        summary = g_strdup_printf (_("Keyboard Brightness: %.0f percent"), value);
        self->priv->n = xfpm_notify_new_notification (self->priv->notify,
                _("Power Manager"),
                summary,
                "keyboard-brightness",
                0,
                XFPM_NOTIFY_NORMAL);
        g_free (summary);
    }

    /* add the brightness value to the notification */
    notify_notification_set_hint (self->priv->n, "value", g_variant_new_int32 (value));

    /* show the notification */
    notify_notification_show (self->priv->n, NULL);
}


static gint
xfpm_kbd_backlight_get_level (XfpmKbdBacklight *backlight)
{
    GError *error = NULL;
    gint32 level = -1;
    GVariant *var;

    var = g_dbus_proxy_call_sync (backlight->priv->proxy, "GetBrightness",
                                  NULL,
                                  G_DBUS_CALL_FLAGS_NONE,
                                  -1, NULL,
                                  &error);
    if (var)
    {
	g_variant_get (var,
		       "(i)",
		       &level);
	g_variant_unref (var);
    }

    if ( error )
    {
        g_warning ("Failed to get keyboard brightness level : %s", error->message);
        g_error_free (error);
    }
    return level;
}


static void
xfpm_kbd_backlight_set_level (XfpmKbdBacklight *backlight, gint32 level)
{
    GError *error = NULL;
    gfloat percent;
    GVariant *var;

    var = g_dbus_proxy_call_sync (backlight->priv->proxy, "SetBrightness",
                                  g_variant_new("(i)", level),
                                  G_DBUS_CALL_FLAGS_NONE,
                                  -1, NULL,
                                  &error);

    if (var)
        g_variant_unref (var);

    if ( error )
    {
        g_warning ("Failed to set keyboard brightness level : %s", error->message);
        g_error_free (error);
    }
    else
    {
        percent = 100.0 * ((gfloat)level / (gfloat)backlight->priv->max_level);
        xfpm_kbd_backlight_show_notification (backlight, percent);
    }
}

static void
xfpm_kbd_backlight_up (XfpmKbdBacklight *backlight)
{
    gint level;

    level = xfpm_kbd_backlight_get_level(backlight);

    if ( level == -1)
        return;

    if ( level == backlight->priv->max_level )
        return;

    level += backlight->priv->step;

    if ( level > backlight->priv->max_level )
        level = backlight->priv->max_level;

    xfpm_kbd_backlight_set_level(backlight, level);
}


static void
xfpm_kbd_backlight_down (XfpmKbdBacklight *backlight)
{
    gint level;

    level = xfpm_kbd_backlight_get_level(backlight);

    if ( level == -1)
        return;

    if ( level == backlight->priv->min_level )
        return;

    level -= backlight->priv->step;

    if ( level < backlight->priv->min_level )
        level = backlight->priv->min_level;

    xfpm_kbd_backlight_set_level(backlight, level);
}


static void
xfpm_kbd_backlight_button_pressed_cb (XfpmButton *button, XfpmButtonKey type, XfpmKbdBacklight *backlight)
{
    if ( type == BUTTON_KBD_BRIGHTNESS_UP )
    {
        xfpm_kbd_backlight_up (backlight);
    }
    else if ( type == BUTTON_KBD_BRIGHTNESS_DOWN )
    {
        xfpm_kbd_backlight_down (backlight);
    }
}


static void
xfpm_kbd_backlight_class_init (XfpmKbdBacklightClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->finalize = xfpm_kbd_backlight_finalize;
}


static void
xfpm_kbd_backlight_init (XfpmKbdBacklight *backlight)
{
    GError *error = NULL;

    backlight->priv = xfpm_kbd_backlight_get_instance_private (backlight);

    backlight->priv->bus = NULL;
    backlight->priv->proxy = NULL;
    backlight->priv->power = NULL;
    backlight->priv->button = NULL;
    backlight->priv->dimmed = FALSE;
    backlight->priv->on_battery = FALSE;
    backlight->priv->max_level = 0;
    backlight->priv->min_level = 0;
    backlight->priv->notify = NULL;
    backlight->priv->n = NULL;

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

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

    backlight->priv->proxy = g_dbus_proxy_new_sync (backlight->priv->bus,
						    G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
						    G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
						    NULL,
						    "org.freedesktop.UPower",
						    "/org/freedesktop/UPower/KbdBacklight",
						    "org.freedesktop.UPower.KbdBacklight",
						    NULL,
						    NULL);
    if ( backlight->priv->proxy == NULL )
    {
        g_warning ("Unable to get the interface, org.freedesktop.UPower.KbdBacklight");
        goto out;
    }

    xfpm_kbd_backlight_init_max_level (backlight);

    if ( backlight->priv->max_level == 0 )
        goto out;

    backlight->priv->step = calculate_step (backlight->priv->max_level);
    backlight->priv->power = xfpm_power_get ();
    backlight->priv->button = xfpm_button_new ();
    backlight->priv->notify = xfpm_notify_new ();

    g_signal_connect (backlight->priv->button, "button-pressed",
                      G_CALLBACK (xfpm_kbd_backlight_button_pressed_cb), backlight);

    g_signal_connect (backlight->priv->power, "on-battery-changed",
                      G_CALLBACK (xfpm_kbd_backlight_on_battery_changed_cb), backlight);

    g_object_get (G_OBJECT (backlight->priv->power),
                  "on-battery", &backlight->priv->on_battery,
                  NULL);

out:
    ;
}


static void
xfpm_kbd_backlight_finalize (GObject *object)
{
    XfpmKbdBacklight *backlight = NULL;

    backlight = XFPM_KBD_BACKLIGHT (object);

    if ( backlight->priv->power )
        g_object_unref (backlight->priv->power );

    if ( backlight->priv->button )
        g_object_unref (backlight->priv->button);

    if ( backlight->priv->notify )
        g_object_unref (backlight->priv->notify);

    if ( backlight->priv->n )
        g_object_unref (backlight->priv->n);

    if ( backlight->priv->proxy )
        g_object_unref (backlight->priv->proxy);

    if ( backlight->priv->bus )
        g_object_unref (backlight->priv->bus);

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


XfpmKbdBacklight *
xfpm_kbd_backlight_new (void)
{
    XfpmKbdBacklight *backlight = NULL;
    backlight = g_object_new (XFPM_TYPE_KBD_BACKLIGHT, NULL);
    return backlight;
}


gboolean xfpm_kbd_backlight_has_hw (XfpmKbdBacklight *backlight)
{
    return ( backlight->priv->proxy == NULL ) ? FALSE : TRUE;
}
