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

void g_roccat_list_free_full(GList *list, GDestroyNotify freefunc) {
#if (GLIB_CHECK_VERSION(2, 28, 0))
	g_list_free_full(list, freefunc);
#else
	g_list_foreach(list, (GFunc)freefunc, NULL);
	g_list_free(list);
#endif
}

GList *g_roccat_list_find_custom_backwards(GList *list, gconstpointer data, GCompareFunc func) {
	GList *element;
	element = g_list_last(list);
	while (element) {
		if (func(element->data, data) == 0)
			return element;
		element = g_list_previous(element);
	}

	return NULL;
}

/*
 * For this implementation to be a stable sort its important that:
 * - pivot is the last element of the list
 * - all elements smaller AND EQUAL are put in lesser list
 */
GList *g_roccat_list_stable_sort(GList *list, GCompareFunc compare_func) {
	GList *lesser = NULL;
	GList *greater = NULL;
	GList *pivot;
	GList *element;
	guint length;

	length = g_list_length(list);

	if (length <= 1)
		return list;

	pivot = g_list_last(list);
	list = g_list_remove_link(list, pivot);

	while (list) {
		element = list;
		list = g_list_remove_link(list, element);

		if (compare_func(element->data, pivot->data) <= 0)
			lesser = g_list_concat(lesser, element);
		else
			greater = g_list_concat(greater, element);
	}

	list = g_list_concat(g_roccat_list_stable_sort(lesser, compare_func), pivot);
	list = g_list_concat(list, g_roccat_list_stable_sort(greater, compare_func));
	return list;
}

gpointer roccat_key_file_get_binary(GKeyFile *key_file, gchar const *group_name, gchar const *key, gsize size, GError **error) {
	gchar *string, *source;
	gpointer result;
	guint8 *target;
	char hex[3] = { 0, 0, 0 };
	guint i;

	string = g_key_file_get_value(key_file, group_name, key, error);

	if (!string)
		return NULL;

	if (strlen(string) != size * 2) {
		g_set_error(error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, "Data has wrong length %lu instead of %lu", (gulong)strlen(string), (gulong)(size * 2));
		g_free(string);
		return NULL;
	}

	source = string;

	result = g_malloc(size);
	target = result;

	for (i = 0; i < size; ++i, ++target) {
		hex[0] = *source++;
		hex[1] = *source++;
		*target = strtoul(hex, NULL, 16);
	}

	g_free(string);
	return result;
}

void roccat_key_file_set_binary(GKeyFile *key_file, gchar const *group_name, gchar const *key, gconstpointer data, gsize size) {
	gchar *string, *new_string, *element;
	guint8 const *source = data;
	guint i;

	string = g_malloc(1);
	*string = 0;

	for (i = 0; i < size; ++i, ++source) {
		element = g_strdup_printf("%02X", *source);
		new_string = g_strconcat(string, element, NULL);
		g_free(string);
		g_free(element);
		string = new_string;
	}

	g_key_file_set_value(key_file, group_name, key, string);
	g_free(string);
}

GKeyFile *roccat_key_file_dup(GKeyFile *src) {
	GKeyFile *dest;
	gchar *data;
	gsize length;

	dest = g_key_file_new();
	data = g_key_file_to_data(src, &length, NULL);

	if (!g_key_file_load_from_data(dest, data, length, G_KEY_FILE_NONE, NULL)) {
		g_key_file_free(dest);
		dest = NULL;
	}

	g_free(data);
	return dest;
}
