#ifndef _ROCCAT_H_
#define _ROCCAT_H_

/*
 * 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/>.
 */

/*!
 * \file roccat.h
 * \brief definitions for libroccat
 * \author Stefan Achatz
 */

#include <glib.h>
#include <linux/ioctl.h>

G_BEGIN_DECLS

#define USB_VENDOR_ID_ROCCAT 0x1e7d

#define ROCCATIOCGREPSIZE _IOR('H', 0xf1, int)

typedef struct _RoccatMacroKeystroke RoccatMacroKeystroke;
typedef struct _RoccatMacroKeystrokes RoccatMacroKeystrokes;
typedef struct _RoccatMacro RoccatMacro;
typedef struct _RoccatMacros RoccatMacros;
typedef struct _RoccatOsdNote RoccatOsdNote;
typedef struct _RoccatNotificator RoccatNotificator;
typedef struct _RoccatNotificationProfile RoccatNotificationProfile;
typedef struct _RoccatNotificationCpi RoccatNotificationCpi;
typedef struct _RoccatNotificationSensitivity RoccatNotificationSensitivity;
typedef struct _RoccatNotificationTimer RoccatNotificationTimer;
typedef struct _RoccatNotificationLiveRecording RoccatNotificationLiveRecording;
typedef struct _RoccatDevice RoccatDevice;
typedef struct _RoccatTimer RoccatTimer;
typedef struct _RoccatTimers RoccatTimers;
typedef struct _RoccatDeviceScanner RoccatDeviceScanner;
typedef struct _RoccatKeyFile RoccatKeyFile;

struct _RoccatMacroKeystroke {
	guint8 key;
	guint8 action;
	guint16 period;
} __attribute__ ((packed));

typedef enum {
	ROCCAT_MACRO_KEYSTROKE_KEY_BUTTON_LEFT = 0xf0,
	ROCCAT_MACRO_KEYSTROKE_KEY_BUTTON_RIGHT = 0xf1,
	ROCCAT_MACRO_KEYSTROKE_KEY_BUTTON_MIDDLE = 0xf2,
} RoccatMacroKeystrokeKey;

typedef enum {
	ROCCAT_MACRO_KEYSTROKE_ACTION_PRESS = 0,
	ROCCAT_MACRO_KEYSTROKE_ACTION_RELEASE = 1
} RoccatMacroKeystrokeAction;

enum {
	ROCCAT_MACRO_KEYSTROKES_NUM = 512,
	ROCCAT_MACRO_KEYSTROKE_PERIOD_MAX = G_MAXUINT16,
};

/*!
 * \brief unified button info for roccat_macro.ini
 */
struct _RoccatMacroKeystrokes {
	guint16 count;
	RoccatMacroKeystroke keystrokes[ROCCAT_MACRO_KEYSTROKES_NUM];
	guint8 loop;
} __attribute__ ((packed));

struct _RoccatMacro {
	gchar *macro_set, *macro;
	RoccatMacroKeystrokes keystrokes;
};

struct _RoccatMacros;

typedef enum {
	ROCCAT_INPUT_EVENT_VALUE_PRESS = 1,
	ROCCAT_INPUT_EVENT_VALUE_RELEASE = 0,
} RoccatInputEventValue;

struct _RoccatTimer {
	gchar *name;
	guint32 seconds;
};

struct _RoccatTimers;

/*!
 * \defgroup macro Macros
 */

RoccatMacroKeystrokes *roccat_macro_keystrokes_new(void);
void roccat_macro_keystrokes_free(RoccatMacroKeystrokes *keystrokes);
void roccat_macro_keystrokes_copy(RoccatMacroKeystrokes const *from, RoccatMacroKeystrokes *to);
RoccatMacroKeystrokes *roccat_macro_keystrokes_dup(RoccatMacroKeystrokes const *old);
gboolean roccat_macro_keystrokes_add(RoccatMacroKeystrokes *keystrokes, RoccatMacroKeystroke const *keystroke);
void roccat_macro_keystrokes_clear(RoccatMacroKeystrokes *keystrokes);

/*!
 * \ingroup macro
 * \brief sets macro set name
 */
void roccat_macro_set_macro_set_name(RoccatMacro *macro, gchar const *new_name);

/*!
 * \ingroup macro
 * \brief sets macro name
 */
void roccat_macro_set_macro_name(RoccatMacro *macro, gchar const *new_name);

/*!
 * \ingroup macro
 * \brief get complete name in format macroset/macro
 */
gchar *roccat_macro_get_name_joined(RoccatMacro const *macro);

RoccatMacroKeystrokes *roccat_macro_get_keystrokes(RoccatMacro *macro);

/*!
 * \ingroup macro
 * \brief add keystroke to macro
 * \retval If successful \c TRUE is returned.
 *         \c FALSE is returned if number of keystrokes is exceeded and keystroke was not added.
 */
gboolean roccat_macro_add_keystroke(RoccatMacro *macro, RoccatMacroKeystroke const *keystroke);

/*!
 * \ingroup macro
 * \brief creates new macro
 *
 * \e keystrokes can be \c NULL. In this case an empty macro is created.
 *
 * \retval macro
 */
RoccatMacro *roccat_macro_new(gchar const *macro_set_name, gchar const *macro_name, RoccatMacroKeystrokes const *keystrokes);

/*!
 * \ingroup macro
 * \brief duplicates a macro
 * \retval macro that has to be freed with \c roccat_macro_free()
 */
RoccatMacro *roccat_macro_dup(RoccatMacro const *roccat_macro);

/*!
 * \ingroup macro
 * \brief has macro mouse keystrokes
 * \retval \c TRUE if macro has mouse keystroke, \c FALSE else.
 */
gboolean roccat_macro_has_mouse_keystroke(RoccatMacro const *roccat_macro);

/*!
 * \ingroup macro
 * \brief TODO doc
 */
void roccat_macro_free(RoccatMacro *macro);

/*!
 * \ingroup macro
 * \brief created new empty macros
 * \retval key file that has to be freed with \c roccat_macros_free()
 */
RoccatMacros *roccat_macros_new(void);

/*!
 * \ingroup macro
 * \brief load macro file
 * Returns key file even if file does not exist
 * \retval key file that has to be freed with \c roccat_macros_free()
 */
RoccatMacros *roccat_macros_load(void);

/*!
 * \ingroup macro
 * \brief save macro file to disk
 * \retval If successful returns +TRUE+. On failure, returns +FALSE+ with +error+ set
 */
gboolean roccat_macros_save(RoccatMacros *roccat_macros, GError **error);

/*!
 * \ingroup macro
 * \brief frees macro file
 */
void roccat_macros_free(RoccatMacros *key_file);

/*!
 * \ingroup macro
 * \brief gets macro
 */
RoccatMacro *roccat_macros_get(RoccatMacros *roccat_macros, gchar const *macro_set_name, gchar const *macro_name, GError **error);

/*!
 * \ingroup macro
 * \brief sets macro
 */
void roccat_macros_set(RoccatMacros *roccat_macros, RoccatMacro const *macro);

/*!
 * \ingroup macro
 * \brief returns array of macro set names
 * \retval NULL-terminated array of strings
 *
 * Use g_strfreev() to free array
 */
gchar **roccat_macros_get_macro_sets(RoccatMacros *roccat_macros, gsize *length);

/*!
 * \ingroup macro
 * \brief returns array of macro names for macro set
 * \retval NULL-terminated array of strings
 *
 * Use \c g_strfreev() to free array
 */
gchar **roccat_macros_get_macros(RoccatMacros *roccat_macros, gchar const *macro_set_name, gsize *length, GError **error);

RoccatTimers *roccat_timers_new(void);
void roccat_timers_free(RoccatTimers *timers);
RoccatTimers *roccat_timers_load(GError **error);
gboolean roccat_timers_save(RoccatTimers *timers, GError **error);
RoccatTimer *roccat_timers_get(RoccatTimers *timers, gchar const *timer_name, GError **error);
void roccat_timers_set(RoccatTimers *timers, RoccatTimer const *timer);
gchar **roccat_timers_get_timer_names(RoccatTimers *timers, gsize *length, GError **error);

/*!
 * \defgroup device Roccat device
 */

/*!
 * \ingroup device
 * \brief returns first device that is handled by given kernel module
 * \retval Pointer to device that has to be freed with \c roccat_device_free() or \c NULL.
 *
 * Needs g_type_init() to be called before.
 * Function does not block and returns only devices already plugged in.
 */
RoccatDevice *roccat_device_first(gchar const *driver_name, guint const *device_ids);

/*
 * \retval \c TRUE if \c device is unused and should be freed
 */
typedef gboolean (*roccat_device_scanner_callback)(RoccatDevice *device, gpointer user_data);

/*
 * \ingroup device
 * \retval Pointer to scanner that has to be freed with \c roccat_device_scanner_free().
 */
RoccatDeviceScanner *roccat_device_scanner_new(gchar const *driver_name, guint const *device_ids,
		roccat_device_scanner_callback add_callback, roccat_device_scanner_callback remove_callback,
		gpointer user_data);

/*
 * \ingroup device
 */
void roccat_device_scanner_free(RoccatDeviceScanner *scanner);

RoccatDevice *roccat_device_new(gchar const *identifier);
void roccat_device_free(RoccatDevice *roccat_dev);

void roccat_device_lock(RoccatDevice *roccat_dev);
void roccat_device_unlock(RoccatDevice *roccat_dev);

/*!
 * \ingroup device
 * \brief returns if given device has been removed
 * \retval TRUE if device has been removed, FALSE on error
 */
gboolean roccat_device_monitor_remove(RoccatDevice const *roccat_device, gboolean wait);

/*!
 * \ingroup device
 * \brief returns product id
 * \retval Product id.
 */
guint roccat_device_get_product_id(RoccatDevice const *roccat_dev);
void roccat_device_set_product_id(RoccatDevice *roccat_dev, guint product_id);

/*!
 * \ingroup device
 * \brief returns driver name
 * \retval Pointer to driver name.
 */
gchar const *roccat_device_get_drivername(RoccatDevice const *roccat_dev);
void roccat_device_set_drivername(RoccatDevice *roccat_dev, gchar const *drivername);

/*!
 * \ingroup device
 * \brief returns unique udev sysname
 * \retval Pointer to sysname.
 */
gchar const *roccat_device_get_identifier(RoccatDevice const *roccat_dev);

/*!
 * \ingroup device
 * \brief get sysfs path
 * This folder contains all the sysfs attributes of the device.
 * \retval Pointer to absolute path of sysfs.
 */
gchar const *roccat_device_get_syspath(RoccatDevice const *roccat_dev);
void roccat_device_set_syspath(RoccatDevice *roccat_dev, gchar const *syspath);

/*!
 * \ingroup device
 * \brief get path to roccat char device
 * \retval Pointer to absolute path to char device, or \c NULL if there is none.
 */
gchar const *roccat_device_get_roccat_dev(RoccatDevice const *roccat_dev);
void roccat_device_set_roccat_dev(RoccatDevice *roccat_dev, gchar const *device);

/*!
 * \ingroup device
 * \brief opens roccat device for reading
 * \retval If successful returns file descriptor that has to be closed with
 *         \c close(), -1 on error
 */
int roccat_device_open_roccat_dev(RoccatDevice const *roccat_dev);

/*!
 * \ingroup device
 * \brief opens mouse event device
 * \retval If successful returns file descriptor that has to be closed with
 *         \c close(), -1 on error
 */
int roccat_device_open_mouse_evdev(RoccatDevice const *roccat_dev);
void roccat_device_set_mouse_evdev(RoccatDevice *roccat_dev, gchar const *mouse_evdev);

/*!
 * \ingroup device
 * \brief opens keyboard event device
 * \retval If successful returns file descriptor that has to be closed with
 *         \c close(), -1 on error
 */
int roccat_device_open_keyboard_evdev(RoccatDevice const *roccat_dev);
void roccat_device_set_keyboard_evdev(RoccatDevice *roccat_dev, gchar const *keyboard_evdev);

/*!
 * \ingroup device
 * \brief opens uinput device supporting mouse buttons and multimedia keys
 * \retval If successful returns file descriptor that has to be closed with
 *         \c roccat_device_close_user_evdev(), -1 on error
 * It takes some time until device is created.
 */
int roccat_device_open_misc_evdev(RoccatDevice const *roccat_dev);
void roccat_device_close_misc_evdev(int fd);

/*!
 * \ingroup device
 * \brief opens event device of interface type none
 * \retval If successful returns file descriptor that has to be closed with
 *         \c close(), -1 on error
 */
int roccat_device_open_none_evdev(RoccatDevice const *roccat_dev);
void roccat_device_set_none_evdev(RoccatDevice *roccat_dev, gchar const *none_evdev);

/*!
 * \ingroup device
 * \brief write press and release event to evdev
 */
void roccat_input_event_write_single(int fd, int code);

/*!
 * \ingroup device
 * \brief write an event to evdev
 */
void roccat_input_event_write_with_sync(int fd, int code, int value);

typedef struct _RoccatMacroThreads RoccatMacroThreads;
RoccatMacroThreads *roccat_macro_threads_new(guint size);
void roccat_macro_threads_free(RoccatMacroThreads *macro_threads);
void roccat_play_macro_threaded(RoccatMacroThreads *threads, guint index, int kbd_file, int mouse_file, RoccatMacro const *macro);

/* USB HID interface protocol codes partially taken from linux kernels hid.h */
enum {
	ROCCAT_USB_INTERFACE_PROTOCOL_NONE = 0,
	ROCCAT_USB_INTERFACE_PROTOCOL_KEYBOARD = 1,
	ROCCAT_USB_INTERFACE_PROTOCOL_MOUSE = 2,
};

/*!
 * \defgroup keyconv Key conversions
 * Conversions happen between following types:
 *
 * - hid usage id
 *   the type stored in macros
 * - keyboard/button keycode
 *   is used by kernel code (evdev events)
 * - xkeycode
 *   means hardware keycode as defined by \c GdkEventKey
 *   hid <--> kbd_keycode <--> xkeycode --> keyval --> keyname
 */

/*!
 * \ingroup keyconv
 * \brief determines if hid usage id is a mouse button
 *
 * Roccat encodes mouse buttons in macro keystroke keys in upper invalid range
 * of hid usage ids.
 *
 * \retval \c TRUE if usage id is a mouse button
 */
gboolean roccat_hid_is_mouse_button(guint8 usage_id);

/*!
 * \ingroup keyconv
 * \brief converts hid usage id to button keycode
 *
 * Uses the values defined in linux/input.h.
 *
 * \retval button keycode
 */
guint roccat_hid_to_btn_keycode(guint8 usage_id);

/*!
 * \ingroup keyconv
 * \brief converts hid usage id to keyboard keycode
 *
 * Uses the same table as linux kernel.
 *
 * \retval keyboard keycode
 */
guint roccat_hid_to_kbd_keycode(guint8 usage_id);

/*!
 * \ingroup keyconv
 * \brief converts xkeycode to hid usage id
 * \retval hid usage id
 */
guint8 roccat_xkeycode_to_hid(guint16 xkeycode);

/*!
 * \ingroup keyconv
 * \brief converts hid usage id to xkeycode
 * \retval xkeycode
 */
guint16 roccat_hid_to_xkeycode(guint8 usage_id);

/*!
 * \defgroup key_file Roccat key file
 */

RoccatKeyFile *roccat_key_file_load(gchar const *filename);

/*
 * Does not create dir
 */
gboolean roccat_key_file_save(RoccatKeyFile *key_file, GError **error);

gboolean roccat_key_file_save_as(RoccatKeyFile *key_file, gchar const *filename, GError **error);
void roccat_key_file_free(RoccatKeyFile *key_file);

gchar *roccat_key_file_get_string_with_default(RoccatKeyFile *key_file, char const *group, gchar const *key, gchar const *defval);
void roccat_key_file_set_string(RoccatKeyFile *key_file, char const *group, gchar const *key, gchar const *new_value);

gint roccat_key_file_get_integer_with_default(RoccatKeyFile *key_file, char const *group, gchar const *key, gint defval);
void roccat_key_file_set_integer(RoccatKeyFile *key_file, char const *group, gchar const *key, gint new_value);

gboolean roccat_key_file_get_boolean_with_default(RoccatKeyFile *key_file, char const *group, gchar const *key, gboolean defval);
void roccat_key_file_set_boolean(RoccatKeyFile *key_file, char const *group, gchar const *key, gboolean new_value);

/*!
 * \defgroup config Configuration
 */

gchar *roccat_configuration_dir(void);
RoccatKeyFile *roccat_configuration_load(void);
gboolean roccat_configuration_save(RoccatKeyFile *config, GError **error);
void roccat_configuration_free(RoccatKeyFile *config);
gchar *roccat_configuration_get_timer_save_path(RoccatKeyFile *config);
void roccat_configuration_set_timer_save_path(RoccatKeyFile *config, gchar const *path);
gchar *roccat_configuration_get_macro_save_path(RoccatKeyFile *config);
void roccat_configuration_set_macro_save_path(RoccatKeyFile *config, gchar const *path);

/*!
 * \defgroup helper Helpers
 */

/*!
 * \ingroup helper
 * \brief sets or clears bit of byte according to value
 *
 * \e bit can be in range 0 to 7
 */
void roccat_set_bit(guint8 *byte, guint bit, gboolean value);

/*!
 * \ingroup helper
 * \brief returns state of bit in byte
 *
 * \e bit can be in range 0 to 7
 *
 * \retval bit
 */
gboolean roccat_get_bit(guint8 byte, guint bit);

void roccat_bitfield_set_bit(guint8 *array, gsize bit, gboolean value);
gboolean roccat_bitfield_get_bit(guint8 *array, gsize bit);

gulong roccat_calc_bytesum(gconstpointer data, gsize size);

/*!
 * \ingroup helper
 * \brief creates directory with user access with parents if not existent.
 * \retval TRUE if successful or directory already exists.
 * 	   On Error returns \c FALSE with \e error set.
 */
gboolean roccat_create_dir_if_needed(gchar const *dir, GError **error);

/*!
 * \ingroup helper
 * \brief reads data from file
 *
 * If \e length is != 0 read bytes are checked and \e error is set to G_FILE_ERROR_INVAL if
 * wrong amount of data is read.
 *
 * Result is NULL terminated.
 *
 * \retval If successful returns pointer to data, that has to be freed with g_free().
 *         On error returns +NULL+ with error set.
 */
gchar *roccat_read_from_file(gchar const *filename, gsize length, GError **error);

/*!
 * \ingroup helper
 * \brief returns the systemwide configuration directory
 * \retval Directory that has to be freed with g_free().
 */
gchar *roccat_profile_dir(void);

/*!
 * \ingroup helper
 * \brief writes data with user and group access
 *
 * Works like g_file_set_contents(), but with mode 660.
 *
 * \retval \c TRUE if successful. On Error returns \c FALSE with \e error set.
 */
gboolean roccat_profile_write_with_path(gchar const *filename, gchar const *data, gsize length, GError **error);

/*!
 * \ingroup helper
 * \brief creates directory with user and group access with parents if not existent.
 *
 * Works like roccat_create_dir_if_needed(), but with mode 770
 *
 * \retval \c TRUE if successful. On Error returns \c FALSE with \e error set.
 */
gboolean roccat_profile_dir_create_if_needed(gchar const *dir, GError **error);

gchar *roccat_device_read(RoccatDevice *roccat_device, gchar const *attribute, gssize length, GError **error);
gboolean roccat_device_write(RoccatDevice *roccat_device, gchar const *attribute, gchar const *data, gssize length, GError **error);

static inline guint16 roccat_macro_keystrokes_get_count(RoccatMacroKeystrokes const *keystrokes) {
	return GUINT16_FROM_LE(keystrokes->count);
}

static inline void roccat_macro_keystrokes_set_count(RoccatMacroKeystrokes *keystrokes, guint16 new_value) {
	keystrokes->count = GUINT16_TO_LE(new_value);
}

static inline guint16 roccat_macro_keystroke_get_period(RoccatMacroKeystroke const *keystroke) {
	return GUINT16_FROM_LE(keystroke->period);
}

static inline void roccat_macro_keystroke_set_period(RoccatMacroKeystroke *keystroke, guint16 new_value) {
	keystroke->period = GUINT16_TO_LE(new_value);
}

/*!
 * \defgroup secure Security Helpers
 */

/*!
 * \ingroup secure
 *
 * Should be called as early as possible in an application.
 *
 * Disables core dumps and makes sure file descriptors are clean etc.
 */
void roccat_secure(void);

gboolean roccat_double_fork(gchar const *string);

typedef enum {
	ROCCAT_NOTIFICATION_TYPE_OFF = 0,
	ROCCAT_NOTIFICATION_TYPE_AUDIO = 1,
	ROCCAT_NOTIFICATION_TYPE_OSD = 2,
} RoccatNotificationType;

RoccatNotificator *roccat_notificator_init(gchar const *app_name);
void roccat_notificator_deinit(RoccatNotificator *notificator);

/*!
 * It's no error if notify_is_initted, but nothing happens then.
 */
RoccatOsdNote *roccat_osd_note_new(RoccatNotificator *notificator, gint timeout);
void roccat_osd_note_free(RoccatOsdNote *note);

/*!
 * \brief updates and shows the note
 */
gboolean roccat_osd_note_update(RoccatOsdNote *note, gchar const *format, ...);
gboolean roccat_osd_note_update_v(RoccatOsdNote *note, gchar const *format, va_list args);
void roccat_osd_note_close(RoccatOsdNote *note);

RoccatNotificationCpi *roccat_notification_cpi_new(RoccatNotificator *notificator, gchar const *device_name);
void roccat_notification_cpi_free(RoccatNotificationCpi *cpi);
void roccat_notification_cpi_update(RoccatNotificationCpi *cpi, RoccatNotificationType type, guint number);
void roccat_notification_cpi_update_up(RoccatNotificationCpi *cpi, RoccatNotificationType type);
void roccat_notification_cpi_update_down(RoccatNotificationCpi *cpi, RoccatNotificationType type);

RoccatNotificationProfile *roccat_notification_profile_new(RoccatNotificator *notificator, gchar const *device_name);
void roccat_notification_profile_free(RoccatNotificationProfile *profile);
void roccat_notification_profile_update(RoccatNotificationProfile *profile, RoccatNotificationType type, guint number, gchar const *name);

RoccatNotificationSensitivity *roccat_notification_sensitivity_new(RoccatNotificator *notificator, gchar const *device_name);
void roccat_notification_sensitivity_free(RoccatNotificationSensitivity *sensitivity);
void roccat_notification_sensitivity_update(RoccatNotificationSensitivity *sensitivity, RoccatNotificationType type, guint x, guint y);
void roccat_notification_sensitivity_update_up(RoccatNotificationSensitivity *sensitivity, RoccatNotificationType type);
void roccat_notification_sensitivity_update_down(RoccatNotificationSensitivity *sensitivity, RoccatNotificationType type);

RoccatNotificationTimer *roccat_notification_timer_new(RoccatNotificator *notificator, gchar const *device_name);
void roccat_notification_timer_free(RoccatNotificationTimer *timer);
void roccat_notification_timer_start(RoccatNotificationTimer *timer, RoccatNotificationType type, gchar const *name, guint seconds);
void roccat_notification_timer_stop(RoccatNotificationTimer *timer);

RoccatNotificationLiveRecording *roccat_notification_live_recording_new(RoccatNotificator *notificator, gchar const *device_name);
void roccat_notification_live_recording_free(RoccatNotificationLiveRecording *notification);
void roccat_notification_live_recording_select_macro_key(RoccatNotificationLiveRecording *notification, RoccatNotificationType type);
void roccat_notification_live_recording_record_macro(RoccatNotificationLiveRecording *notification, RoccatNotificationType type);
void roccat_notification_live_recording_end_success(RoccatNotificationLiveRecording *notification, RoccatNotificationType type);
void roccat_notification_live_recording_end_abort(RoccatNotificationLiveRecording *notification, RoccatNotificationType type);
void roccat_notification_live_recording_invalid_key(RoccatNotificationLiveRecording *notification, RoccatNotificationType type);

RoccatTimers *roccat_original_timers_import(gchar const *filename, GError **error);
gboolean roccat_original_timers_export(gchar const *filename, RoccatTimers *timers, GError **error);

RoccatTimer *roccat_timer_new(void);
void roccat_timer_free(RoccatTimer *timer);
void roccat_timer_set_name(RoccatTimer *timer, gchar const *new_name);

static inline guint32 roccat_timer_get_seconds(RoccatTimer const *timer) {
	return GUINT32_FROM_LE(timer->seconds);
}

static inline void roccat_timer_set_seconds(RoccatTimer *timer, guint32 new_value) {
	timer->seconds = GUINT32_TO_LE(new_value);
}

#define ROCCAT_DBUS_SERVER_NAME "org.roccat"
#define ROCCAT_DBUS_SERVER_PATH "/org/roccat"
#define ROCCAT_DBUS_SERVER_INTERFACE "org.roccat"

typedef enum {
	ROCCAT_TALK_DEVICE_NONE = 0,
	/* Devices use USB_DEVICE_ID_ROCCAT_* */
	ROCCAT_TALK_DEVICE_KEYBOARD = 0xfffd, /* np */
	ROCCAT_TALK_DEVICE_MOUSE = 0xfffe, /* np */
	ROCCAT_TALK_DEVICE_ALL = 0xffff,
} RoccatTalkDevice;

typedef enum {
	ROCCAT_TALK_EASYAIM_OFF = 0,
	ROCCAT_TALK_EASYAIM_1 = 1,
	ROCCAT_TALK_EASYAIM_2 = 2,
	ROCCAT_TALK_EASYAIM_3 = 3,
	ROCCAT_TALK_EASYAIM_4 = 4,
	ROCCAT_TALK_EASYAIM_5 = 5,
} RoccatTalkEasyaim;

guint roccat_talk_device_get_type(guint talk_device);
gchar const *roccat_talk_device_get_text_static(guint talk_device);

#define ROCCAT_ERROR roccat_error_quark()
GQuark roccat_error_quark(void);

typedef enum {
	ROCCAT_ERROR_MACRO_TOO_LONG,
} RoccatError;

RoccatMacros *roccat_original_macros_import(gchar const *filename, GError **error);
gboolean roccat_original_macros_export(gchar const *filename, RoccatMacros *roccat_macros, GError **error);

RoccatMacros *arvo_original_macros_import(gchar const *filename, GError **error);
gboolean arvo_original_macros_export(gchar const *filename, RoccatMacros *roccat_macros, GError **error);

RoccatMacros *kone_original_macros_import(gchar const *filename, GError **error);
gboolean kone_original_macros_export(gchar const *filename, RoccatMacros *roccat_macros, GError **error);

RoccatMacros *valo_original_macros_import(gchar const *filename, GError **error);
gboolean valo_original_macros_export(gchar const *filename, RoccatMacros *roccat_macros, GError **error);

G_END_DECLS

#endif
