/*
 * 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 "ryosmkfx_effect_gui.h"
#include "ryosmkfx_light_layer.h"
#include "ryosmkfx_keyboard_selector.h"
#include "ryos_key_relations.h"
#include "ryos_device.h"
#include "g_roccat_helper.h"
#include <string.h>

#define RYOSMKFX_EFFECT_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RYOSMKFX_EFFECT_GUI_TYPE, RyosmkfxEffectGuiClass))
#define IS_RYOSMKFX_EFFECT_GUI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RYOSMKFX_EFFECT_GUI_TYPE))
#define RYOSMKFX_EFFECT_GUI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), RYOSMKFX_EFFECT_GUI_TYPE, RyosmkfxEffectGuiClass))
#define RYOSMKFX_EFFECT_GUI_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), RYOSMKFX_EFFECT_GUI_TYPE, RyosmkfxEffectGuiPrivate))

typedef struct _RyosmkfxEffectGuiClass RyosmkfxEffectGuiClass;
typedef struct _RyosmkfxEffectGuiPrivate RyosmkfxEffectGuiPrivate;
typedef struct _RyosmkfxEffectGuiPrivateData RyosmkfxEffectGuiPrivateData;

struct _RyosmkfxEffectGui {
	GObject parent;
	RyosmkfxEffectGuiPrivate *priv;
};

struct _RyosmkfxEffectGuiClass {
	GObjectClass parent_class;
};

struct _RyosmkfxEffectGuiPrivate {
	RyosmkfxEffectGuiPrivateData *data;
	RyosmkfxKeyboardSelector *keyboard_selector;
};

struct _RyosmkfxEffectGuiPrivateData {
	RyosmkfxLightLayerData data;
	RyosmkfxLightLayerData data_to_send;
};

static gboolean ryosmkfx_effect_client_iface_init(RyosEffectClient *self, gpointer client_data, GError **error) {
	RyosmkfxEffectGuiPrivate *priv = RYOSMKFX_EFFECT_GUI(self)->priv;

	priv->keyboard_selector = RYOSMKFX_KEYBOARD_SELECTOR(client_data);
	g_object_ref(G_OBJECT(priv->keyboard_selector));

	return TRUE;
}

static gboolean ryosmkfx_effect_client_iface_deinit(RyosEffectClient *self, GError **error) {
	RyosmkfxEffectGuiPrivate *priv = RYOSMKFX_EFFECT_GUI(self)->priv;

	g_clear_object(&priv->keyboard_selector);

	return TRUE;
}

static gboolean ryosmkfx_effect_client_iface_activate(RyosEffectClient *self, GError **error) {
	/* RyosMKFX doesn't show stored layer settings while effect is inactive,
	 * so there's no need
	 */
	return TRUE;
}

static gboolean ryosmkfx_effect_client_iface_deactivate(RyosEffectClient *self, GError **error) {
	return TRUE;
}

static gboolean idle_callback(gpointer data) {
	RyosmkfxEffectGuiPrivate *priv = RYOSMKFX_EFFECT_GUI(data)->priv;
	/* gdk_thread lock is held here */
	ryosmkfx_keyboard_selector_effect_show(priv->keyboard_selector, &priv->data->data_to_send);
	return FALSE; /* G_SOURCE_REMOVE */
}

static gboolean ryosmkfx_effect_client_iface_send(RyosEffectClient *self, GError **error) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	memcpy(&data->data_to_send, &data->data, sizeof(RyosmkfxLightLayerData));
	gdk_threads_add_idle(idle_callback, self);
	/* keeping speed in sync with hardware timing */
	g_usleep(RYOS_WRITE_CHECK_WAIT_LIGHT_CONTROL * G_ROCCAT_USEC_PER_MSEC);
	return TRUE;
}

static void ryosmkfx_effect_client_iface_set_key_state(RyosEffectClient *self, guint index, gboolean on) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key = &data->data.keys[ryos_sdk_index_to_light_index(index)];
	key->state = on;
}

static void ryosmkfx_effect_client_iface_set_state(RyosEffectClient *self, gboolean on) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key;
	guint key_index;

	for (key_index = 0; key_index < RYOSMKFX_LIGHT_LAYER_KEYS_NUM; ++key_index) {
		key = &data->data.keys[key_index];
		key->state = on;
	}
}

static void ryosmkfx_effect_client_iface_set_key_color(RyosEffectClient *self, guint index, guint red, guint green, guint blue) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key = &data->data.keys[ryos_sdk_index_to_light_index(index)];
	key->red = red;
	key->green = green;
	key->blue = blue;
}

static void ryosmkfx_effect_client_iface_set_color(RyosEffectClient *self, guint red, guint green, guint blue) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key;
	guint key_index;

	for (key_index = 0; key_index < RYOSMKFX_LIGHT_LAYER_KEYS_NUM; ++key_index) {
		key = &data->data.keys[key_index];
		key->red = red;
		key->green = green;
		key->blue = blue;
	}
}

static gboolean ryosmkfx_effect_client_iface_get_key_state(RyosEffectClient *self, guint index) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key = &data->data.keys[ryos_sdk_index_to_light_index(index)];
	return key->state;
}

static guint ryosmkfx_effect_client_iface_get_key_red(RyosEffectClient *self, guint index) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key = &data->data.keys[ryos_sdk_index_to_light_index(index)];
	return key->red;
}

static guint ryosmkfx_effect_client_iface_get_key_green(RyosEffectClient *self, guint index) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key = &data->data.keys[ryos_sdk_index_to_light_index(index)];
	return key->green;
}

static guint ryosmkfx_effect_client_iface_get_key_blue(RyosEffectClient *self, guint index) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	RyosmkfxLightLayerKey *key = &data->data.keys[ryos_sdk_index_to_light_index(index)];
	return key->blue;
}

static gboolean ryosmkfx_effect_client_iface_get_all_off(RyosEffectClient *self) {
	RyosmkfxEffectGuiPrivateData *data = RYOSMKFX_EFFECT_GUI(self)->priv->data;
	return ryosmkfx_light_layer_data_are_all_off(&data->data);
}

static void ryosmkfx_effect_gui_init_client_iface(RyosEffectClientInterface *iface) {
	iface->init = ryosmkfx_effect_client_iface_init;
	iface->deinit = ryosmkfx_effect_client_iface_deinit;
	iface->activate = ryosmkfx_effect_client_iface_activate;
	iface->deactivate = ryosmkfx_effect_client_iface_deactivate;
	iface->set_key_state = ryosmkfx_effect_client_iface_set_key_state;
	iface->set_state = ryosmkfx_effect_client_iface_set_state;
	iface->get_key_state = ryosmkfx_effect_client_iface_get_key_state;
	iface->set_key_color = ryosmkfx_effect_client_iface_set_key_color;
	iface->set_color = ryosmkfx_effect_client_iface_set_color;
	iface->get_key_red = ryosmkfx_effect_client_iface_get_key_red;
	iface->get_key_green = ryosmkfx_effect_client_iface_get_key_green;
	iface->get_key_blue = ryosmkfx_effect_client_iface_get_key_blue;
	iface->get_all_off = ryosmkfx_effect_client_iface_get_all_off;
	iface->send = ryosmkfx_effect_client_iface_send;
	iface->is_emulator = TRUE;
}

G_DEFINE_TYPE_WITH_CODE(RyosmkfxEffectGui, ryosmkfx_effect_gui, G_TYPE_OBJECT,
		G_IMPLEMENT_INTERFACE(RYOS_EFFECT_CLIENT_TYPE, ryosmkfx_effect_gui_init_client_iface));

RyosmkfxEffectGui *ryosmkfx_effect_gui_new(void) {
	return RYOSMKFX_EFFECT_GUI(g_object_new(RYOSMKFX_EFFECT_GUI_TYPE, NULL));
}

static void ryosmkfx_effect_gui_init(RyosmkfxEffectGui *self) {
	RyosmkfxEffectGuiPrivate *priv = RYOSMKFX_EFFECT_GUI_GET_PRIVATE(self);
	self->priv = priv;
	priv->data = (RyosmkfxEffectGuiPrivateData *)g_malloc(sizeof(RyosmkfxEffectGuiPrivateData));
}

static void finalize(GObject *object) {
	RyosmkfxEffectGuiPrivate *priv = RYOSMKFX_EFFECT_GUI(object)->priv;
	g_free(priv->data);
	G_OBJECT_CLASS(ryosmkfx_effect_gui_parent_class)->finalize(object);
}

static void ryosmkfx_effect_gui_class_init(RyosmkfxEffectGuiClass *klass) {
	GObjectClass *gobject_class = (GObjectClass*)klass;
	gobject_class->finalize = finalize;
	g_type_class_add_private(klass, sizeof(RyosmkfxEffectGuiPrivate));
}
