#ifndef _ROCCAT_SAVU_H_
#define _ROCCAT_SAVU_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 <stdio.h>
#include <roccat/roccat.h>
#include <dbus/dbus-glib.h>

G_BEGIN_DECLS

#define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a

typedef struct _SavuControl SavuControl;
typedef struct _SavuProfile SavuProfile;
typedef struct _SavuGeneral SavuGeneral;
typedef struct _SavuButton SavuButton;
typedef struct _SavuButtons SavuButtons;
typedef struct _SavuKeystroke SavuKeystroke;
typedef struct _SavuMacro SavuMacro;
typedef struct _SavuInfo SavuInfo;
typedef struct _SavuSensor SavuSensor;
typedef struct _SavuFirmware SavuFirmware;
typedef struct _SavuRmp SavuRmp;
typedef struct _SavuRmpTimer SavuRmpTimer;
typedef struct _SavuRmpMacroKeyInfo SavuRmpMacroKeyInfo;
typedef struct _SavuChrdevEvent SavuChrdevEvent;

enum {
	SAVU_PROFILE_NUM = 5,
	SAVU_PHYSICAL_BUTTON_NUM = 7,
	SAVU_BUTTON_NUM = 2 * SAVU_PHYSICAL_BUTTON_NUM,
	SAVU_CPI_NUM = 4,
	SAVU_FIRMWARE_SIZE = 24076,
	SAVU_FIRMWARE_CHUNK_SIZE = 1024,
	SAVU_FIRMWARE_CHUNKS = 24,
	SAVU_GENERAL_CPI_LEVELS_NUM = 4,
	SAVU_GENERAL_SENSITIVITY_MIN = 0x1,
	SAVU_GENERAL_SENSITIVITY_MAX = 0xb,
	SAVU_MACRO_MACROSET_NAME_LENGTH = 24,
	SAVU_MACRO_MACRO_NAME_LENGTH = 24,
	SAVU_MACRO_KEYSTROKES_NUM = 500,
	SAVU_RMP_MACRO_KEY_INFO_NAME_LENGTH = 52,
	SAVU_RMP_TIMER_NAME_LENGTH = 24,
	SAVU_RMP_GAME_FILE_NUM = 3,
	SAVU_SINGLE_COLOR_NUM = 16,
	SAVU_GENERAL_COLOR_INDEX_CUSTOM = 0x10,
	SAVU_SENSOR_FRAME_WIDTH = 30,
	SAVU_SENSOR_FRAME_HEIGHT = 30,
	SAVU_SENSOR_FRAME_SIZE = SAVU_SENSOR_FRAME_WIDTH * SAVU_SENSOR_FRAME_HEIGHT,
	SAVU_GAMEFILE_NUM = 3,
};

/* also valid as SavuFirmwareControl */
struct _SavuControl {
	guint8 command; /* SAVU_COMMAND_CONTROL */
	guint8 value;
	guint8 request;
} __attribute__ ((packed));

gboolean savu_select(RoccatDevice *savu, guint profile_index, guint request, GError **error);

typedef enum {
	SAVU_CONTROL_VALUE_CRITICAL = 0,
	SAVU_CONTROL_VALUE_OK = 1,
	SAVU_CONTROL_VALUE_INVALID = 2,
	SAVU_CONTROL_VALUE_BUSY = 3,
} SavuControlValue;

typedef enum {
	SAVU_CONTROL_REQUEST_WRITE_CHECK = 0x00, /* SavuControl.value = SavuControlValue */

	/*
	 * To request macro readout:
	 * SavuControl.value = profile_index
	 * SavuControl.request = button_index in range 2-13
	 */

	SAVU_CONTROL_REQUEST_GENERAL = 0x80, /* SavuControl.value = profile_index */
	SAVU_CONTROL_REQUEST_BUTTONS = 0x90, /* SavuControl.value = profile_index */
} SavuControlRequest;

struct _SavuProfile {
	guint8 command; /* SAVU_COMMAND_PROFILE */
	guint8 size; /* 0x3 */
	guint8 profile_index; /* range 0-4 */
} __attribute__ ((packed));

guint savu_profile_read(RoccatDevice *savu, GError **error);
gboolean savu_profile_write(RoccatDevice *savu, guint profile_index, GError **error);

struct _SavuGeneral {
	guint8 command; /* SAVU_COMMAND_GENERAL */
	guint8 size; /* 0x10 */
	guint8 profile_index; /* range 0-4 */
	guint8 sensitivity_sync;
	guint8 sensitivity_x; /* range SAVU_GENERAL_SENSITIVITY_MIN - SAVU_GENERAL_SENSITIVITY_MAX (-5 - +5) */
	guint8 sensitivity_y; /* range SAVU_GENERAL_SENSITIVITY_MIN - SAVU_GENERAL_SENSITIVITY_MAX (-5 - +5) */
	guint8 cpi_mask;
	guint8 cpi;
	guint8 polling_rate;
	guint8 lighting_mask;
	guint8 color_index; /* range 0x0 - 0xf, 0x10 is custom color */
	guint8 red;
	guint8 green;
	guint8 blue;
	guint16 checksum;
} __attribute__ ((packed));

SavuGeneral *savu_general_read(RoccatDevice *savu, guint profile_index, GError **error);
gboolean savu_general_write(RoccatDevice *savu, guint profile_index, SavuGeneral *general, GError **error);
guint16 savu_general_calc_checksum(SavuGeneral const *general);

static inline guint16 savu_general_get_checksum(SavuGeneral const *general) {
	return GUINT16_FROM_LE(general->checksum);
}

static inline void savu_general_set_checksum(SavuGeneral *general, guint16 new_value) {
	general->checksum = GUINT16_TO_LE(new_value);
}

typedef enum {
	SAVU_GENERAL_SENSITIVITY_SYNC_ON = 0,
	SAVU_GENERAL_SENSITIVITY_SYNC_OFF = 1,
} SavuGeneralSensitivitySync;

typedef enum {
	SAVU_GENERAL_CPI_MASK_BIT_400 = 0,
	SAVU_GENERAL_CPI_MASK_BIT_800 = 1,
	SAVU_GENERAL_CPI_MASK_BIT_1600 = 2,
	SAVU_GENERAL_CPI_MASK_BIT_4000 = 3,
} SavuGeneralCpiMaskBit;

typedef enum {
	SAVU_GENERAL_CPI_400 = 0,
	SAVU_GENERAL_CPI_800 = 1,
	SAVU_GENERAL_CPI_1600 = 2,
	SAVU_GENERAL_CPI_4000 = 3,
} SavuGeneralCpi;

typedef enum {
	SAVU_GENERAL_POLLING_RATE_125 = 0,
	SAVU_GENERAL_POLLING_RATE_250 = 1,
	SAVU_GENERAL_POLLING_RATE_500 = 2,
	SAVU_GENERAL_POLLING_RATE_1000 = 3,
} SavuGeneralPollingRate;

typedef enum {
	SAVU_GENERAL_LIGHTING_MASK_BIT_LIGHT = 0,
	SAVU_GENERAL_LIGHTING_MASK_BIT_BREATHING = 1,
	SAVU_GENERAL_LIGHTING_MASK_BIT_COLOR_FLOW = 2,
} SavuGeneralLightingMask;

/* also valid as SavuRmpButton */
struct _SavuButton {
	guint8 type;
	guint8 modifier;
	guint8 key;
} __attribute__ ((packed));

void savu_button_set_to_shortcut(SavuButton *button, guint hid_usage_id, guint8 modifier);
guint8 savu_button_build_shortcut_modifier(gboolean ctrl, gboolean shift, gboolean alt, gboolean win);
void savu_button_set_to_normal(SavuButton *button, guint type);

typedef enum {
	SAVU_BUTTON_TYPE_CLICK = 0x01,
	SAVU_BUTTON_TYPE_MENU = 0x02,
	SAVU_BUTTON_TYPE_UNIVERSAL_SCROLLING = 0x03,
	SAVU_BUTTON_TYPE_DOUBLE_CLICK = 0x04,
	SAVU_BUTTON_TYPE_SHORTCUT = 0x05,
	SAVU_BUTTON_TYPE_DISABLED = 0x06,
	SAVU_BUTTON_TYPE_IE_FORWARD = 0x07,
	SAVU_BUTTON_TYPE_IE_BACKWARD = 0x08,
	SAVU_BUTTON_TYPE_TILT_LEFT = 0x09,
	SAVU_BUTTON_TYPE_TILT_RIGHT = 0x0a,
	SAVU_BUTTON_TYPE_SCROLL_UP = 0x0d,
	SAVU_BUTTON_TYPE_SCROLL_DOWN = 0x0e,
	SAVU_BUTTON_TYPE_QUICKLAUNCH = 0x0f,
	SAVU_BUTTON_TYPE_PROFILE_CYCLE = 0x10,
	SAVU_BUTTON_TYPE_PROFILE_UP = 0x11,
	SAVU_BUTTON_TYPE_PROFILE_DOWN = 0x12,
	SAVU_BUTTON_TYPE_CPI_CYCLE = 0x14,
	SAVU_BUTTON_TYPE_CPI_UP = 0x15,
	SAVU_BUTTON_TYPE_CPI_DOWN = 0x16,
	SAVU_BUTTON_TYPE_SENSITIVITY_CYCLE = 0x17,
	SAVU_BUTTON_TYPE_SENSITIVITY_UP = 0x18,
	SAVU_BUTTON_TYPE_SENSITIVITY_DOWN = 0x19,
	SAVU_BUTTON_TYPE_WINDOWS_KEY = 0x1a,
	SAVU_BUTTON_TYPE_OPEN_DRIVER = 0x1b,
	SAVU_BUTTON_TYPE_MULTIMEDIA_OPEN_PLAYER = 0x20,
	SAVU_BUTTON_TYPE_MULTIMEDIA_PREV_TRACK = 0x21,
	SAVU_BUTTON_TYPE_MULTIMEDIA_NEXT_TRACK = 0x22,
	SAVU_BUTTON_TYPE_MULTIMEDIA_PLAY_PAUSE = 0x23,
	SAVU_BUTTON_TYPE_MULTIMEDIA_STOP = 0x24,
	SAVU_BUTTON_TYPE_MULTIMEDIA_MUTE = 0x25,
	SAVU_BUTTON_TYPE_MULTIMEDIA_VOLUME_UP = 0x26,
	SAVU_BUTTON_TYPE_MULTIMEDIA_VOLUME_DOWN = 0x27,
	SAVU_BUTTON_TYPE_MACRO = 0x30,
	SAVU_BUTTON_TYPE_TIMER = 0x31,
	SAVU_BUTTON_TYPE_TIMER_STOP = 0x32,
	SAVU_BUTTON_TYPE_EASYAIM_400 = 0x33,
	SAVU_BUTTON_TYPE_EASYAIM_800 = 0x34,
	SAVU_BUTTON_TYPE_EASYAIM_1600 = 0x35,
	SAVU_BUTTON_TYPE_EASYAIM_4000 = 0x36,
	SAVU_BUTTON_TYPE_EASYSHIFT = 0x41,
	SAVU_BUTTON_TYPE_EASYWHEEL_SENSITIVITY = 0x42,
	SAVU_BUTTON_TYPE_EASYWHEEL_PROFILE = 0x43,
	SAVU_BUTTON_TYPE_EASYWHEEL_CPI = 0x44,
	SAVU_BUTTON_TYPE_EASYWHEEL_VOLUME = 0x45,
	SAVU_BUTTON_TYPE_EASYWHEEL_ALT_TAG = 0x46,
} SavuButtonType;

typedef enum {
	SAVU_BUTTON_MODIFIER_BIT_NONE = 0,
	SAVU_BUTTON_MODIFIER_BIT_SHIFT = 1,
	SAVU_BUTTON_MODIFIER_BIT_CTRL = 2,
	SAVU_BUTTON_MODIFIER_BIT_ALT = 3,
	SAVU_BUTTON_MODIFIER_BIT_WIN = 4,
} SavuButtonModifier;

struct _SavuButtons {
	guint8 command; /* SAVU_COMMAND_BUTTONS */
	guint8 size; /* 0x2f */
	guint8 profile_index; /* range 0-4 */
	SavuButton buttons[SAVU_BUTTON_NUM];
	guint16 checksum;
} __attribute__ ((packed));

SavuButtons *savu_buttons_read(RoccatDevice *savu, guint profile_index, GError **error);
gboolean savu_buttons_write(RoccatDevice *savu, guint profile_index, SavuButtons *buttons, GError **error);
guint16 savu_buttons_calc_checksum(SavuButtons const *buttons);

static inline guint16 savu_buttons_get_checksum(SavuButtons const *buttons) {
	return GUINT16_FROM_LE(buttons->checksum);
}

static inline void savu_buttons_set_checksum(SavuButtons *buttons, guint16 new_value) {
	buttons->checksum = GUINT16_TO_LE(new_value);
}

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

typedef enum {
	SAVU_KEYSTROKE_ACTION_PRESS = 1,
	SAVU_KEYSTROKE_ACTION_RELEASE = 2,
} SavuKeystrokeAction;

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

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

struct _SavuMacro {
	guint8 command; /* SAVU_COMMAND_MACRO */
	guint16 size; /* 0x0823 */
	guint8 profile_index; /* range 0-4 */
	guint8 button_index; /* range 0-13 */
	guint8 unused1; /* 0 */
	guint8 loop;
	guint8 unused2[24];
	guint8 macroset_name[SAVU_MACRO_MACROSET_NAME_LENGTH];
	guint8 macro_name[SAVU_MACRO_MACRO_NAME_LENGTH];
	guint16 count;
	SavuKeystroke keystrokes[SAVU_MACRO_KEYSTROKES_NUM];
	guint16 checksum;
} __attribute__ ((packed));

SavuMacro *savu_macro_read(RoccatDevice *savu, guint profile_index, guint button_index, GError **error);
gboolean savu_macro_write(RoccatDevice *savu, guint profile_index, guint button_index, SavuMacro *macro, GError **error);
guint16 savu_macro_calc_checksum(SavuMacro const *macro);

static inline guint16 savu_macro_get_size(SavuMacro const *macro) {
	return GUINT16_FROM_LE(macro->size);
}

static inline void savu_macro_set_size(SavuMacro *macro, guint16 new_value) {
	macro->size = GUINT16_TO_LE(new_value);
}

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

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

static inline guint16 savu_macro_get_checksum(SavuMacro const *macro) {
	return GUINT16_FROM_LE(macro->checksum);
}

static inline void savu_macro_set_checksum(SavuMacro *macro, guint16 new_value) {
	macro->checksum = GUINT16_TO_LE(new_value);
}

/* also valid as SavuMacroButtonIndex */
typedef enum {
	SAVU_BUTTON_INDEX_LEFT = 0,
	SAVU_BUTTON_INDEX_RIGHT = 1,
	SAVU_BUTTON_INDEX_MIDDLE = 2,
	SAVU_BUTTON_INDEX_FORWARD = 3,
	SAVU_BUTTON_INDEX_BACKWARD = 4,
	SAVU_BUTTON_INDEX_WHEEL_UP = 5,
	SAVU_BUTTON_INDEX_WHEEL_DOWN = 6,
	SAVU_BUTTON_INDEX_SHIFT_LEFT = 7,
	SAVU_BUTTON_INDEX_SHIFT_RIGHT = 8,
	SAVU_BUTTON_INDEX_SHIFT_MIDDLE = 9,
	SAVU_BUTTON_INDEX_SHIFT_FORWARD = 10,
	SAVU_BUTTON_INDEX_SHIFT_BACKWARD = 11,
	SAVU_BUTTON_INDEX_SHIFT_WHEEL_UP = 12,
	SAVU_BUTTON_INDEX_SHIFT_WHEEL_DOWN = 13,
} SavuButtonIndex;

struct _SavuInfo {
	guint8 command; /* SAVU_COMMAND_INFO */
	guint8 size; /* 0x08 */
	guint8 firmware_version;
	guint8 unknown1;
	guint8 unknown2;
	guint8 unknown3;
	guint8 sensor_srom_id;
	guint8 unknown5;
} __attribute__ ((packed));

typedef enum {
	SAVU_INFO_FUNCTION_RESET = 0x6,
} SavuInfoFunction;

SavuInfo *savu_info_read(RoccatDevice *savu, GError **error);
guint savu_firmware_version_read(RoccatDevice *savu, GError **error);
gchar *savu_firmware_version_to_string(guint firmware_version);
gboolean savu_reset(RoccatDevice *savu, GError **error);

struct _SavuA {
	guint8 command; /* SAVU_COMMAND_A */
	guint8 size; /* 0x3 */
	guint8 unknown;
} __attribute__ ((packed));

struct _SavuSensor {
	guint8 command; /* SAVU_COMMAND_SENSOR */
	guint8 action;
	guint8 reg;
	guint8 value;
} __attribute__ ((packed));

typedef enum {
	SAVU_SENSOR_ACTION_WRITE = 1,
	SAVU_SENSOR_ACTION_READ = 2,
	SAVU_SENSOR_ACTION_FRAME_CAPTURE = 3,
} SavuSensorAction;

gboolean savu_sensor_write_value(RoccatDevice *savu, guint8 reg, guint8 value, GError **error);
guint8 savu_sensor_read_value(RoccatDevice *savu, guint8 reg, GError **error);

struct _SavuFirmware {
	guint8 command; /* SAVU_COMMAND_FIRMWARE */
	guint8 number;
	guint8 data[SAVU_FIRMWARE_CHUNK_SIZE];
} __attribute__ ((packed));

typedef enum {
	SAVU_COMMAND_CONTROL = 0x04,
	SAVU_COMMAND_PROFILE = 0x05,
	SAVU_COMMAND_GENERAL = 0x06,
	SAVU_COMMAND_BUTTONS = 0x07,
	SAVU_COMMAND_MACRO = 0x08,
	SAVU_COMMAND_INFO = 0x09,
	SAVU_COMMAND_A = 0x0a, /* wo */
	SAVU_COMMAND_SENSOR = 0x0c,
	SAVU_COMMAND_D = 0x0d,
	SAVU_COMMAND_FIRMWARE = 0x1b,
	SAVU_COMMAND_FIRMWARE_CONTROL = 0x1c, /* struct uses SAVU_COMMAND_CONTROL */
} SavuCommand;

static inline guint16 savu_usb_command(guint8 command) { return 0x300 | command; }

struct _SavuRmpTimer {
	guint32 seconds;
	guint8 name[SAVU_RMP_TIMER_NAME_LENGTH];
} __attribute__ ((packed));

RoccatTimer *savu_rmp_timer_to_roccat_timer(SavuRmpTimer const *rmp_timer);
SavuRmpTimer *savu_roccat_timer_to_rmp_timer(RoccatTimer const *timer);

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

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

struct _SavuRmpMacroKeyInfo {
	guint8 name[SAVU_RMP_MACRO_KEY_INFO_NAME_LENGTH];
	guint32 loop;
	guint32 count;
	SavuKeystroke keystrokes[SAVU_MACRO_KEYSTROKES_NUM];
	guint8 checksum;
} __attribute__ ((packed));

SavuRmpMacroKeyInfo *savu_rmp_macro_key_info_new(void);
SavuRmpMacroKeyInfo *savu_rmp_macro_key_info_dup(SavuRmpMacroKeyInfo const *source);
void savu_rmp_macro_key_info_free(SavuRmpMacroKeyInfo *macro_key_info);
SavuMacro *savu_rmp_macro_key_info_to_macro(SavuRmpMacroKeyInfo const *macro_key_info);
RoccatMacro *savu_rmp_macro_key_info_to_roccat_macro(SavuRmpMacroKeyInfo const *macro);
SavuRmpMacroKeyInfo *savu_roccat_macro_to_rmp_macro_key_info(RoccatMacro const *roccat_macro, GError **error);

static inline guint32 savu_rmp_macro_key_info_get_loop(SavuRmpMacroKeyInfo const *macro_key_info) {
	return GUINT32_FROM_LE(macro_key_info->loop);
}

static inline void savu_rmp_macro_key_info_set_loop(SavuRmpMacroKeyInfo *macro_key_info, guint32 new_value) {
	macro_key_info->loop = GUINT32_TO_LE(new_value);
}

static inline guint32 savu_rmp_macro_key_info_get_count(SavuRmpMacroKeyInfo const *macro_key_info) {
	return GUINT32_FROM_LE(macro_key_info->count);
}

static inline void savu_rmp_macro_key_info_set_count(SavuRmpMacroKeyInfo *macro_key_info, guint32 new_value) {
	macro_key_info->count = GUINT32_TO_LE(new_value);
}

struct _SavuChrdevEvent {
	guint8 type;
	guint8 data[2];
} __attribute__ ((packed));

typedef enum {
	SAVU_CHRDEV_EVENT_ACTION_PRESS = 0,
	SAVU_CHRDEV_EVENT_ACTION_RELEASE = 1,
} SavuChrdevEventAction;

typedef enum {
	SAVU_CHRDEV_EVENT_TYPE_TILT = 0x10,
	SAVU_CHRDEV_EVENT_TYPE_PROFILE = 0x20,

	/* data[0] = count, usually 0x0a */
	SAVU_CHRDEV_EVENT_TYPE_RAD_LEFT = 0x30,
	SAVU_CHRDEV_EVENT_TYPE_RAD_RIGHT = 0x31,
	SAVU_CHRDEV_EVENT_TYPE_RAD_MIDDLE = 0x32,
	SAVU_CHRDEV_EVENT_TYPE_RAD_THUMB_1 = 0x33,
	SAVU_CHRDEV_EVENT_TYPE_RAD_THUMB_2 = 0x34,
	SAVU_CHRDEV_EVENT_TYPE_RAD_SCROLL_UP = 0x35,
	SAVU_CHRDEV_EVENT_TYPE_RAD_SCROLL_DOWN = 0x36,
	SAVU_CHRDEV_EVENT_TYPE_RAD_EASYSHIFT = 0x37,
	SAVU_CHRDEV_EVENT_TYPE_RAD_EASYAIM = 0x38,
	SAVU_CHRDEV_EVENT_TYPE_RAD_DISTANCE = 0x39, // TOTO unit

	SAVU_CHRDEV_EVENT_TYPE_QUICKLAUNCH = 0x60,
	SAVU_CHRDEV_EVENT_TYPE_TIMER = 0x80,
	SAVU_CHRDEV_EVENT_TYPE_TIMER_STOP = 0x90,
	SAVU_CHRDEV_EVENT_TYPE_OPEN_DRIVER = 0xa0,
	SAVU_CHRDEV_EVENT_TYPE_CPI = 0xb0,
	SAVU_CHRDEV_EVENT_TYPE_SENSITIVITY = 0xc0,
	SAVU_CHRDEV_EVENT_TYPE_MULTIMEDIA = 0xf0,
} SavuChrdevEventType;

RoccatDevice *savu_device_first(void);
RoccatDeviceScanner *savu_device_scanner_new(roccat_device_scanner_callback add_callback,
		roccat_device_scanner_callback remove_callback, gpointer user_data);

SavuRmp *savu_rmp_load(RoccatDevice *savu, guint profile_index, GError **error);
gboolean savu_rmp_save(RoccatDevice *savu, SavuRmp *rmp, guint profile_index, GError **error);
SavuRmp *savu_rmp_load_save_after_reset(RoccatDevice *savu, guint profile_index, GError **error);
SavuRmp *savu_rmp_load_actual(guint profile_index);
gboolean savu_rmp_save_actual(SavuRmp *rmp, guint profile_index, GError **error);
void savu_rmp_update_from_device(RoccatDevice *savu, SavuRmp *rmp, guint profile_index);
gboolean savu_rmp_get_modified(SavuRmp const *rmp);
void savu_rmp_set_modified(SavuRmp *rmp);
void savu_rmp_set_unmodified(SavuRmp *rmp);
void savu_rmp_free(SavuRmp *rmp);
SavuRmp *savu_rmp_dup(SavuRmp *src);
SavuRmp *savu_rmp_read_with_path(gchar const *path, GError **error);
gboolean savu_rmp_write_with_path(gchar const *path, SavuRmp *rmp, GError **error);
SavuRmp *savu_default_rmp(void);

/* returns NULL if button is not a macro */
SavuMacro *savu_rmp_to_macro(SavuRmp *rmp, guint index);

SavuButtons *savu_rmp_to_buttons(SavuRmp *rmp);
SavuGeneral *savu_rmp_to_general(SavuRmp *rmp);

void savu_rmp_update_with_roccat_timer(SavuRmp *rmp, guint index, RoccatTimer const *timer);
void savu_rmp_update_with_macro(SavuRmp *rmp, guint index, SavuMacro const *macro);
void savu_rmp_update_with_general(SavuRmp *rmp, SavuGeneral *general);
void savu_rmp_update_with_buttons(SavuRmp *rmp, SavuButtons *buttons);

gchar *savu_rmp_get_profile_name(SavuRmp *rmp);
void savu_rmp_set_profile_name(SavuRmp *rmp, gchar const *string);

/* Deceptive: A value of 1 means async (Advanced on)
 * Windows driver stores a value of 6 for x and y if this value is 0 (Advanced off)
 */
void savu_rmp_set_xy_synchronous(SavuRmp *rmp, guint value);
guint savu_rmp_get_xy_synchronous(SavuRmp *rmp);

void savu_rmp_set_sensitivity_x(SavuRmp *rmp, guint value);
guint savu_rmp_get_sensitivity_x(SavuRmp *rmp);
void savu_rmp_set_sensitivity_y(SavuRmp *rmp, guint value);
guint savu_rmp_get_sensitivity_y(SavuRmp *rmp);
void savu_rmp_set_color_red(SavuRmp *rmp, guint value);
guint savu_rmp_get_color_red(SavuRmp *rmp);
void savu_rmp_set_color_green(SavuRmp *rmp, guint value);
guint savu_rmp_get_color_green(SavuRmp *rmp);
void savu_rmp_set_color_blue(SavuRmp *rmp, guint value);
guint savu_rmp_get_color_blue(SavuRmp *rmp);
void savu_rmp_set_polling_rate(SavuRmp *rmp, guint value);
guint savu_rmp_get_polling_rate(SavuRmp *rmp);
void savu_rmp_set_color_database(SavuRmp *rmp, guint value);
guint savu_rmp_get_color_database(SavuRmp *rmp);
void savu_rmp_set_cpi_step(SavuRmp *rmp, guint value);
guint savu_rmp_get_cpi_step(SavuRmp *rmp);
void savu_rmp_set_cpi(SavuRmp *rmp, guint index, guint value);
guint savu_rmp_get_cpi(SavuRmp *rmp, guint index);
void savu_rmp_set_light_switch(SavuRmp *rmp, guint value);
guint savu_rmp_get_light_switch(SavuRmp *rmp);
void savu_rmp_set_breath_on(SavuRmp *rmp, guint value);
guint savu_rmp_get_breath_on(SavuRmp *rmp);
void savu_rmp_set_color_flow(SavuRmp *rmp, guint value);
guint savu_rmp_get_color_flow(SavuRmp *rmp);
SavuRmpMacroKeyInfo *savu_rmp_get_macro_key_info(SavuRmp *rmp, guint index);
void savu_rmp_set_macro_key_info(SavuRmp *rmp, guint index, SavuRmpMacroKeyInfo const *key_info);
SavuRmpTimer *savu_rmp_get_timer(SavuRmp *rmp, guint index);
void savu_rmp_set_timer(SavuRmp *rmp, guint index, SavuRmpTimer const *timer);
SavuButton *savu_rmp_get_button(SavuRmp *rmp, guint index);
void savu_rmp_set_button(SavuRmp *rmp, guint index, SavuButton const *button);
gchar *savu_rmp_get_game_file(SavuRmp *rmp, guint index);
void savu_rmp_set_game_file(SavuRmp *rmp, guint index, gchar const *string);
gchar *savu_rmp_get_launchpath(SavuRmp *rmp, guint index);
void savu_rmp_set_launchpath(SavuRmp *rmp, guint index, gchar const *string);

struct libusb_device_handle *savu_libusb_open_first_device(void);
gboolean savu_libusb_check_firmware_write(struct libusb_device_handle *handle);
guint savu_libusb_firmware_version_read(struct libusb_device_handle *handle);
gboolean savu_libusb_firmware_write(struct libusb_device_handle *handle, SavuFirmware const *data);

RoccatKeyFile *savu_configuration_load(void);
void savu_configuration_free(RoccatKeyFile *config);
gboolean savu_configuration_save(RoccatKeyFile *config, GError **error);

gchar *savu_configuration_get_rmp_path(RoccatKeyFile *config);
void savu_configuration_set_rmp_path(RoccatKeyFile *config, gchar const *path);

gchar *savu_configuration_get_macro_path(RoccatKeyFile *config);
void savu_configuration_set_macro_path(RoccatKeyFile *config, gchar const *path);

gchar *savu_configuration_get_timer_path(RoccatKeyFile *config);
void savu_configuration_set_timer_path(RoccatKeyFile *config, gchar const *path);

gint savu_configuration_get_timer_notification_type(RoccatKeyFile *config);
void savu_configuration_set_timer_notification_type(RoccatKeyFile *config, gint new_value);

gint savu_configuration_get_profile_notification_type(RoccatKeyFile *config);
void savu_configuration_set_profile_notification_type(RoccatKeyFile *config, gint new_value);

gint savu_configuration_get_default_profile_number(RoccatKeyFile *config);
void savu_configuration_set_default_profile_number(RoccatKeyFile *config, gint new_value);

#define SAVU_DEVICE_NAME "Savu"

#define SAVU_DBUS_SERVER_PATH "/org/roccat/Savu"
#define SAVU_DBUS_SERVER_INTERFACE "org.roccat.Savu"

DBusGProxy *savu_dbus_proxy_new(void);
gboolean savu_dbus_emit_profile_data_changed_outside(DBusGProxy *proxy, guint profile_number);
gboolean savu_dbus_emit_profile_data_changed_outside_instant(guint profile_number);
gboolean savu_dbus_emit_profile_changed_outside(DBusGProxy *proxy, guint profile_number);
gboolean savu_dbus_emit_profile_changed_outside_instant(guint profile_number);
gboolean savu_dbus_emit_configuration_changed_outside(DBusGProxy *proxy);

gboolean savu_play_quicklaunch_forked(SavuRmp *rmp, guint index);

G_END_DECLS

#endif
