/*
 * Copyright (c) 2011 Matej Klonfar
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 * - The name of the author may not be used to endorse or promote products
 *   derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/** @addtogroup libusb
 * @{
 */
/** @file
 * Basic data structures for USB HID Report descriptor and report parser.
 */
#ifndef LIBUSB_HIDTYPES_H_
#define LIBUSB_HIDTYPES_H_

#include <stdint.h>
#include <adt/list.h>

/**
 * Maximum amount of specified usages for one report item
 */
#define USB_HID_MAX_USAGES	0xffff

/**
 * Converts integer from unsigned two's complement format format to signed
 * one.
 *
 * @param x Number to convert
 * @param size Length of the unsigned number in bites
 * @return signed int
 */
#define USB_HID_UINT32_TO_INT32(x, size)	\
	((((x) & (1 << ((size) - 1))) != 0) ?   \
	 -(~((x) - 1) & ((1 << size) - 1)) : (x))

/**
 * Convert integer from signed format to unsigned. If number is negative the
 * two's complement format is used.
 *
 * @param x Number to convert
 * @param size Length of result number in bites
 * @return unsigned int
 */
#define USB_HID_INT32_TO_UINT32(x, size)	\
	(((x) < 0 ) ? ((1 << (size)) + (x)) : (x))

/**
 * Enum of report types
 */
typedef enum {
	/** Input report. Data are sent from device to system */
	USB_HID_REPORT_TYPE_INPUT = 1,

	/** Output report. Data are sent from system to device */
	USB_HID_REPORT_TYPE_OUTPUT = 2,

	/** Feature report. Describes device configuration information that
	 * can be sent to the device
	 */
	USB_HID_REPORT_TYPE_FEATURE = 3
} usb_hid_report_type_t;

/**
 * Description of all reports described in one report descriptor.
 */
typedef struct {
	/** Count of available reports. */
	int report_count;

	/** List of description of reports. */
	list_t reports; /* of usb_hid_report_description_t */

	/** List of all used usage/collection paths. */
	list_t collection_paths;

	/** Length of list of usage paths. */
	int collection_paths_count;

	/** Flag whether report ids are used. */
	int use_report_ids;

	/** Report id of last parsed report. */
	uint8_t last_report_id;

} usb_hid_report_t;

/**
 * Description of one concrete report
 */
typedef struct {
	/** Report id. Zero when no report id is used. */
	uint8_t report_id;

	/** Type of report */
	usb_hid_report_type_t type;

	/** Bit length of the report */
	size_t bit_length;

	/** Number of items in report */
	size_t item_length;

	/** List of report items in report */
	list_t report_items;

	/** Link to usb_hid_report_t.reports list. */
	link_t reports_link;
} usb_hid_report_description_t;

/**
 * Description of one field/item in report
 */
typedef struct {
	/** Bit offset of the field */
	int offset;

	/** Bit size of the field */
	size_t size;

	/** Usage page. Zero when usage page can be changed. */
	uint16_t usage_page;

	/** Usage. Zero when usage can be changed. */
	uint16_t usage;

	/** Item's attributes */
	uint8_t item_flags;

	/** Usage/Collection path of the field. */
	usb_hid_report_path_t *collection_path;

	/**
	 * The lowest valid logical value (value with the device operates)
	 */
	int32_t logical_minimum;

	/**
	 * The greatest valid logical value
	 */
	int32_t logical_maximum;

	/**
	 * The lowest valid physical value (value with the system operates)
	 */
	int32_t physical_minimum;

	/** The greatest valid physical value */
	int32_t physical_maximum;

	/** The lowest valid usage index */
	int32_t usage_minimum;

	/** The greatest valid usage index */
	int32_t usage_maximum;

	/** Unit of the value */
	uint32_t unit;

	/** Unit exponent */
	uint32_t unit_exponent;

	/** Array of possible usages */
	uint32_t *usages;

	/** Size of the array of usages */
	size_t usages_count;

	/** Parsed value */
	int32_t value;

	/** Link to usb_hid_report_description_t.report_items list */
	link_t ritems_link;
} usb_hid_report_field_t;

/**
 * State table for report descriptor parsing
 */
typedef struct {
	/** report id */
	int32_t id;

	/** Extended usage page */
	uint16_t extended_usage_page;

	/** Array of usages specified for this item */
	uint32_t usages[USB_HID_MAX_USAGES];

	/** Length of usages array */
	int usages_count;

	/** Usage page*/
	uint32_t usage_page;

	/** Minimum valid usage index */
	int32_t usage_minimum;

	/** Maximum valid usage index */
	int32_t usage_maximum;

	/** Minimum valid logical value */
	int32_t logical_minimum;

	/** Maximum valid logical value */
	int32_t logical_maximum;

	/** Length of the items in bits*/
	int32_t size;

	/** COunt of items*/
	int32_t count;

	/**  Bit offset of the item in report */
	size_t offset;

	/** Unit exponent */
	int32_t unit_exponent;
	/** Unit of the value */
	int32_t unit;

	/** String index */
	uint32_t string_index;

	/** Minimum valid string index */
	uint32_t string_minimum;

	/** Maximum valid string index */
	uint32_t string_maximum;

	/** The designator index */
	uint32_t designator_index;

	/** Minimum valid designator value*/
	uint32_t designator_minimum;

	/** Maximum valid designator value*/
	uint32_t designator_maximum;

	/** Minimal valid physical value*/
	int32_t physical_minimum;

	/** Maximal valid physical value */
	int32_t physical_maximum;

	/** Items attributes*/
	uint8_t item_flags;

	/** Report type */
	usb_hid_report_type_t type;

	/** current collection path*/
	usb_hid_report_path_t *usage_path;

	/** Unused*/
	link_t link;

	int in_delimiter;
} usb_hid_report_item_t;

/**
 * Enum of the keyboard modifiers
 */
typedef enum {
	USB_HID_MOD_LCTRL = 0x01,
	USB_HID_MOD_LSHIFT = 0x02,
	USB_HID_MOD_LALT = 0x04,
	USB_HID_MOD_LGUI = 0x08,
	USB_HID_MOD_RCTRL = 0x10,
	USB_HID_MOD_RSHIFT = 0x20,
	USB_HID_MOD_RALT = 0x40,
	USB_HID_MOD_RGUI = 0x80,
	USB_HID_MOD_COUNT = 8
} usb_hid_modifiers_t;

static const usb_hid_modifiers_t
    usb_hid_modifiers_consts[USB_HID_MOD_COUNT] = {
	USB_HID_MOD_LCTRL,
	USB_HID_MOD_LSHIFT,
	USB_HID_MOD_LALT,
	USB_HID_MOD_LGUI,
	USB_HID_MOD_RCTRL,
	USB_HID_MOD_RSHIFT,
	USB_HID_MOD_RALT,
	USB_HID_MOD_RGUI
};

#endif
/**
 * @}
 */
