/*
 * 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 "ryos_ripple_thread.h"
#include "g_roccat_helper.h"
#include "g_thread_roccat_helper.c"
#include <string.h>

#define RYOS_RIPPLE_THREAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), RYOS_RIPPLE_THREAD_TYPE, RyosRippleThreadClass))
#define IS_RYOS_RIPPLE_THREAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), RYOS_RIPPLE_THREAD_TYPE))
#define RYOS_RIPPLE_THREAD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), RYOS_RIPPLE_THREAD_TYPE, RyosRippleThreadPrivate))

typedef struct _RyosRippleThreadClass RyosRippleThreadClass;
typedef struct _RyosRippleThreadPrivate RyosRippleThreadPrivate;

struct _RyosRippleThread {
	GObject parent;
	RyosRippleThreadPrivate *priv;
};

struct _RyosRippleThreadClass {
	GObjectClass parent_class;
};

struct _RyosRippleThreadPrivate {
	GThread *thread;

	gint cancelled;
	gint running;

	RoccatDevice *ryos;
	guint8 hid;
};

enum {
	UWAIT = 10 * G_ROCCAT_USEC_PER_MSEC,
};

G_DEFINE_TYPE(RyosRippleThread, ryos_ripple_thread, G_TYPE_OBJECT);

static void ryos_ripple_thread_set_running(RyosRippleThread *ripple_thread, gboolean value) {
	g_atomic_int_set(&ripple_thread->priv->running, value);
}

gboolean ryos_ripple_thread_get_running(RyosRippleThread *ripple_thread) {
	return g_atomic_int_get(&ripple_thread->priv->running);
}

void ryos_ripple_thread_set_cancelled(RyosRippleThread *ripple_thread) {
	g_atomic_int_set(&ripple_thread->priv->cancelled, TRUE);
}

gboolean ryos_ripple_thread_get_cancelled(RyosRippleThread *ripple_thread) {
	return g_atomic_int_get(&ripple_thread->priv->cancelled);
}

static gpointer thread(gpointer user_data) {
	RyosRippleThread *ripple_thread = RYOS_RIPPLE_THREAD(user_data);
	RyosRippleThreadPrivate *priv = ripple_thread->priv;
	RyosCustomLights custom_lights;
	guint radius;
	GError *local_error = NULL;
	guint center_row;
	guint center_col;

	if (!ryos_ripple_get_center(priv->hid, &center_row, &center_col)) {
		g_warning("ripple_thread: couldn't find a key for hid 0x%02x", priv->hid);
		goto exit;
	}

	if (!ryos_light_control_custom(priv->ryos, TRUE, &local_error)) {
		g_warning("ripple_thread: couldn't init custom: %s", local_error->message);
		g_clear_error(&local_error);
		goto exit;
	}

	memset(&custom_lights.data, 0, 15);
	radius = 1;

	while (TRUE) {
		if (!ryos_ripple_draw_circle(&custom_lights, center_row, center_col, radius, TRUE))
			break;

		if (!ryos_custom_lights_write(priv->ryos, &custom_lights, &local_error)) {
			g_warning("ripple_thread: couldn't write data: %s", local_error->message);
			g_clear_error(&local_error);
			break;
		}

		g_usleep(UWAIT);

		if (ryos_ripple_thread_get_cancelled(ripple_thread))
			break;

		ryos_ripple_draw_circle(&custom_lights, center_row, center_col, radius - 1 , FALSE);

		if (ryos_custom_lights_empty(&custom_lights))
			break;

		if (!ryos_custom_lights_write(priv->ryos, &custom_lights, &local_error)) {
			g_warning("ripple_thread: couldn't write data: %s", local_error->message);
			g_clear_error(&local_error);
			break;
		}

		g_usleep(UWAIT);

		if (ryos_ripple_thread_get_cancelled(ripple_thread))
			break;

		++radius;
	}

	if (!ryos_light_control_custom(priv->ryos, FALSE, &local_error)) {
		g_warning("ripple_thread: couldn't deinit custom: %s", local_error->message);
		g_clear_error(&local_error);
	}
exit:
	ryos_ripple_thread_set_running(ripple_thread, FALSE);
	return NULL;
}

RyosRippleThread *ryos_ripple_thread_new(RoccatDevice *ryos, guint8 hid) {
	GObject *object;
	RyosRippleThread *ripple_thread;
	RyosRippleThreadPrivate *priv;
	GError *error = NULL;

	object = g_object_new(RYOS_RIPPLE_THREAD_TYPE, NULL);
	ripple_thread = RYOS_RIPPLE_THREAD(object);
	priv = ripple_thread->priv;

	g_atomic_int_set(&ripple_thread->priv->cancelled, FALSE);
	ryos_ripple_thread_set_running(ripple_thread, TRUE);
	priv->ryos = ryos;
	priv->hid = hid;

	priv->thread = g_roccat_thread_try_new("RyosRipple", thread, ripple_thread, &error);

	if (error) {
		g_warning("Can't create ripple thread: %s", error->message);
		g_clear_error(&error);
	}

	return ripple_thread;
}

static void ryos_ripple_thread_init(RyosRippleThread *ripple_thread) {
	RyosRippleThreadPrivate *priv = RYOS_RIPPLE_THREAD_GET_PRIVATE(ripple_thread);
	ripple_thread->priv = priv;
}

static void ryos_ripple_thread_finalize(GObject *object) {
	RyosRippleThreadPrivate *priv = RYOS_RIPPLE_THREAD(object)->priv;

	if (priv->thread)
		g_thread_join(priv->thread);

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

static void ryos_ripple_thread_class_init(RyosRippleThreadClass *klass) {
	GObjectClass *gobject_class;

	gobject_class = (GObjectClass*)klass;
	gobject_class->finalize = ryos_ripple_thread_finalize;

	g_type_class_add_private(klass, sizeof(RyosRippleThreadPrivate));
}
