/*
 * This file is part of roccat-tools.
 *
 * roccat-tools 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.
 *
 * roccat-tools 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 roccat-tools. If not, see <http://www.gnu.org/licenses/>.
 */

#include "roccat_sensitivity_selector.h"
#include "roccat_hscale.h"

#define ROCCAT_SENSITIVITY_SELECTOR_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), ROCCAT_SENSITIVITY_SELECTOR_TYPE, RoccatSensitivitySelectorPrivate))

struct _RoccatSensitivitySelectorPrivate {
	GtkTable *table;
	GtkCheckButton *linked;
	RoccatHScale *x_scale;
	RoccatHScale *y_scale;
	gulong x_scale_handler;
	gulong y_scale_handler;
	gulong linked_handler;
	gdouble min, max, step;
};

enum {
	PROP_0,
	PROP_MIN,
	PROP_MAX,
	PROP_STEP,
};

G_DEFINE_TYPE(RoccatSensitivitySelector, roccat_sensitivity_selector, GTK_TYPE_FRAME);

static void x_to_y(RoccatSensitivitySelector *selector) {
	RoccatSensitivitySelectorPrivate *priv = selector->priv;

	g_signal_handler_block(G_OBJECT(priv->y_scale), priv->y_scale_handler);
	roccat_hscale_set_value_blocked(priv->y_scale, roccat_hscale_get_value(priv->x_scale));
	g_signal_handler_unblock(G_OBJECT(priv->y_scale), priv->y_scale_handler);
}

static void y_to_x(RoccatSensitivitySelector *selector) {
	RoccatSensitivitySelectorPrivate *priv = selector->priv;

	g_signal_handler_block(G_OBJECT(priv->x_scale), priv->x_scale_handler);
	roccat_hscale_set_value_blocked(priv->x_scale, roccat_hscale_get_value(priv->y_scale));
	g_signal_handler_unblock(G_OBJECT(priv->x_scale), priv->x_scale_handler);
}

void roccat_sensitivity_selector_set_digits(RoccatSensitivitySelector *selector, guint digits) {
	roccat_hscale_set_digits(selector->priv->x_scale, digits);
	roccat_hscale_set_digits(selector->priv->y_scale, digits);
}

gdouble roccat_sensitivity_selector_get_x_value(RoccatSensitivitySelector *selector) {
	return roccat_hscale_get_value(selector->priv->x_scale);
}

void roccat_sensitivity_selector_set_x_value_blocked(RoccatSensitivitySelector *selector, gdouble value) {
	roccat_hscale_set_value_blocked(selector->priv->x_scale, value);
}

gdouble roccat_sensitivity_selector_get_y_value(RoccatSensitivitySelector *selector) {
	return roccat_hscale_get_value(selector->priv->y_scale);
}

void roccat_sensitivity_selector_set_y_value_blocked(RoccatSensitivitySelector *selector, gdouble value) {
	roccat_hscale_set_value_blocked(selector->priv->y_scale, value);
}

gboolean roccat_sensitivity_selector_get_linked(RoccatSensitivitySelector *selector) {
	return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(selector->priv->linked));
}

void roccat_sensitivity_selector_set_linked(RoccatSensitivitySelector *selector, gboolean value) {
	RoccatSensitivitySelectorPrivate *priv = selector->priv;
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->linked), value);
	if (value)
		x_to_y(selector);
}

static RoccatHScale *scale_new(gdouble min, gdouble max, gdouble step) {
	RoccatHScale *scale;
	gdouble i;

	scale = ROCCAT_HSCALE(roccat_hscale_new_with_range(min, max, step));

	for (i = min; i <= max; i += 1.0)
		roccat_hscale_add_mark(scale, i, GTK_POS_BOTTOM, NULL);
	return scale;
}

static void x_value_changed_cb(RoccatHScale *scale, gpointer user_data) {
	RoccatSensitivitySelector *selector = ROCCAT_SENSITIVITY_SELECTOR(user_data);

	if (roccat_sensitivity_selector_get_linked(selector))
		x_to_y(selector);
}

static void y_value_changed_cb(RoccatHScale *scale, gpointer user_data) {
	RoccatSensitivitySelector *selector = ROCCAT_SENSITIVITY_SELECTOR(user_data);

	if (roccat_sensitivity_selector_get_linked(selector))
		y_to_x(selector);
}

static void link_toggled_cb(GtkToggleButton *button, gpointer user_data) {
	RoccatSensitivitySelector *selector = ROCCAT_SENSITIVITY_SELECTOR(user_data);

	if (gtk_toggle_button_get_active(button))
		x_to_y(selector);
}

static GObject *roccat_sensitivity_selector_constructor(GType gtype, guint n_properties, GObjectConstructParam *properties) {
	RoccatSensitivitySelectorPrivate *priv;
	RoccatSensitivitySelector *selector;
	GObject *obj;

	obj = G_OBJECT_CLASS(roccat_sensitivity_selector_parent_class)->constructor(gtype, n_properties, properties);
	selector = ROCCAT_SENSITIVITY_SELECTOR(obj);
	priv = selector->priv;

	priv->x_scale = scale_new(priv->min, priv->max, priv->step);
	priv->y_scale = scale_new(priv->min, priv->max, priv->step);
	priv->linked = GTK_CHECK_BUTTON(gtk_check_button_new_with_label("Link"));

	gtk_table_attach(GTK_TABLE(priv->table), gtk_label_new("X"), 0, 1, 0, 1, GTK_SHRINK, GTK_EXPAND, 0, 0);
	gtk_table_attach(GTK_TABLE(priv->table), gtk_label_new("Y"), 0, 1, 2, 3, GTK_SHRINK, GTK_EXPAND, 0, 0);
	gtk_table_attach(GTK_TABLE(priv->table), GTK_WIDGET(priv->x_scale), 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0);
	gtk_table_attach(GTK_TABLE(priv->table), GTK_WIDGET(priv->linked), 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0);
	gtk_table_attach(GTK_TABLE(priv->table), GTK_WIDGET(priv->y_scale), 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND, 0, 0);

	priv->x_scale_handler = g_signal_connect(G_OBJECT(priv->x_scale), "value-changed", G_CALLBACK(x_value_changed_cb), selector);
	priv->y_scale_handler = g_signal_connect(G_OBJECT(priv->y_scale), "value-changed", G_CALLBACK(y_value_changed_cb), selector);
	priv->linked_handler = g_signal_connect(G_OBJECT(priv->linked), "toggled", G_CALLBACK(link_toggled_cb), selector);

	return obj;
}

GtkWidget *roccat_sensitivity_selector_new(gdouble min, gdouble max, gdouble step) {
	RoccatSensitivitySelector *selector;

	selector = g_object_new(ROCCAT_SENSITIVITY_SELECTOR_TYPE,
			"min", min,
			"max", max,
			"step", step,
			NULL);

	return GTK_WIDGET(selector);
}

static void roccat_sensitivity_selector_init(RoccatSensitivitySelector *selector) {
	RoccatSensitivitySelectorPrivate *priv = ROCCAT_SENSITIVITY_SELECTOR_GET_PRIVATE(selector);

	selector->priv = priv;

	priv->table = GTK_TABLE(gtk_table_new(1, 1, FALSE));
	gtk_container_add(GTK_CONTAINER(selector), GTK_WIDGET(priv->table));

	gtk_frame_set_label(GTK_FRAME(selector), "Sensitivity");
}

static void roccat_sensitivity_selector_set_property(GObject *object, guint prop_id, GValue const *value, GParamSpec *pspec) {
	RoccatSensitivitySelectorPrivate *priv = ROCCAT_SENSITIVITY_SELECTOR(object)->priv;
	switch(prop_id) {
	case PROP_MIN:
		priv->min = g_value_get_double(value);
		break;
	case PROP_MAX:
		priv->max = g_value_get_double(value);
		break;
	case PROP_STEP:
		priv->step = g_value_get_double(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}

static void roccat_sensitivity_selector_class_init(RoccatSensitivitySelectorClass *klass) {
	GObjectClass *gobject_class;

	gobject_class = (GObjectClass *)klass;

	gobject_class->constructor = roccat_sensitivity_selector_constructor;
	gobject_class->set_property = roccat_sensitivity_selector_set_property;

	g_type_class_add_private(klass, sizeof(RoccatSensitivitySelectorPrivate));

	g_object_class_install_property(gobject_class, PROP_MIN,
			g_param_spec_double("min",
					"minimum value",
					"The minimum value",
					-G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
					G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property(gobject_class, PROP_MAX,
			g_param_spec_double("max",
					"maximum value",
					"The maximum value",
					-G_MAXDOUBLE, G_MAXDOUBLE, 0.0,
					G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));

	g_object_class_install_property(gobject_class, PROP_STEP,
			g_param_spec_double("step",
					"step",
					"Step",
					G_MINDOUBLE, G_MAXDOUBLE, 1.0,
					G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}
