/*
 * 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 <gaminggear/gdk_key_translations.h>
#include <gaminggear/hid_uid.h>
#include "ryos_key_button.h"
#include "ryos_gui_key_relations.h"
#include "gdk_roccat_helper.h"

#define RYOS_KEY_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RYOS_KEY_BUTTON_TYPE, RyosKeyButtonClass))
#define IS_RYOS_KEY_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RYOS_KEY_BUTTON_TYPE))
#define RYOS_KEY_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), RYOS_KEY_BUTTON_TYPE, RyosKeyButtonPrivate))

static double const ryos_key_button_border_factor = 0.2;

typedef struct _RyosKeyButtonClass RyosKeyButtonClass;
typedef struct _RyosKeyButtonPrivate RyosKeyButtonPrivate;

struct _RyosKeyButton {
	GtkRadioButton parent;
	RyosKeyButtonPrivate *priv;
};

struct _RyosKeyButtonClass {
	GtkRadioButtonClass parent_class;
};

struct _RyosKeyButtonPrivate {
	GtkWidget *draw_area;
	GtkLabel *label;
	gboolean light_on;
	GdkColor color;
	double intensity;
	double active_red;
	double active_green;
	double active_blue;
};

G_DEFINE_TYPE(RyosKeyButton, ryos_key_button, GTK_TYPE_RADIO_BUTTON);

static void calculate_active_color(RyosKeyButton *key_button) {
	RyosKeyButtonPrivate *priv = key_button->priv;

	priv->active_red = (double)priv->color.red * priv->intensity / (double)G_MAXUINT16;
	priv->active_green = (double)priv->color.green * priv->intensity / (double)G_MAXUINT16;
	priv->active_blue = (double)priv->color.blue * priv->intensity / (double)G_MAXUINT16;

	gtk_widget_queue_draw(priv->draw_area);
}

static gboolean expose_cb(GtkWidget *widget, GdkEventExpose *event, gpointer user_data) {
	RyosKeyButtonPrivate *priv = RYOS_KEY_BUTTON(user_data)->priv;
	GtkAllocation allocation;
	cairo_t *cr;
	double border_width;

	if (!priv->light_on)
		return FALSE;

	gtk_widget_get_allocation(widget, &allocation);

	cr = gdk_cairo_create(event->window);

	border_width = MIN(allocation.width, allocation.height) * ryos_key_button_border_factor;

	cairo_move_to(cr, allocation.x,                                   allocation.y);
	cairo_line_to(cr, allocation.x + allocation.width,                allocation.y);
	cairo_line_to(cr, allocation.x + allocation.width,                allocation.y + allocation.height);
	cairo_line_to(cr, allocation.x,                                   allocation.y + allocation.height);
	cairo_line_to(cr, allocation.x,                                   allocation.y);
	cairo_line_to(cr, allocation.x + border_width,                    allocation.y + border_width);
	cairo_line_to(cr, allocation.x + border_width,                    allocation.y + allocation.height - border_width);
	cairo_line_to(cr, allocation.x + allocation.width - border_width, allocation.y + allocation.height - border_width);
	cairo_line_to(cr, allocation.x + allocation.width - border_width, allocation.y + border_width);
	cairo_line_to(cr, allocation.x + border_width,                    allocation.y + border_width);
	cairo_close_path(cr);

	cairo_clip(cr);
	cairo_set_source_rgb(cr, priv->active_red, priv->active_green, priv->active_blue);
	cairo_paint(cr);
	cairo_destroy(cr);

	return FALSE;
}

static gchar *hid_to_beauty_keyname(guint8 hid) {
	gchar *text;

	text = ryos_hid_to_special_keyname(hid);
	if (text)
		return text;

	switch(hid) {
	case HID_UID_KB_ENTER:
		text = "\xe2\x86\xb5";
		break;
	case HID_UID_KB_ESCAPE:
		text = "ESC";
		break;
	case HID_UID_KB_BACKSPACE:
		text = "\xe2\x9f\xb5";
		break;
	case HID_UID_KB_TAB:
		text = "\xe2\x86\xb9";
		break;
	case HID_UID_KB_SPACE:
		text = "Space";
		break;
	case HID_UID_KB_CAPS_LOCK:
		text = "\xe2\x87\xa9";
		break;
	case HID_UID_KB_SCROLL_LOCK:
		text = "Scroll\nLock";
		break;
	case HID_UID_KB_PAGE_UP:
		text = "Pg\xe2\x86\x91";
		break;
	case HID_UID_KB_PAGE_DOWN:
		text = "Pg\xe2\x86\x93";
		break;
	case HID_UID_KB_RIGHT_ARROW:
		text = "\xe2\x86\x92";
		break;
	case HID_UID_KB_LEFT_ARROW:
		text = "\xe2\x86\x90";
		break;
	case HID_UID_KB_DOWN_ARROW:
		text = "\xe2\x86\x93";
		break;
	case HID_UID_KB_UP_ARROW:
		text = "\xe2\x86\x91";
		break;
	case HID_UID_KP_NUM_LOCK:
		text = "Num\nLock";
		break;
	case HID_UID_KP_DIV:
		text = "/\nKP";
		break;
	case HID_UID_KP_MUL:
		text = "*\nKP";
		break;
	case HID_UID_KP_MINUS:
		text = "-\nKP";
		break;
	case HID_UID_KP_PLUS:
		text = "+\nKP";
		break;
	case HID_UID_KP_ENTER:
		text = "Enter";
		break;
	case HID_UID_KP_1:
		text = "1\nKP";
		break;
	case HID_UID_KP_2:
		text = "2\nKP";
		break;
	case HID_UID_KP_3:
		text = "3\nKP";
		break;
	case HID_UID_KP_4:
		text = "4\nKP";
		break;
	case HID_UID_KP_5:
		text = "5\nKP";
		break;
	case HID_UID_KP_6:
		text = "6\nKP";
		break;
	case HID_UID_KP_7:
		text = "7\nKP";
		break;
	case HID_UID_KP_8:
		text = "8\nKP";
		break;
	case HID_UID_KP_9:
		text = "9\nKP";
		break;
	case HID_UID_KP_0:
		text = "0\nKP";
		break;
	case HID_UID_KB_LEFT_CONTROL:
		text = "Ctrl\nL";
		break;
	case HID_UID_KB_LEFT_SHIFT:
		text = "\xe2\x87\xa7\nL";
		break;
	case HID_UID_KB_LEFT_ALT:
		text = "Alt\nL";
		break;
	case HID_UID_KB_LEFT_GUI:
		text = "GUI\nL";
		break;
	case HID_UID_KB_RIGHT_CONTROL:
		text = "Ctrl\nR";
		break;
	case HID_UID_KB_RIGHT_SHIFT:
		text = "\xe2\x87\xa7\nR";
		break;
	case HID_UID_KB_RIGHT_ALT:
		text = "Alt\nR";
		break;
	case HID_UID_KB_RIGHT_GUI:
		text = "GUI\nR";
		break;
	default:
		break;
	}

	if (text)
		return g_strdup(text);
	else
		return gaminggear_hid_to_keyname(hid);
}

static void button_size_request_cb(GtkWidget *button, GtkRequisition *requisition, gpointer user_data) {
	requisition->width = requisition->height;
}

void ryos_key_button_set_intensity(RyosKeyButton *key_button, gfloat intensity) {
	key_button->priv->intensity = intensity;
	calculate_active_color(key_button);
}

void ryos_key_button_set_color(RyosKeyButton *key_button, GdkColor const *color) {
	key_button->priv->color = *color;
	calculate_active_color(key_button);
}

void ryos_key_button_set_light(RyosKeyButton *key_button, gboolean state) {
	key_button->priv->light_on = state;
	gtk_widget_queue_draw(key_button->priv->draw_area);
}

void ryos_key_button_set_label(RyosKeyButton *key_button, guint hid_usage_id) {
	gchar *label_text = hid_to_beauty_keyname(hid_usage_id);
	gtk_label_set_text(key_button->priv->label, label_text);
	g_free(label_text);
}

GtkWidget *ryos_key_button_new(RyosKeyButton *group, guint hid_usage_id) {
	RyosKeyButton *key_button;

	key_button = RYOS_KEY_BUTTON(g_object_new(RYOS_KEY_BUTTON_TYPE,
			"group", group,
			"draw_indicator", FALSE,
			NULL));

	g_signal_connect(G_OBJECT(key_button), "size-request", G_CALLBACK(button_size_request_cb), NULL);
	ryos_key_button_set_label(key_button, hid_usage_id);

	return GTK_WIDGET(key_button);
}

static void ryos_key_button_init(RyosKeyButton *key_button) {
	RyosKeyButtonPrivate *priv = RYOS_KEY_BUTTON_GET_PRIVATE(key_button);
	key_button->priv = priv;

	gdk_color_parse("black", &priv->color);
	priv->intensity = 1.0;
	priv->light_on = FALSE;

	priv->draw_area = gtk_alignment_new(0.5, 0.5, 0.0, 0.0);
	gtk_widget_set_size_request(GTK_WIDGET(priv->draw_area), 25, 25);
	g_signal_connect(G_OBJECT(priv->draw_area), "expose-event", G_CALLBACK(expose_cb), key_button);
	gtk_widget_show(priv->draw_area);

	priv->label = GTK_LABEL(gtk_label_new(NULL));
	gtk_label_set_justify(priv->label, GTK_JUSTIFY_CENTER);

	gtk_container_add(GTK_CONTAINER(priv->draw_area), GTK_WIDGET(priv->label));
	gtk_container_add(GTK_CONTAINER(key_button), priv->draw_area);
}

static void ryos_key_button_class_init(RyosKeyButtonClass *klass) {
	g_type_class_add_private(klass, sizeof(RyosKeyButtonPrivate));
}
