/*
 * 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 "lua_eventhandler.h"
#include "lua_dbus_server.h"
#include "roccat_process_helper.h"
#include "lua.h"
#include "config.h"
#include <glib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define LUA_EVENTHANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), LUA_EVENTHANDLER_TYPE, LuaEventhandlerClass))
#define IS_LUA_EVENTHANDLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), LUA_EVENTHANDLER_TYPE))
#define LUA_EVENTHANDLER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), LUA_EVENTHANDLER_TYPE, LuaEventhandlerPrivate))

typedef struct _LuaEventhandlerClass LuaEventhandlerClass;

struct _LuaEventhandlerClass {
	GObjectClass parent_class;
};

struct _LuaEventhandlerPrivate {
	RoccatEventhandlerHost *host;
	LuaDBusServer *dbus_server;
	RoccatDeviceScanner *device_scanner;
	RoccatDevice *device;
	gboolean device_set_up;
	RoccatKeyFile *config;
};

enum {
	PROP_0,
	PROP_HOST,
};

static guint lua_eventhandler_get_talk_device(RoccatEventhandlerPlugin *self) {
	return 0;
}

static void lua_roccat_eventhandler_interface_init(RoccatEventhandlerPluginInterface *iface) {
	iface->get_talk_device = lua_eventhandler_get_talk_device;
	iface->talk_easyshift = NULL;
	iface->talk_easyshift_lock = NULL;
	iface->talk_easyaim = NULL;
	iface->set_led_rgb = NULL;
	iface->restore_led_rgb = NULL;
}

G_DEFINE_TYPE_WITH_CODE(LuaEventhandler, lua_eventhandler, G_TYPE_OBJECT,
		G_IMPLEMENT_INTERFACE(ROCCAT_EVENTHANDLER_PLUGIN_TYPE, lua_roccat_eventhandler_interface_init));

GObject *lua_eventhandler_new(RoccatEventhandlerHost *host) {
	return g_object_new(LUA_EVENTHANDLER_TYPE,
			"host", host,
			NULL);
}

static void open_gui_cb(gpointer source, gpointer user_data) {
	gchar *path;
	path = g_build_path("/", INSTALL_PREFIX, "bin", "luaconfig", (gchar const *)NULL);
	roccat_double_fork(path);
	g_free(path);
}

static void configuration_reload(LuaEventhandler *eventhandler) {
	LuaEventhandlerPrivate *priv = eventhandler->priv;
	if (priv->config)
		lua_configuration_free(priv->config);
	priv->config = lua_configuration_load();
}

static void device_add_cb(RoccatDeviceScannerInterface *interface, RoccatDevice *device, gpointer user_data) {
	LuaEventhandler *eventhandler = LUA_EVENTHANDLER(user_data);
	LuaEventhandlerPrivate *priv = eventhandler->priv;

	if (priv->device) {
		g_critical("Multiple devices present.");
		return;
	}

	priv->device = device;
	g_object_ref(G_OBJECT(device));
	roccat_device_debug(device);

	configuration_reload(eventhandler);

	// FIXME create in init and just disconnect/reconnect?
	priv->dbus_server = lua_dbus_server_new();
	lua_dbus_server_connect(priv->dbus_server);
	g_signal_connect(G_OBJECT(priv->dbus_server), "open-gui", G_CALLBACK(open_gui_cb), eventhandler);

	priv->device_set_up = TRUE;
}

static void stop(LuaEventhandler *eventhandler) {
	LuaEventhandlerPrivate *priv = eventhandler->priv;

	priv->device_set_up = FALSE;

	g_object_unref(priv->dbus_server);
	priv->dbus_server = NULL;

	if (priv->device) {
		g_object_unref(G_OBJECT(priv->device));
		priv->device = NULL;
	}

	lua_configuration_free(priv->config);
	priv->config = NULL;
}

static void device_remove_cb(RoccatDeviceScanner *scanner, RoccatDevice *device, gpointer user_data) {
	LuaEventhandler *eventhandler = LUA_EVENTHANDLER(user_data);
	LuaEventhandlerPrivate *priv = eventhandler->priv;
	if (priv->device && gaminggear_device_equal(GAMINGGEAR_DEVICE(device), GAMINGGEAR_DEVICE(priv->device)))
		stop(eventhandler);
}

static void lua_eventhandler_init(LuaEventhandler *eventhandler) {
	eventhandler->priv = LUA_EVENTHANDLER_GET_PRIVATE(eventhandler);
}

static void finalize(GObject *object) {
	LuaEventhandler *eventhandler = LUA_EVENTHANDLER(object);
	LuaEventhandlerPrivate *priv = eventhandler->priv;

	if (priv->device_scanner) {
		g_object_unref(G_OBJECT(priv->device_scanner));
		priv->device_scanner = NULL;
	}

	stop(eventhandler);

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

static GObject *constructor(GType gtype, guint n_properties, GObjectConstructParam *properties) {
	LuaEventhandler *eventhandler;
	LuaEventhandlerPrivate *priv;
	GObject *obj;

	obj = G_OBJECT_CLASS(lua_eventhandler_parent_class)->constructor(gtype, n_properties, properties);
	eventhandler = LUA_EVENTHANDLER(obj);
	priv = eventhandler->priv;

	priv->device_scanner = lua_device_scanner_new();
	g_signal_connect(G_OBJECT(priv->device_scanner), "device-added", G_CALLBACK(device_add_cb), eventhandler);
	g_signal_connect(G_OBJECT(priv->device_scanner), "device-removed", G_CALLBACK(device_remove_cb), eventhandler);
	roccat_device_scanner_interface_start(ROCCAT_DEVICE_SCANNER_INTERFACE(priv->device_scanner));

	return obj;
}

static void set_property(GObject *object, guint prop_id, GValue const *value, GParamSpec *pspec) {
	LuaEventhandlerPrivate *priv = LUA_EVENTHANDLER(object)->priv;

	switch(prop_id) {
	case PROP_HOST:
		priv->host = g_value_get_pointer(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
	}
}

static void lua_eventhandler_class_init(LuaEventhandlerClass *klass) {
	GObjectClass *gobject_class;

	gobject_class = G_OBJECT_CLASS(klass);

	gobject_class->constructor = constructor;
	gobject_class->finalize = finalize;
	gobject_class->set_property = set_property;

	g_type_class_add_private(klass, sizeof(LuaEventhandlerPrivate));

	g_object_class_install_property(gobject_class, PROP_HOST,
			g_param_spec_pointer("host",
					"Host",
					"Pointer to RoccatEventhandlerHost interface",
					G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
}

ROCCAT_EVENTHANDLER_PLUGIN_API RoccatEventhandlerPlugin *roccat_eventhandler_plugin_new(RoccatEventhandlerHost *host) {
	return ROCCAT_EVENTHANDLER_PLUGIN(lua_eventhandler_new(host));
}
