#ifndef _ROCCAT_RYOS_H_
#define _ROCCAT_RYOS_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/>.
 */

#include <glib.h>
#include <roccat/roccat.h>
#include <dbus/dbus-glib.h>

G_BEGIN_DECLS

#define USB_DEVICE_ID_ROCCAT_RYOS_MK 0x3138
#define USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW 0x31ce
#define USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO 0x3232

typedef struct _RyosKey RyosKey;
typedef struct _RyosKeystroke RyosKeystroke;
typedef struct _RyosChrdevEvent RyosChrdevEvent;
typedef struct _RyosControl RyosControl;
typedef struct _RyosProfile RyosProfile;
typedef struct _RyosKeysPrimary RyosKeysPrimary;
typedef struct _RyosKeysFunction RyosKeysFunction;
typedef struct _RyosKeysMacro RyosKeysMacro;
typedef struct _RyosKeysThumbster RyosKeysThumbster;
typedef struct _RyosKeysExtra RyosKeysExtra;
typedef struct _RyosKeysEasyzone RyosKeysEasyzone;
typedef struct _RyosKeyMask RyosKeyMask;
typedef struct _RyosLight RyosLight;
typedef struct _RyosMacro RyosMacro;
typedef struct _RyosInfo RyosInfo;
typedef struct _RyosReset RyosReset;
typedef struct _Ryos12 Ryos12;
typedef struct _RyosLightControl RyosLightControl;
typedef struct _Ryos14 Ryos14;
typedef struct _RyosDriverState RyosDriverState;
typedef struct _RyosLightLayer RyosLightLayer;
typedef struct _RyosStoredLights RyosStoredLights;
typedef struct _RyosCustomLights RyosCustomLights;
typedef struct _RyosTalk RyosTalk;
typedef struct _RyosFirmwareWrite RyosFirmwareWrite;
typedef struct _RyosRkpTimer RyosRkpTimer;
typedef struct _RyosRkpQuicklaunch RyosRkpQuicklaunch;
typedef struct _RyosRkpTalk RyosRkpTalk;
typedef struct _RyosRkp RyosRkp;
typedef struct _RyosRkpData RyosRkpData;

enum {
	RYOS_PROFILE_NUM = 5,
	RYOS_KEYS_EASYZONE_NUM = 96,
	RYOS_KEYS_MACRO_NUM = 5,
	RYOS_KEYS_THUMBSTER_NUM = 3,
	RYOS_KEYS_FUNCTION_NUM = 15,
	RYOS_KEYS_PRIMARY_NUM = 120,
	RYOS_LIGHT_BRIGHTNESS_MIN = 0,
	RYOS_LIGHT_BRIGHTNESS_MAX = 5,
	RYOS_MACRO_MACROSET_NAME_LENGTH = 24,
	RYOS_MACRO_MACRO_NAME_LENGTH = 24,
	RYOS_MACRO_KEYSTROKES_NUM = 480,
	RYOS_RKP_PROFILE_NAME_LENGTH = 50, /* utf16 */
	RYOS_RKP_PROFILE_GAMEFILE_LENGTH = 256,
	RYOS_RKP_PROFILE_GAMEFILE_NUM = 3,
	RYOS_RKP_KEYS_NUM = 0x70,
	RYOS_RKP_TIMER_NAME_LENGTH = 24,
	RYOS_RKP_QUICKLAUNCH_PATH_LENGTH = 256, /* utf16 */
	RYOS_RKP_QUICKLAUNCH_NAME_LENGTH = 24, /* utf16 */
	RYOS_FIRMWARE_SIZE = 56844,
	RYOS_FIRMWARE_CHUNKS = 56,
	RYOS_FIRMWARE_CHUNK_SIZE = 1024,
	RYOS_STORED_LIGHTS_LAYER_NUM = 11,
	RYOS_LIGHT_LAYER_KEY_NUM = 123,
	RYOS_RKP_TALK_NAME_LENGTH = 40, /* utf16 */
	RYOS_RKP_TALK_NUM = 9,
};

/*
 * valid as:
 * RyosLightIlluminationMode
 * RyosStoredLightsIlluminationMode
 */
typedef enum {
	RYOS_ILLUMINATION_MODE_AUTOMATIC = 0,
	RYOS_ILLUMINATION_MODE_MANUAL = 1,
} RyosIlluminationMode;

struct _RyosKey {
	guint8 type;

	/* used for RYOS_KEY_TYPE_SHORTCUT */
	guint8 modifier;
	guint8 key;
} __attribute__ ((packed));

typedef enum {
	RYOS_KEY_TYPE_DISABLED = 0x00,
	RYOS_KEY_TYPE_SYSTEM_SHUTDOWN = 0x81,
	RYOS_KEY_TYPE_SYSTEM_SLEEP = 0x82,
	RYOS_KEY_TYPE_SYSTEM_WAKE = 0x83,
	RYOS_KEY_TYPE_TALK_EASYSHIFT_ALL = 0x8c,

	/* Only work with fn and only on f11/f12 */
	RYOS_KEY_TYPE_LIGHT = 0xa3,
	RYOS_KEY_TYPE_RECORD = 0xa7,

	RYOS_KEY_TYPE_PROFILE_UP = 0xb0,
	RYOS_KEY_TYPE_PROFILE_DOWN = 0xb1,
	RYOS_KEY_TYPE_PROFILE_CYCLE = 0xb2,
	RYOS_KEY_TYPE_PROFILE_1 = 0xb3,
	RYOS_KEY_TYPE_PROFILE_2 = 0xb4,
	RYOS_KEY_TYPE_PROFILE_3 = 0xb5,
	RYOS_KEY_TYPE_PROFILE_4 = 0xb6,
	RYOS_KEY_TYPE_PROFILE_5 = 0xb7,
	RYOS_KEY_TYPE_SHORTCUT = 0xb8,
	RYOS_KEY_TYPE_QUICKLAUNCH = 0xb9,
	RYOS_KEY_TYPE_MACRO_PLAY_PAUSE = 0xba,
	RYOS_KEY_TYPE_TIMER = 0xbc,
	RYOS_KEY_TYPE_TIMER_STOP = 0xbd,
	RYOS_KEY_TYPE_MACRO = 0xbe,
	RYOS_KEY_TYPE_APP_EMAIL = 0xc0,
	RYOS_KEY_TYPE_APP_CALCULATOR = 0xc1,
	RYOS_KEY_TYPE_APP_MY_COMPUTER = 0xc2,
	RYOS_KEY_TYPE_APP_BROWSER = 0xc3,
	RYOS_KEY_TYPE_WWW_SEARCH = 0xc4,
	RYOS_KEY_TYPE_WWW_HOME = 0xc5,
	RYOS_KEY_TYPE_WWW_BACK = 0xc6,
	RYOS_KEY_TYPE_WWW_FORWARD = 0xc7,
	RYOS_KEY_TYPE_WWW_STOP = 0xc8,
	RYOS_KEY_TYPE_WWW_REFRESH = 0xc9,
	RYOS_KEY_TYPE_WWW_FAVORITES = 0xca,
	RYOS_KEY_TYPE_MULTIMEDIA_OPEN_PLAYER = 0xcb,
	RYOS_KEY_TYPE_MULTIMEDIA_PLAY_PAUSE = 0xcc,
	RYOS_KEY_TYPE_MULTIMEDIA_NEXT_TRACK = 0xcd,
	RYOS_KEY_TYPE_MULTIMEDIA_PREV_TRACK = 0xce,
	RYOS_KEY_TYPE_MULTIMEDIA_STOP = 0xcf,
	RYOS_KEY_TYPE_MULTIMEDIA_VOLUME_UP = 0xd0,
	RYOS_KEY_TYPE_MULTIMEDIA_VOLUME_DOWN = 0xd1,
	RYOS_KEY_TYPE_MULTIMEDIA_MUTE = 0xd2,
	RYOS_KEY_TYPE_STANDARD_SHIFT_1 = 0xd3,
	RYOS_KEY_TYPE_STANDARD_SHIFT_2 = 0xd4,
	RYOS_KEY_TYPE_STANDARD_SHIFT_3 = 0xd5,
	RYOS_KEY_TYPE_STANDARD_SHIFT_4 = 0xd6,
	RYOS_KEY_TYPE_STANDARD_SHIFT_5 = 0xd7,
	RYOS_KEY_TYPE_STANDARD_CTRL_1 = 0xd8,
	RYOS_KEY_TYPE_STANDARD_CTRL_2 = 0xd9,
	RYOS_KEY_TYPE_STANDARD_CTRL_3 = 0xda,
	RYOS_KEY_TYPE_STANDARD_CTRL_4 = 0xdb,
	RYOS_KEY_TYPE_STANDARD_CTRL_5 = 0xdc,
	RYOS_KEY_TYPE_OPEN_DRIVER = 0xdd,
	RYOS_KEY_TYPE_FN = 0xf1,
	RYOS_KEY_TYPE_TALK_EASYSHIFT = 0xf5,
	RYOS_KEY_TYPE_TALK_EASYSHIFT_LOCK = 0xf6,
	RYOS_KEY_TYPE_TALK_EASYAIM_1 = 0xf7,
	RYOS_KEY_TYPE_TALK_EASYAIM_2 = 0xf8,
	RYOS_KEY_TYPE_TALK_EASYAIM_3 = 0xf9,
	RYOS_KEY_TYPE_TALK_EASYAIM_4 = 0xfa,
	RYOS_KEY_TYPE_TALK_EASYAIM_5 = 0xfb,
	RYOS_KEY_TYPE_EASYSHIFT = 0xff,
} RyosKeyType;

typedef enum {
	RYOS_KEY_MODIFIER_BIT_CTRL = 0,
	RYOS_KEY_MODIFIER_BIT_SHIFT = 1,
	RYOS_KEY_MODIFIER_BIT_ALT = 2,
	RYOS_KEY_MODIFIER_BIT_WIN = 3,
} RyosKeyModifier;

guint8 ryos_key_build_shortcut_modifier(gboolean ctrl, gboolean shift, gboolean alt, gboolean win);
void ryos_key_set_to_shortcut(RyosKey *key, guint hid_usage_id, guint8 modifier);
void ryos_key_set_to_normal(RyosKey *key, guint type);
gboolean ryos_key_equal(RyosKey const *left, RyosKey const *right);
void ryos_key_copy(RyosKey *destination, RyosKey const *source);

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

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

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

typedef enum {
	RYOS_KEYSTROKE_ACTION_PRESS = 1,
	RYOS_KEYSTROKE_ACTION_RELEASE = 2,
} RyosKeystrokeAction;


struct _RyosControl {
	guint8 command; /* RYOS_COMMAND_CONTROL */
	guint8 value;
	guint8 request;
} __attribute__ ((packed));

/*
 * read: (after other write)
 *   value = RyosControlValue
 *   request = RYOS_CONTROL_REQUEST_CHECK
 * write selection: (before profile specific read)
 *   value = profile index 0-4
 *   request = RyosControlRequest or key index for macros
 */
typedef enum {
	RYOS_CONTROL_REQUEST_CHECK = 0x00,
	RYOS_CONTROL_REQUEST_KEYS_PRIMARY = 0xa0,
	RYOS_CONTROL_REQUEST_KEYS_FUNCTION = 0xa1,
	RYOS_CONTROL_REQUEST_KEYS_MACRO = 0xa2,
	RYOS_CONTROL_REQUEST_KEYS_THUMBSTER = 0xa3,
	// TODO 0xa4
	RYOS_CONTROL_REQUEST_KEYS_EASYZONE = 0xa5,
	RYOS_CONTROL_REQUEST_KEY_MASK = 0xb0,
	RYOS_CONTROL_REQUEST_LIGHT = 0xb1,
	RYOS_CONTROL_REQUEST_KEYS_EXTRA = 0xb2,
	RYOS_CONTROL_REQUEST_STORED_LIGHTS_AUTOMATIC = 0xc0,
	RYOS_CONTROL_REQUEST_STORED_LIGHTS_MANUAL = 0xd0,
	RYOS_CONTROL_REQUEST_LIGHT_MACRO = 0xe0,
	RYOS_CONTROL_REQUEST_12 = 0xf0, // TODO called only for index 0
} RyosControlRequest;

typedef enum {
	RYOS_CONTROL_VALUE_STATUS_OK = 1,
	RYOS_CONTROL_VALUE_STATUS_INVALID = 2,
	RYOS_CONTROL_VALUE_STATUS_BUSY = 3,
	RYOS_CONTROL_VALUE_STATUS_CRITICAL = 4,
} RyosControlValue;

gboolean ryos_select(RoccatDevice *ryos, guint profile_index, guint request, GError **error);

struct _RyosProfile {
	guint8 command; /* RYOS_COMMAND_PROFILE */
	guint8 size; /* always 0x03 */
	guint8 profile_index;
} __attribute__ ((packed));

guint ryos_profile_read(RoccatDevice *ryos, GError **error);
gboolean ryos_profile_write(RoccatDevice *ryos, guint profile_index, GError **error);

struct _RyosKeysPrimary {
	guint8 command; /* RYOS_COMMAND_KEYS_PRIMARY */
	guint8 size; /* always 0x7d */
	guint8 profile_index;
	guint8 keys[RYOS_KEYS_PRIMARY_NUM];
	guint16 checksum;
} __attribute__ ((packed));

gboolean ryos_keys_primary_write(RoccatDevice *ryos, guint profile_index, RyosKeysPrimary *keys_primary, GError **error);
RyosKeysPrimary *ryos_keys_primary_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_keys_primary_equal(RyosKeysPrimary const *left, RyosKeysPrimary const *right);
void ryos_keys_primary_copy(RyosKeysPrimary *destination, RyosKeysPrimary const *source);
guint8 ryos_keys_primary_correct_remapped(RyosKeysPrimary const *actual_keys, guint8 hid);

struct _RyosKeysFunction {
	guint8 command; /* RYOS_COMMAND_KEYS_FUNCTION */
	guint8 size; /* always 0x5f */
	guint8 profile_index;
	RyosKey keys[RYOS_KEYS_FUNCTION_NUM * 2];
	guint16 checksum;
} __attribute__ ((packed));

gboolean ryos_keys_function_write(RoccatDevice *ryos, guint profile_index, RyosKeysFunction *keys_function, GError **error);
RyosKeysFunction *ryos_keys_function_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_keys_function_equal(RyosKeysFunction const *left, RyosKeysFunction const *right);
void ryos_keys_function_copy(RyosKeysFunction *destination, RyosKeysFunction const *source);

struct _RyosKeysMacro {
	guint8 command; /* RYOS_COMMAND_KEYS_MACRO */
	guint8 size; /* always 0x23 */
	guint8 profile_index;
	RyosKey keys[RYOS_KEYS_MACRO_NUM * 2];
	guint16 checksum;
} __attribute__ ((packed));

gboolean ryos_keys_macro_write(RoccatDevice *ryos, guint profile_index, RyosKeysMacro *keys_macro, GError **error);
RyosKeysMacro *ryos_keys_macro_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_keys_macro_equal(RyosKeysMacro const *left, RyosKeysMacro const *right);
void ryos_keys_macro_copy(RyosKeysMacro *destination, RyosKeysMacro const *source);
guint ryos_keys_macro_index_to_macro_index(guint macro_index);
guint ryos_macro_index_to_keys_macro_index(guint macro_index);
gboolean ryos_macro_index_is_keys_macro(guint macro_index);

struct _RyosKeysThumbster {
	guint8 command; /* RYOS_COMMAND_KEYS_THUMBSTER */
	guint8 size; /* always 0x17 */
	guint8 profile_index;
	RyosKey keys[RYOS_KEYS_THUMBSTER_NUM * 2];
	guint16 checksum;
} __attribute__ ((packed));

gboolean ryos_keys_thumbster_write(RoccatDevice *ryos, guint profile_index, RyosKeysThumbster *keys_thumbster, GError **error);
RyosKeysThumbster *ryos_keys_thumbster_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_keys_thumbster_equal(RyosKeysThumbster const *left, RyosKeysThumbster const *right);
void ryos_keys_thumbster_copy(RyosKeysThumbster *destination, RyosKeysThumbster const *source);
guint ryos_keys_thumbster_index_to_macro_index(guint thumbster_index);
guint ryos_macro_index_to_keys_thumbster_index(guint macro_index);
gboolean ryos_macro_index_is_keys_thumbster(guint macro_index);

struct _RyosKeysExtra {
	guint8 command; /* RYOS_CONTROL_REQUEST_KEYS_EXTRA */
	guint8 size; /* always 0x08 */
	guint8 profile_index;
	guint8 capslock;
	guint8 fn;
	guint8 unused;
	guint16 checksum;
} __attribute__ ((packed));

gboolean ryos_keys_extra_write(RoccatDevice *ryos, guint profile_index, RyosKeysExtra *keys_extra, GError **error);
RyosKeysExtra *ryos_keys_extra_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_keys_extra_equal(RyosKeysExtra const *left, RyosKeysExtra const *right);
void ryos_keys_extra_copy(RyosKeysExtra *destination, RyosKeysExtra const *source);

struct _RyosKeysEasyzone { /* Secondary key functions */
	guint8 command; /* RYOS_COMMAND_KEYS_EASYZONE */
	guint16 size; /* always 0x0126 */
	guint8 profile_index;
	RyosKey keys[RYOS_KEYS_EASYZONE_NUM];
	guint16 checksum;
} __attribute__ ((packed));

gboolean ryos_keys_easyzone_write(RoccatDevice *ryos, guint profile_index, RyosKeysEasyzone *keys_easyzone, GError **error);
RyosKeysEasyzone *ryos_keys_easyzone_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_keys_easyzone_equal(RyosKeysEasyzone const *left, RyosKeysEasyzone const *right);
void ryos_keys_easyzone_copy(RyosKeysEasyzone *destination, RyosKeysEasyzone const *source);
guint ryos_keys_easyzone_index_to_macro_index(guint easyzone_index);
guint ryos_macro_index_to_keys_easyzone_index(guint macro_index);
gboolean ryos_macro_index_is_keys_easyzone(guint macro_index);

struct _RyosKeyMask {
	guint8 command; /* RYOS_COMMAND_KEY_MASK */
	guint8 size; /* always 0x06 */
	guint8 profile_index;
	guint8 mask;
	guint16 checksum;
} __attribute__ ((packed));

typedef enum {
	RYOS_KEY_MASK_BIT_TAB = 0,
	RYOS_KEY_MASK_BIT_LEFT_WIN = 2,
	RYOS_KEY_MASK_BIT_RIGHT_WIN = 3,
	RYOS_KEY_MASK_BIT_APP = 4,
	RYOS_KEY_MASK_BIT_LEFT_SHIFT = 5,
} RyosKeyMaskBit;

gboolean ryos_key_mask_write(RoccatDevice *ryos, guint profile_index, RyosKeyMask *key_mask, GError **error);
RyosKeyMask *ryos_key_mask_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_key_mask_equal(RyosKeyMask const *left, RyosKeyMask const *right);
void ryos_key_mask_copy(RyosKeyMask *destination, RyosKeyMask const *source);

struct _RyosLight {
	guint8 command; /* RYOS_COMMAND_LIGHT */
	guint8 size; /* always 0x10 */
	guint8 profile_index;
	guint8 brightness; /* 0-5 */
	guint8 dimness;
	guint8 timeout; /* minutes */
	guint8 unknown1;
	guint8 unknown2;
	guint8 unknown3;
	guint8 unknown4;
	guint8 illumination_mode;
	guint8 macro_exec_feedback;
	guint8 dimness_type;
	guint8 unknown5;
	guint16 checksum;
} __attribute__ ((packed));

typedef enum {
	RYOS_LIGHT_MACRO_EXEC_FEEDBACK_ON = 1,
	RYOS_LIGHT_MACRO_EXEC_FEEDBACK_OFF = 0,
} RyosLightMacroExecFeedback;

typedef enum {
	RYOS_LIGHT_DIMNESS_TYPE_VALUE = 0,
	RYOS_LIGHT_DIMNESS_TYPE_STARLIT_SKY = 1,
	RYOS_LIGHT_DIMNESS_TYPE_MACRO = 2,
	RYOS_LIGHT_DIMNESS_TYPE_FALL_ASLEEP = 3,
} RyosLightDimnessType;

gboolean ryos_light_write(RoccatDevice *ryos, guint profile_index, RyosLight *light, GError **error);
RyosLight *ryos_light_read(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_light_equal(RyosLight const *left, RyosLight const *right);
void ryos_light_copy(RyosLight *destination, RyosLight const *source);

/* Given is full sized struct of 2002 bytes that are always sent,
 * but size is dynamic through RYOS_MACRO_KEYSTROKES_NUM
 */
struct _RyosMacro {
	guint8 command; /* RYOS_COMMAND_MACRO */
	guint16 size; /* dynamic */
	guint8 profile_index;
	guint8 button_index;
	guint8 loop;
	guint8 unknown1[8];
	guint8 macroset_name[RYOS_MACRO_MACROSET_NAME_LENGTH];
	guint8 unkown2[16];
	guint8 macro_name[RYOS_MACRO_MACRO_NAME_LENGTH];
	guint16 count;
	RyosKeystroke keystrokes[RYOS_MACRO_KEYSTROKES_NUM];
	guint16 checksum;
} __attribute__ ((packed));

static inline guint16 ryos_macro_get_count(RyosMacro const *macro) {
	return GUINT16_FROM_LE(macro->count);
}

static inline void ryos_macro_set_count(RyosMacro *macro, guint16 new_value) {
	macro->count = GUINT16_TO_LE(new_value);
}

gboolean ryos_macro_write(RoccatDevice *ryos, guint profile_index, guint button_index, RyosMacro *macro, GError **error);
RyosMacro *ryos_macro_read(RoccatDevice *ryos, guint profile_index, guint button_index, GError **error);
gboolean roccat_macro_to_ryos_macro(RoccatMacro const *roccat_macro, RyosMacro *ryos_macro, GError **error);
RoccatMacro *ryos_macro_to_roccat_macro(RyosMacro const *ryos_macro);
gboolean ryos_macro_equal(RyosMacro const *left, RyosMacro const *right);
void ryos_macro_copy(RyosMacro *destination, RyosMacro const *source);
gboolean ryos_light_macro_write(RoccatDevice *ryos, guint profile_index, RyosMacro *light_macro, GError **error);
RyosMacro *ryos_light_macro_read(RoccatDevice *ryos, guint profile_index, GError **error);

struct _RyosInfo {
	guint8 command; /* RYOS_COMMAND_INFO */
	guint8 size; /* always 0x08 */
	guint8 firmware_version;
	guint8 unknown1;
	guint8 unused1;
	guint8 unknown2;
	guint8 unknown3;
	guint8 unused2;
} __attribute__ ((packed));

RyosInfo *ryos_info_read(RoccatDevice *ryos, GError **error);
guint ryos_firmware_version_read(RoccatDevice *ryos, GError **error);
gchar *ryos_firmware_version_to_string(guint firmware_version);

struct _RyosReset {
	guint8 command; /* RYOS_COMMAND_RESET */
	guint8 size; /* always 0x03 */
	guint8 function;
} __attribute__ ((packed));

typedef enum {
	/* supports 1-5 */
	RYOS_RESET_FUNCTION_RESET = 0x4,
} RyosResetFunction;

gboolean ryos_reset(RoccatDevice *ryos, guint function, GError **error);

struct _Ryos12 {
	guint8 command; /* RYOS_COMMAND_12 */
	guint8 size; /* always 0x0e */
	guint8 profile_index; // TODO ?
	guint8 unknown[9];
	guint16 checksum;
} __attribute__ ((packed));

struct _RyosLightControl {
	guint8 command; /* RYOS_COMMAND_LIGHT_CONTROL */
	guint8 size; /* 0x08 */
	guint8 custom;
	guint8 unknown[5];
} __attribute__ ((packed));

typedef enum {
	RYOS_LIGHT_CONTROL_CUSTOM = 0x1,
	RYOS_LIGHT_CONTROL_STORED = 0x0,
} RyosLightControlCustom;

gboolean ryos_light_control_custom(RoccatDevice *ryos, gboolean on, GError **error);

struct _Ryos14 {
	guint8 unknown[20]; // all 0xff on plain
} __attribute__ ((packed));

struct _RyosDriverState {
	guint8 command; /* RYOS_COMMAND_DRIVER_STATE */
	guint8 size; /* always 0x03 */
	guint8 state;
} __attribute__ ((packed));

typedef enum {
	/* Driver handles multimedia events */
	RYOS_DRIVER_STATE_STATE_ON = 1,
	/* Keyboard sends HID multimedia events */
	RYOS_DRIVER_STATE_STATE_OFF = 0,
} RyosDriverStateState;

struct _RyosLightLayer {
	guint8 effect;
	guint8 fade_delay; /* seconds */
	guint8 keys[RYOS_LIGHT_LAYER_KEY_NUM]; /* index != primary_keys for 4 keys */
} __attribute__ ((packed));

typedef enum {
	RYOS_LIGHT_LAYER_EFFECT_OFF = 0x0,
	RYOS_LIGHT_LAYER_EFFECT_FADE = 0x1,
	RYOS_LIGHT_LAYER_EFFECT_RIPPLE = 0x2,
} RyosLightLayerEffect;

typedef enum {
	RYOS_LIGHT_LAYER_KEY_BIT_ON = 0,
	RYOS_LIGHT_LAYER_KEY_BIT_BLINK = 1,
} RyosLightLayerKey;

struct _RyosStoredLights {
	guint8 command; /* RYOS_COMMAND_STORED_LIGHTS */
	guint16 size; /* 1382 */
	guint8 profile_index;
	guint8 illumination_mode;
	RyosLightLayer layers[RYOS_STORED_LIGHTS_LAYER_NUM];
	guint16 checksum;
} __attribute__ ((packed));

typedef enum {
	RYOS_STORED_LIGHTS_LAYER_NORMAL = 0x0,
	RYOS_STORED_LIGHTS_LAYER_LEFT_SHIFT = 0x1,
	RYOS_STORED_LIGHTS_LAYER_RIGHT_SHIFT = 0x2,
	RYOS_STORED_LIGHTS_LAYER_LEFT_CTRL = 0x3,
	RYOS_STORED_LIGHTS_LAYER_RIGHT_CTRL = 0x4,
	RYOS_STORED_LIGHTS_LAYER_LEFT_ALT = 0x5,
	RYOS_STORED_LIGHTS_LAYER_RIGHT_ALT = 0x6,
	RYOS_STORED_LIGHTS_LAYER_LEFT_WIN = 0x7,
	RYOS_STORED_LIGHTS_LAYER_RIGHT_WIN = 0x8,
	RYOS_STORED_LIGHTS_LAYER_EASY_SHIFT = 0x9,
	RYOS_STORED_LIGHTS_LAYER_FN = 0xa,
} RyosStoredLightsLayers;

gboolean ryos_stored_lights_write(RoccatDevice *ryos, guint profile_index, guint mode, RyosStoredLights *stored_lights, GError **error);
RyosStoredLights *ryos_stored_lights_read_manual(RoccatDevice *ryos, guint profile_index, GError **error);
RyosStoredLights *ryos_stored_lights_read_automatic(RoccatDevice *ryos, guint profile_index, GError **error);
gboolean ryos_stored_lights_equal(RyosStoredLights const *left, RyosStoredLights const *right);
void ryos_stored_lights_copy(RyosStoredLights *destination, RyosStoredLights const *source);

struct _RyosCustomLights {
	guint8 command; /* RYOS_COMMAND_CUSTOM_LIGHTS */
	guint8 size; /* 20 */
	guint8 profile_index; /* FIXME not used in firmware? */
	guint8 data[15]; /* index as in keys_primary */
	guint16 checksum;
} __attribute__ ((packed));

gboolean ryos_custom_lights_write(RoccatDevice *ryos, RyosCustomLights *custom_lights, GError **error);
gboolean ryos_custom_lights_empty(RyosCustomLights const *custom_lights);

struct _RyosTalk {
	guint8 command; /* RYOS_COMMAND_TALK */
	guint8 size; /* always 0x10 */
	guint8 easyshift;
	guint8 easyshift_lock; // TODO confirm
	guint8 unused[12];
} __attribute__ ((packed));

/* also valid as RyosTalkEasyshiftLock */
typedef enum {
	RYOS_TALK_EASYSHIFT_OFF = 0,
	RYOS_TALK_EASYSHIFT_ON = 1,
	RYOS_TALK_EASYSHIFT_UNUSED = 0,
} RyosTalkEasyshift;

gboolean ryos_talk_easyshift(RoccatDevice *ryos, guint state, GError **error);
gboolean ryos_talk_easyshift_lock(RoccatDevice *ryos, guint state, GError **error);

struct _RyosFirmwareWrite {
	guint8 command; /* RYOS_COMMAND_FIRMWARE_WRITE */
	guint8 type;
	guint8 data[RYOS_FIRMWARE_CHUNK_SIZE];
} __attribute__ ((packed));

typedef enum {
	RYOS_FIRMWARE_WRITE_TYPE_0 = 0x00,
	RYOS_FIRMWARE_WRITE_TYPE_1 = 0x01,
	RYOS_FIRMWARE_WRITE_TYPE_2 = 0x02,
	RYOS_FIRMWARE_WRITE_TYPE_3 = 0x03,
	RYOS_FIRMWARE_WRITE_TYPE_4 = 0x04,
	RYOS_FIRMWARE_WRITE_TYPE_F = 0x0f,
} RyosFirmwareWriteType;

typedef enum {
	RYOS_COMMAND_CONTROL = 0x04, /* rw */
	RYOS_COMMAND_PROFILE = 0x05, /* rw */
	RYOS_COMMAND_KEYS_PRIMARY = 0x06, /* rw */
	RYOS_COMMAND_KEYS_FUNCTION = 0x07, /* rw */
	RYOS_COMMAND_KEYS_MACRO = 0x08, /* rw */
	RYOS_COMMAND_KEYS_THUMBSTER = 0x09, /* rw */
	RYOS_COMMAND_KEYS_EXTRA = 0x0a, /* rw */
	RYOS_COMMAND_KEYS_EASYZONE = 0x0b, /* rw */
	RYOS_COMMAND_KEY_MASK = 0x0c, /* rw */
	RYOS_COMMAND_LIGHT = 0x0d, /* rw */
	RYOS_COMMAND_MACRO = 0x0e, /* rw */
	RYOS_COMMAND_INFO = 0x0f, /* ro */
	RYOS_COMMAND_RESET = 0x11, /* wo */
	RYOS_COMMAND_12 = 0x12, /* rw */
	RYOS_COMMAND_LIGHT_CONTROL = 0x13, /* wo */
	RYOS_COMMAND_14 = 0x14, /* rw */
	RYOS_COMMAND_DRIVER_STATE = 0x15, /* wo */
	RYOS_COMMAND_TALK = 0x16, /* rw */
	RYOS_COMMAND_STORED_LIGHTS = 0x17, /* rw */
	RYOS_COMMAND_CUSTOM_LIGHTS = 0x18, /* wo */
	RYOS_COMMAND_LIGHT_MACRO = 0x19, /* rw */
	RYOS_COMMAND_FIRMWARE_WRITE = 0x1b, /* wo */
	RYOS_COMMAND_FIRMWARE_WRITE_CONTROL = 0x1c, /* ro */
	RYOS_COMMAND_1d = 0x1d, /* wo */
} RyosCommand;

struct _RyosChrdevEvent {
	guint8 command;
	guint8 type;
	guint8 special;
	guint8 data;
	guint8 action;
} __attribute__ ((packed));

typedef enum {
	RYOS_CHRDEV_EVENT_TYPE_SPECIAL = 0x00,

	/* special = 0xfa
	 * data = 0x01
	 * action = 0x00
	 */
	RYOS_CHRDEV_EVENT_TYPE_RAD_W = 0x01,
	RYOS_CHRDEV_EVENT_TYPE_RAD_A = 0x02,
	RYOS_CHRDEV_EVENT_TYPE_RAD_S = 0x03,
	RYOS_CHRDEV_EVENT_TYPE_RAD_D = 0x04,
	RYOS_CHRDEV_EVENT_TYPE_RAD_THUMBSTER1 = 0x05,
	RYOS_CHRDEV_EVENT_TYPE_RAD_THUMBSTER2 = 0x06,
	RYOS_CHRDEV_EVENT_TYPE_RAD_THUMBSTER3 = 0x07,
	RYOS_CHRDEV_EVENT_TYPE_RAD_EASYSHIFT = 0x08,
	RYOS_CHRDEV_EVENT_TYPE_RAD_MULTIMEDIA = 0x09,
	RYOS_CHRDEV_EVENT_TYPE_RAD_M1 = 0x0a,
	RYOS_CHRDEV_EVENT_TYPE_RAD_M2 = 0x0b,
	RYOS_CHRDEV_EVENT_TYPE_RAD_M3 = 0x0c,
	RYOS_CHRDEV_EVENT_TYPE_RAD_M4 = 0x0d,
	RYOS_CHRDEV_EVENT_TYPE_RAD_M5 = 0x0e,
	RYOS_CHRDEV_EVENT_TYPE_RAD_MACRO_SHORTCUT = 0x0f,
	RYOS_CHRDEV_EVENT_TYPE_RAD_TALK = 0x10,
	RYOS_CHRDEV_EVENT_TYPE_RAD_MACRO_LIFE_REC = 0x11,
	RYOS_CHRDEV_EVENT_TYPE_RAD_BACKLIGHT = 0x12,
	RYOS_CHRDEV_EVENT_TYPE_RAD_TOTAL = 0x13,
} RyosChrdevEventType;

typedef enum {
	RYOS_CHRDEV_EVENT_ACTION_PRESS = 0,
	RYOS_CHRDEV_EVENT_ACTION_RELEASE = 1,
} RyosChrdevEventAction;

typedef enum {
	RYOS_CHRDEV_EVENT_RIPPLE_ACTION_PRESS = 1,
	RYOS_CHRDEV_EVENT_RIPPLE_ACTION_RELEASE = 0,
} RyosChrdevEventRippleAction;

typedef enum {
	/* data = profile index
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_PROFILE_START = 0x01,
	RYOS_CHRDEV_EVENT_SPECIAL_PROFILE = 0x02,

	/* data = key index
	 * action = TODO
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_MACRO = 0x03,

	/* data = see RyosChrdevEventLiveRecordingAction
	 * action = RyosChrdevEventLiveRecordingAction
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_LIVE_RECORDING = 0x04,

	/* data = key index
	 * action = RyosChrdevEventAction
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_QUICKLAUNCH = 0x07,

	/* data = 0xff
	 * action = RyosChrdevEventAction
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_EASYSHIFT = 0xa, // FIXME confirm

	RYOS_CHRDEV_EVENT_SPECIAL_MULTIMEDIA = 0x0b,

	/* data = 0-5
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_BACKLIGHT = 0x0c,

	/* data = key index
	 * action = RyosChrdevEventAction
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_TIMER_START = 0x0d,
	RYOS_CHRDEV_EVENT_SPECIAL_TIMER_STOP = 0x0e,

	RYOS_CHRDEV_EVENT_SPECIAL_OPEN_DRIVER = 0x10,

	RYOS_CHRDEV_EVENT_SPECIAL_RAD = 0xfa,

	/* data = hid
	 * action = RyosChrdevEventRippleAction
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_RIPPLE = 0xfb,

	// TODO 0xfc sent when talk from extern
	// TODO 0xfd sent when talk from extern

	/* data = hid
	 * action = RyosChrdevEventAction
	 */
	RYOS_CHRDEV_EVENT_SPECIAL_TALK = 0xff,
} RyosChrdevEventSpecial;

typedef enum {
	RYOS_CHRDEV_EVENT_LIVE_RECORDING_ACTION_START = 1, /* data = 0x00 */
	RYOS_CHRDEV_EVENT_LIVE_RECORDING_ACTION_MACRO_KEY_SELECTED = 2, /* data = key_index */
	RYOS_CHRDEV_EVENT_LIVE_RECORDING_ACTION_END_SUCCESS = 3, /* data = key_index (driver reads macro) (update of key settings seems to be done by device and driver individually) */
	RYOS_CHRDEV_EVENT_LIVE_RECORDING_ACTION_END_ABORT = 4, /* data = 0x00 */
	RYOS_CHRDEV_EVENT_LIVE_RECORDING_ACTION_INVALID_KEY = 5, /* data = 0x00 */
} RyosChrdevEventLiveRecordingAction;

struct _RyosRkpTimer {
	guint8 name[RYOS_RKP_TIMER_NAME_LENGTH];
	guint8 duration;
	guint8 unknown[12];
} __attribute__ ((packed));

void ryos_rkp_timer_set_name(RyosRkpTimer *timer, gchar const *new_name);
void roccat_timer_to_ryos_rkp_timer(RoccatTimer const *roccat_timer, RyosRkpTimer *ryos_timer);
gboolean ryos_rkp_timer_equal(RyosRkpTimer const *left, RyosRkpTimer const *right);
void ryos_rkp_timer_copy(RyosRkpTimer *destination, RyosRkpTimer const *source);
RyosRkpTimer *ryos_rkp_timer_dup(RyosRkpTimer const *source);

struct _RyosRkpQuicklaunch {
	guint16 path[RYOS_RKP_QUICKLAUNCH_PATH_LENGTH];
	guint16 name[RYOS_RKP_QUICKLAUNCH_NAME_LENGTH];
} __attribute__ ((packed));

gchar *ryos_rkp_quicklaunch_get_path(RyosRkpQuicklaunch const *quicklaunch);
gchar *ryos_rkp_quicklaunch_get_name(RyosRkpQuicklaunch const *quicklaunch);
void ryos_rkp_quicklaunch_set(RyosRkpQuicklaunch *quicklaunch, gchar const *path);
gboolean ryos_rkp_quicklaunch_equal(RyosRkpQuicklaunch const *left, RyosRkpQuicklaunch const *right);
void ryos_rkp_quicklaunch_copy(RyosRkpQuicklaunch *destination, RyosRkpQuicklaunch const *source);
RyosRkpQuicklaunch *ryos_rkp_quicklaunch_dup(RyosRkpQuicklaunch const *source);

struct _RyosRkpTalk {
	guint16 vendor_id;
	guint16 product_id;
	guint8 type;
	guint16 name[RYOS_RKP_TALK_NAME_LENGTH];
} __attribute__ ((packed));

typedef enum {
	RYOS_RKP_TALK_INDEX_M1 = 0,
	RYOS_RKP_TALK_INDEX_M2 = 1,
	RYOS_RKP_TALK_INDEX_M3 = 2,
	RYOS_RKP_TALK_INDEX_M4 = 3,
	RYOS_RKP_TALK_INDEX_M5 = 4,
	RYOS_RKP_TALK_INDEX_T1 = 5,
	RYOS_RKP_TALK_INDEX_T2 = 6,
	RYOS_RKP_TALK_INDEX_T3 = 7,
	RYOS_RKP_TALK_INDEX_CAPSLOCK = 8,

} RyosRkpTalkIndex;

static inline guint16 ryos_rkp_talk_get_vendor_id(RyosRkpTalk const *talk) {
	return GUINT16_FROM_LE(talk->vendor_id);
}

static inline void ryos_rkp_talk_set_vendor_id(RyosRkpTalk *talk, guint16 new_value) {
	talk->vendor_id = GUINT16_TO_LE(new_value);
}

static inline guint16 ryos_rkp_talk_get_product_id(RyosRkpTalk const *talk) {
	return GUINT16_FROM_LE(talk->product_id);
}

static inline void ryos_rkp_talk_set_product_id(RyosRkpTalk *talk, guint16 new_value) {
	talk->product_id = GUINT16_TO_LE(new_value);
}

gchar *ryos_rkp_talk_get_name(RyosRkpTalk const *talk);
void ryos_rkp_talk_set(RyosRkpTalk *talk, guint target, guint type, gchar const *name);
gboolean ryos_rkp_talk_equal(RyosRkpTalk const *left, RyosRkpTalk const *right);
void ryos_rkp_talk_copy(RyosRkpTalk *destination, RyosRkpTalk const *source);
RyosRkpTalk *ryos_rkp_talk_dup(RyosRkpTalk const *source);
gint ryos_hid_to_rkp_talk_index(guint hid);

struct _RyosRkpData {
	RyosKeysPrimary keys_primary;
	guint8 unused1;
	RyosKeysFunction keys_function;
	guint8 unused2;
	RyosKeysMacro keys_macro;
	guint8 unused3;
	RyosKeysThumbster keys_thumbster;
	guint8 unused4;
	RyosKeysExtra keys_extra;
	guint8 unused5;
	RyosKeysEasyzone keys_easyzone;
	guint8 unused6;
	RyosKeyMask key_mask;
	guint8 unused7;
	RyosLight light;
	guint8 unused8;
	Ryos14 _14;
	guint8 unused9;
	RyosMacro macros[RYOS_RKP_KEYS_NUM];
	guint8 unused10;
	guint16 profile_name[RYOS_RKP_PROFILE_NAME_LENGTH];
	guint32 character_repeat_delay;
	guint32 character_repeat_rate;
	guint8 unknown1;
	guint8 led_volume_indicator;
	guint8 sound_feedback_mask;
	guint8 sound_feedback_volume;
	guint8 unknown2[5];
	RyosRkpTalk talks[RYOS_RKP_TALK_NUM];
	guint8 unused11;
	guint16 gamefile_names[RYOS_RKP_PROFILE_GAMEFILE_NUM][RYOS_RKP_PROFILE_GAMEFILE_LENGTH];
	guint8 unknown3[5];
	RyosRkpTimer timers[RYOS_RKP_KEYS_NUM];
	guint8 unknown4[1070];
	RyosRkpQuicklaunch launchers[RYOS_RKP_KEYS_NUM];

	//guint8 unknown5[16241];

	// FIXME positions not confirmed
	RyosMacro light_macro; /* used in dimness settings */
	RyosStoredLights lights_automatic;
	RyosStoredLights lights_manual;
	guint8 unknown5[11475];

} __attribute__ ((packed)); // size 0x4c09f

typedef enum {
	RYOS_RKP_DATA_LED_VOLUME_INDICATOR_ON = 1,
	RYOS_RKP_DATA_LED_VOLUME_INDICATOR_OFF = 0,
} RyosRkpDataLedVolumeIndicator;

struct _RyosRkp {
	RyosRkpData data;
	gboolean modified_rkp;
	gboolean modified_keys_primary;
	gboolean modified_keys_function;
	gboolean modified_keys_macro;
	gboolean modified_keys_thumbster;
	gboolean modified_keys_extra;
	gboolean modified_keys_easyzone;
	gboolean modified_key_mask;
	gboolean modified_light;
	gboolean modified_macros[RYOS_RKP_KEYS_NUM];
	gboolean modified_light_macro;
	gboolean modified_lights_automatic;
	gboolean modified_lights_manual;
};

RyosRkpData const *ryos_rkp_default_data_static(void);
RyosRkp *ryos_rkp_default(void);
RyosRkp *ryos_rkp_new(void);
void ryos_rkp_free(RyosRkp *rkp);
RyosRkp *ryos_rkp_dup(RyosRkp const *other);

gboolean ryos_rkp_update_from_device(RoccatDevice *ryos, RyosRkp *rkp, guint profile_index, GError **error);
gboolean ryos_rkp_get_modified(RyosRkp const *rkp);
void ryos_rkp_set_modified(RyosRkp *rkp);
void ryos_rkp_set_unmodified(RyosRkp *rkp);

gboolean ryos_rkp_write_with_path(gchar const *path, RyosRkp *rkp, GError **error);
gboolean ryos_rkp_save_actual(RyosRkp *rkp, guint profile_index, GError **error);
gboolean ryos_rkp_save(RoccatDevice *ryos, RyosRkp *rkp, guint profile_index, GError **error);
RyosRkp *ryos_rkp_load_save_after_reset(RoccatDevice *ryos, guint profile_index, GError **error);
RyosRkp *ryos_rkp_read_with_path(gchar const *path);
RyosRkp *ryos_rkp_load_actual(guint profile_index);
RyosRkp *ryos_rkp_load(RoccatDevice *ryos, guint profile_index, GError **error);

gchar *ryos_rkp_get_profile_name(RyosRkp const *rkp);
gchar *ryos_rkp_get_gamefile_name(RyosRkp const *rkp, guint index);
RyosKeysPrimary *ryos_rkp_get_keys_primary(RyosRkp const *rkp);
RyosKeysFunction *ryos_rkp_get_keys_function(RyosRkp const *rkp);
RyosKeysMacro *ryos_rkp_get_keys_macro(RyosRkp const *rkp);
RyosKeysThumbster *ryos_rkp_get_keys_thumbster(RyosRkp const *rkp);
RyosKeysExtra *ryos_rkp_get_keys_extra(RyosRkp const *rkp);
RyosKeysEasyzone *ryos_rkp_get_keys_easyzone(RyosRkp const *rkp);
RyosKeyMask *ryos_rkp_get_key_mask(RyosRkp const *rkp);
RyosLight *ryos_rkp_get_light(RyosRkp const *rkp);
RyosMacro *ryos_rkp_get_light_macro(RyosRkp const *rkp);
RyosMacro *ryos_rkp_get_macro(RyosRkp const *rkp, guint index);
RyosRkpTimer *ryos_rkp_get_timer(RyosRkp const *rkp, guint index);
RyosRkpQuicklaunch *ryos_rkp_get_quicklaunch(RyosRkp const *rkp, guint index);
RyosRkpTalk *ryos_rkp_get_talk(RyosRkp const *rkp, guint index);
RyosStoredLights *ryos_rkp_get_lights_automatic(RyosRkp const *rkp);
RyosStoredLights *ryos_rkp_get_lights_manual(RyosRkp const *rkp);

void ryos_rkp_set_profile_name(RyosRkp *rkp, gchar const *name);
void ryos_rkp_set_gamefile_name(RyosRkp *rkp, guint index, gchar const *name);
void ryos_rkp_set_keys_primary(RyosRkp *rkp, RyosKeysPrimary const *keys_primary);
void ryos_rkp_set_keys_function(RyosRkp *rkp, RyosKeysFunction const *keys_function);
void ryos_rkp_set_keys_macro(RyosRkp *rkp, RyosKeysMacro const *keys_macro);
void ryos_rkp_set_keys_thumbster(RyosRkp *rkp, RyosKeysThumbster const *keys_thumbster);
void ryos_rkp_set_keys_extra(RyosRkp *rkp, RyosKeysExtra const *keys_extra);
void ryos_rkp_set_keys_easyzone(RyosRkp *rkp, RyosKeysEasyzone const *keys_easyzone);
void ryos_rkp_set_key_to_macro_without_modified(RyosRkp *rkp, guint macro_index);
void ryos_rkp_set_key_to_macro(RyosRkp *rkp, guint macro_index);
void ryos_rkp_set_key_mask(RyosRkp *rkp, RyosKeyMask const *key_mask);
void ryos_rkp_set_light(RyosRkp *rkp, RyosLight const *light);
void ryos_rkp_set_light_macro(RyosRkp *rkp, RyosMacro const *light_macro);
void ryos_rkp_set_light_brightness_without_modified(RyosRkp *rkp, guint8 new_brightness);
void ryos_rkp_set_light_brightness(RyosRkp *rkp, guint8 new_brightness);
void ryos_rkp_set_light_dimness(RyosRkp *rkp, guint8 new_dimness);
void ryos_rkp_set_light_dimness_type(RyosRkp *rkp, guint8 new_type);
void ryos_rkp_set_light_timeout(RyosRkp *rkp, guint8 new_timeout);
void ryos_rkp_set_light_macro_exec_feedback(RyosRkp *rkp, guint8 new_feedback);
void ryos_rkp_set_light_illumination_mode(RyosRkp *rkp, guint8 new_illumination_mode);
void ryos_rkp_set_macro_without_modified(RyosRkp *rkp, guint index, RyosMacro const *macro);
void ryos_rkp_set_macro(RyosRkp *rkp, guint index, RyosMacro const *macro);
void ryos_rkp_set_timer(RyosRkp *rkp, guint index, RyosRkpTimer const *timer);
void ryos_rkp_set_quicklaunch(RyosRkp *rkp, guint index, RyosRkpQuicklaunch const *quicklaunch);
void ryos_rkp_set_talk(RyosRkp *rkp, guint index, RyosRkpTalk const *talk);
void ryos_rkp_set_led_volume_indicator(RyosRkp *rkp, guint8 new_value);
void ryos_rkp_set_lights_automatic(RyosRkp *rkp, RyosStoredLights *lights);
void ryos_rkp_set_lights_manual(RyosRkp *rkp, RyosStoredLights *lights);

#define RYOS_DEVICE_NAME "Ryos MK"
#define RYOS_DEVICE_NAME_GLOW "Ryos MK Glow"
#define RYOS_DEVICE_NAME_PRO "Ryos MK Pro"
#define RYOS_DEVICE_NAME_COMBINED "RyosMK/Glow/Pro"

RoccatDevice *ryos_device_first(void);
RoccatDeviceScanner *ryos_device_scanner_new(roccat_device_scanner_callback add_callback,
		roccat_device_scanner_callback remove_callback, gpointer user_data);
gchar *ryos_device_name(RoccatDevice const *ryos);

RoccatKeyFile *ryos_configuration_load(void);
gboolean ryos_configuration_save(RoccatKeyFile *config, GError **error);
void ryos_configuration_free(RoccatKeyFile *config);
gchar *ryos_configuration_get_rkp_path(RoccatKeyFile *config);
void ryos_configuration_set_rkp_path(RoccatKeyFile *config, gchar const *path);
gint ryos_configuration_get_timer_notification_type(RoccatKeyFile *config);
void ryos_configuration_set_timer_notification_type(RoccatKeyFile *config, gint new_value);
gint ryos_configuration_get_profile_notification_type(RoccatKeyFile *config);
void ryos_configuration_set_profile_notification_type(RoccatKeyFile *config, gint new_value);
gint ryos_configuration_get_live_recording_notification_type(RoccatKeyFile *config);
void ryos_configuration_set_live_recording_notification_type(RoccatKeyFile *config, guint new_value);
gint ryos_configuration_get_default_profile_number(RoccatKeyFile *config);
void ryos_configuration_set_default_profile_number(RoccatKeyFile *config, gint new_value);

#define RYOS_DBUS_SERVER_PATH "/org/roccat/Ryos"
#define RYOS_DBUS_SERVER_INTERFACE "org.roccat.Ryos"

DBusGProxy *ryos_dbus_proxy_new(void);
gboolean ryos_dbus_emit_profile_data_changed_outside(DBusGProxy *proxy, guint profile_index);
gboolean ryos_dbus_emit_profile_data_changed_outside_instant(guint profile_index);
gboolean ryos_dbus_emit_profile_changed_outside(DBusGProxy *proxy, guint profile_index);
gboolean ryos_dbus_emit_profile_changed_outside_instant(guint profile_index);
gboolean ryos_dbus_emit_configuration_changed_outside(DBusGProxy *proxy);

struct libusb_device_handle *ryos_libusb_open_first_device(void);
struct libusb_device_handle *ryos_libusb_open_from_roccat_device(RoccatDevice const *roccat_device);
gboolean ryos_libusb_check_firmware_write(struct libusb_device_handle *handle);
guint ryos_libusb_firmware_version_read(struct libusb_device_handle *handle);
gboolean ryos_libusb_firmware_write_0(struct libusb_device_handle *handle);
gboolean ryos_libusb_firmware_write_1(struct libusb_device_handle *handle);
gboolean ryos_libusb_firmware_write_2(struct libusb_device_handle *handle);
gboolean ryos_libusb_firmware_write_3(struct libusb_device_handle *handle, guint16 number, guint8 const *chunk);
gboolean ryos_libusb_firmware_write_4(struct libusb_device_handle *handle, guint8 const *chunk);
gboolean ryos_libusb_firmware_write_f(struct libusb_device_handle *handle);

gboolean ryos_sdk_set_kb_sdk_mode(RoccatDevice *ryos, gboolean state, GError **error);
gboolean ryos_sdk_set_all_leds(RoccatDevice *ryos, GArray const *data, GError **error);
gboolean ryos_sdk_turn_on_all_leds(RoccatDevice *ryos, GError **error);
gboolean ryos_sdk_turn_off_all_leds(RoccatDevice *ryos, GError **error);
gboolean ryos_sdk_set_led_on(RoccatDevice *ryos, guint8 index, GError **error);
gboolean ryos_sdk_set_led_off(RoccatDevice *ryos, guint8 index, GError **error);
gboolean ryos_sdk_all_key_blinking(RoccatDevice *ryos, guint interval, guint count, GError **error);

gboolean ryos_ripple_get_center(guint8 hid, guint *row, guint *col);
gboolean ryos_ripple_draw_circle(RyosCustomLights *custom_lights, gint center_row, gint center_col, gint radius, gboolean value);

G_END_DECLS

#endif
