/*
 * 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 <stdio.h>
#include <sys/stat.h>
#include <errno.h>
#include "roccat_helper.h"

static gboolean roccat_create_dir_if_needed_with_mode(gchar const *dir, int mode, GError **error) {
	if (!g_file_test(dir, G_FILE_TEST_IS_DIR)) {
		if (g_mkdir_with_parents(dir, mode)) {
			g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno), "Error creating directory: %s", g_strerror(errno));
			return FALSE;
		}
	}
	return TRUE;
}

gboolean roccat_create_dir_if_needed(gchar const *dir, GError **error) {
	return roccat_create_dir_if_needed_with_mode(dir, 0700, error);
}

gchar *roccat_read_from_file(gchar const *filename, gsize length, GError **error) {
	gchar *result;
	gsize bytes;

	if (!g_file_get_contents(filename, &result, &bytes, error)) {
		return NULL;
	}

	if (length && bytes != length) {
		g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_INVAL, "size of read data is invalid (%zu instead of %zu)", bytes, length);
		g_free(result);
		return NULL;
	}

	return result;
}

gchar *roccat_profile_dir(void) {
	return g_build_path("/", "/var", "lib", "roccat", NULL);
}

gboolean roccat_profile_dir_create_if_needed(gchar const *dir, GError **error) {
	mode_t mask;
	gboolean retval;

	mask = umask(S_IROTH | S_IWOTH | S_IXOTH);
	retval = roccat_create_dir_if_needed_with_mode(dir, 0770, error);
	(void)umask(mask);

	return retval;
}

gboolean roccat_profile_write_with_path(gchar const *filename, gchar const *data, gsize length, GError **error) {
	mode_t mask;
	gboolean retval;

	mask = umask(S_IROTH | S_IWOTH | S_IXOTH);
	retval = g_file_set_contents(filename, data, length, error);
	(void)umask(mask);

	return retval;
}

gchar *roccat_profile_filename_proposition(gchar const *profile_name, guint profile_index, gchar const *extension) {
	gchar *filename;

	if (profile_name != 0 && *profile_name != 0)
		filename = g_strdup_printf("%s.%s", profile_name, extension);
	else
		filename = g_strdup_printf("Profile%u.%s", profile_index + 1, extension);
	return filename;
}

gchar *roccat_create_filename_with_extension(gchar const *filename, gchar const *extension) {
	gchar *result;
	gchar *suffix;

	suffix = g_strconcat(".", extension, NULL);

	if (g_str_has_suffix(filename, suffix))
		result = g_strdup(filename);
	else
		result = g_strconcat(filename, suffix, NULL);

	g_free(suffix);

	return result;
}

void roccat_set_bit(guint8 *byte, guint bit, gboolean value) {
	g_assert(bit < 8);
	if (value)
		*byte |= 1 << bit;
	else
		*byte &= ~(1 << bit);
}

gboolean roccat_get_bit(guint8 byte, guint bit) {
	g_assert(bit < 8);
	return (byte & (1 << bit)) ? TRUE : FALSE;
}

void roccat_bitfield_set_bit(guint8 *array, gsize bit, gboolean value) {
	gsize byte_offset;
	guint bit_in_byte;

	byte_offset = bit >> 3; /* /8 */
	bit_in_byte = bit & 7; /* %8 */

	roccat_set_bit(array + byte_offset, bit_in_byte, value);
}

gboolean roccat_bitfield_get_bit(guint8 const *array, gsize bit) {
	gsize byte_offset;
	guint bit_in_byte;

	byte_offset = bit >> 3; /* /8 */
	bit_in_byte = bit & 7; /* %8 */

	return roccat_get_bit(*(array + byte_offset), bit_in_byte);
}

gulong roccat_calc_bytesum(gconstpointer data, gsize size) {
	guchar const *array = (guchar const *)data;
	gsize i;
	gulong result = 0;

	for (i = 0; i < size; ++i)
		result += array[i];

	return result;
}

#if (GLIB_CHECK_VERSION(2, 34, 0))
G_DEFINE_QUARK(roccat-error-quark, roccat_error);
#else
GQuark roccat_error_quark(void) {
	static GQuark q;
	if G_UNLIKELY (q == 0)
		q = g_quark_from_static_string("roccat-error-quark");
	return q;
}
#endif

gchar *roccat_data_to_string(guchar const *data, gsize length) {
	gchar *buffer;
	gsize i;
	guint n;

	if (length == 0)
		return g_strdup("");

	buffer = (gchar *)g_malloc(length * 3);

	n = sprintf(buffer, "%02x", data[0]);
	for (i = 1; i < length; ++i)
		n += sprintf(buffer + n, " %02x", data[i]);

	return buffer;
}
