/*
 * MATE CPUFreq Applet
 * Copyright (C) 2008 Carlos Garcia Campos <carlosgc@gnome.org>
 *
 *  This library 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 library 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 library; if not, write to the Free
 *  Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <sys/sysinfo.h>

#ifdef HAVE_POLKIT
#include <gio/gio.h>
#endif /* HAVE_POLKIT */

#include "cpufreq-selector.h"

struct _CPUFreqSelector {
	GObject parent;

#ifdef HAVE_POLKIT
	GDBusConnection *system_bus;
	GDBusProxy      *proxy;
#endif /* HAVE_POLKIT */
};

struct _CPUFreqSelectorClass {
	GObjectClass parent_class;
};

G_DEFINE_TYPE (CPUFreqSelector, cpufreq_selector, G_TYPE_OBJECT)

static void
cpufreq_selector_finalize (GObject *object)
{
#ifdef HAVE_POLKIT
	CPUFreqSelector *selector = CPUFREQ_SELECTOR (object);

	g_clear_object (&selector->proxy);
	g_clear_object (&selector->system_bus);
#endif /* HAVE_POLKIT */

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

static void
cpufreq_selector_class_init (CPUFreqSelectorClass *klass)
{
	GObjectClass *object_class = G_OBJECT_CLASS (klass);

	object_class->finalize = cpufreq_selector_finalize;
}

static void
cpufreq_selector_init (CPUFreqSelector *selector)
{
}

CPUFreqSelector *
cpufreq_selector_get_default (void)
{
	static CPUFreqSelector *selector = NULL;

	if (!selector)
		selector = CPUFREQ_SELECTOR (g_object_new (CPUFREQ_TYPE_SELECTOR, NULL));

	return selector;
}

#ifdef HAVE_POLKIT
typedef enum {
	FREQUENCY,
	GOVERNOR
} CPUFreqSelectorCall;

typedef struct {
	CPUFreqSelector *selector;

	CPUFreqSelectorCall call;

	guint  cpu;
	guint  frequency;
	gchar *governor;

	guint32 parent_xid;
} SelectorAsyncData;

static void
selector_async_data_free (SelectorAsyncData *data)
{
	if (!data)
		return;

	g_free (data->governor);
	g_free (data);
}

static gboolean
cpufreq_selector_connect_to_system_bus (CPUFreqSelector *selector,
					GError         **error)
{
	if (selector->system_bus)
		return TRUE;

	selector->system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);

	return (selector->system_bus != NULL);
}


static gboolean
cpufreq_selector_create_proxy (CPUFreqSelector  *selector,
                               GError          **error)
{
	if (selector->proxy)
		return TRUE;

	selector->proxy = g_dbus_proxy_new_sync (selector->system_bus,
	                                         G_DBUS_PROXY_FLAGS_NONE,
	                                         NULL,
	                                         "org.mate.CPUFreqSelector",
	                                         "/org/mate/cpufreq_selector/selector",
	                                         "org.mate.CPUFreqSelector",
	                                         NULL,
	                                         error);

	return (selector->proxy != NULL);
}

static void
selector_setter_cb (GObject      *source,
                    GAsyncResult *result,
                    gpointer      user_data)
{
	GDBusProxy *proxy = G_DBUS_PROXY (source);
	SelectorAsyncData *data = (SelectorAsyncData *)user_data;
	GError *error = NULL;

	g_dbus_proxy_call_finish (proxy, result, &error);
	if (error != NULL) {
		g_warning ("%s", error->message);
		g_clear_error (&error);
	}
	selector_async_data_free (data);
}

static void
selector_set_frequency_async (SelectorAsyncData *data)
{
	GError *error = NULL;

	if (!cpufreq_selector_connect_to_system_bus (data->selector, &error)) {
		g_warning ("%s", error->message);
		g_error_free (error);

		selector_async_data_free (data);

		return;
	}

	if (!cpufreq_selector_create_proxy (data->selector, &error)) {
		g_warning ("%s", error->message);
		g_error_free (error);

		selector_async_data_free (data);
		return;
	}

	g_dbus_proxy_call (data->selector->proxy,
	                   "SetFrequency",
	                   g_variant_new ("(uu)",
	                                  data->cpu,
	                                  data->frequency),
	                   G_DBUS_CALL_FLAGS_NONE,
	                   -1,
	                   NULL,
	                   selector_setter_cb,
	                   data);
}

void
cpufreq_selector_set_frequency_async (CPUFreqSelector *selector,
				      guint            cpu,
				      guint            frequency)
{
    guint            cores;
    cores = get_nprocs() ;
	for (cpu = 0; cpu < cores; cpu = cpu+1){ 
	    SelectorAsyncData *data;

	    data = g_new0 (SelectorAsyncData, 1);

	    data->selector = selector;
	    data->call = FREQUENCY;
	    data->cpu = cpu;
	    data->frequency = frequency;

	    selector_set_frequency_async (data);
        }
}

static void
selector_set_governor_async (SelectorAsyncData *data)
{
	GError *error = NULL;

	if (!cpufreq_selector_connect_to_system_bus (data->selector, &error)) {
		g_warning ("%s", error->message);
		g_error_free (error);

		selector_async_data_free (data);

		return;
	}

	if (!cpufreq_selector_create_proxy (data->selector, &error)) {
		g_warning ("%s", error->message);
		g_error_free (error);

		selector_async_data_free (data);

		return;
	}

	g_dbus_proxy_call (data->selector->proxy,
	                   "SetGovernor",
	                   g_variant_new ("(us)",
	                                  data->cpu,
	                                  data->governor),
	                   G_DBUS_CALL_FLAGS_NONE,
	                   -1,
	                   NULL,
	                   selector_setter_cb,
	                   data);
}

void
cpufreq_selector_set_governor_async (CPUFreqSelector *selector,
				     guint            cpu,
				     const gchar     *governor)
{
    guint            cores;
    cores = get_nprocs() ;
    for (cpu = 0; cpu < cores; cpu = cpu+1){
	    SelectorAsyncData *data;

	    data = g_new0 (SelectorAsyncData, 1);

	    data->selector = selector;
	    data->call = GOVERNOR;
	    data->cpu = cpu;
	    data->governor = g_strdup (governor);

	    selector_set_governor_async (data);
   }
}
#else /* !HAVE_POLKIT */
static void
cpufreq_selector_run_command (CPUFreqSelector *selector,
			      const gchar     *args)
{
	gchar  *command;
	gchar  *path;
	GError *error = NULL;

	path = g_find_program_in_path ("cpufreq-selector");

	if (!path)
		return;

	command = g_strdup_printf ("%s %s", path, args);
	g_free (path);

	g_spawn_command_line_async (command, &error);
	g_free (command);

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

void
cpufreq_selector_set_frequency_async (CPUFreqSelector *selector,
				      guint            cpu,
				      guint            frequency)
{
    guint            cores;
    cores = get_nprocs() ;

    for (cpu = 0; cpu < cores; cpu = cpu+1){
	    gchar *args;

	    args = g_strdup_printf ("-c %u -f %u", cpu, frequency);
	    cpufreq_selector_run_command (selector, args);
	    g_free (args);
    }
}

void
cpufreq_selector_set_governor_async (CPUFreqSelector *selector,
				     guint            cpu,
				     const gchar     *governor)
{
    guint            cores;
    cores = get_nprocs() ;
    for (cpu = 0; cpu < cores; cpu = cpu+1){
	    gchar *args;

	    args = g_strdup_printf ("-c %u -g %s", cpu, governor);
	    cpufreq_selector_run_command (selector, args);
	    g_free (args);
    }
}
#endif /* HAVE_POLKIT */