/*******************************************************
 HIDAPI - Multi-Platform library for
 communication with HID devices.

 libusb/hidapi Team

 Copyright 2022, All Rights Reserved.

 At the discretion of the user of this library,
 this software may be licensed under the terms of the
 GNU General Public License v3, a BSD-Style license, or the
 original HIDAPI license as outlined in the LICENSE.txt,
 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
 files located at the root of the source distribution.
 These files may also be found in the public source
 code repository located at:
        https://github.com/libusb/hidapi .
********************************************************/
#ifndef HIDAPI_DESCRIPTOR_RECONSTRUCT_H__
#define HIDAPI_DESCRIPTOR_RECONSTRUCT_H__

#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
/* Do not warn about wcsncpy usage.
   https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "hidapi_winapi.h"

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4200)
#pragma warning(disable: 4201)
#pragma warning(disable: 4214)
#endif

#include <windows.h>

#include "hidapi_hidsdi.h"
#include <assert.h>

#define NUM_OF_HIDP_REPORT_TYPES 3

typedef enum rd_items_ {
	rd_main_input               = 0x80, /* 1000 00 nn */
	rd_main_output              = 0x90, /* 1001 00 nn */
	rd_main_feature             = 0xB0, /* 1011 00 nn */
	rd_main_collection          = 0xA0, /* 1010 00 nn */
	rd_main_collection_end      = 0xC0, /* 1100 00 nn */
	rd_global_usage_page        = 0x04, /* 0000 01 nn */
	rd_global_logical_minimum   = 0x14, /* 0001 01 nn */
	rd_global_logical_maximum   = 0x24, /* 0010 01 nn */
	rd_global_physical_minimum  = 0x34, /* 0011 01 nn */
	rd_global_physical_maximum  = 0x44, /* 0100 01 nn */
	rd_global_unit_exponent     = 0x54, /* 0101 01 nn */
	rd_global_unit              = 0x64, /* 0110 01 nn */
	rd_global_report_size       = 0x74, /* 0111 01 nn */
	rd_global_report_id         = 0x84, /* 1000 01 nn */
	rd_global_report_count      = 0x94, /* 1001 01 nn */
	rd_global_push              = 0xA4, /* 1010 01 nn */
	rd_global_pop               = 0xB4, /* 1011 01 nn */
	rd_local_usage              = 0x08, /* 0000 10 nn */
	rd_local_usage_minimum      = 0x18, /* 0001 10 nn */
	rd_local_usage_maximum      = 0x28, /* 0010 10 nn */
	rd_local_designator_index   = 0x38, /* 0011 10 nn */
	rd_local_designator_minimum = 0x48, /* 0100 10 nn */
	rd_local_designator_maximum = 0x58, /* 0101 10 nn */
	rd_local_string             = 0x78, /* 0111 10 nn */
	rd_local_string_minimum     = 0x88, /* 1000 10 nn */
	rd_local_string_maximum     = 0x98, /* 1001 10 nn */
	rd_local_delimiter          = 0xA8  /* 1010 10 nn */
} rd_items;

typedef enum rd_main_items_ {
	rd_input = HidP_Input,
	rd_output = HidP_Output,
	rd_feature = HidP_Feature,
	rd_collection,
	rd_collection_end,
	rd_delimiter_open,
	rd_delimiter_usage,
	rd_delimiter_close,
} rd_main_items;

typedef struct rd_bit_range_ {
	int FirstBit;
	int LastBit;
} rd_bit_range;

typedef enum rd_item_node_type_ {
	rd_item_node_cap,
	rd_item_node_padding,
	rd_item_node_collection,
} rd_node_type;

struct rd_main_item_node {
	int FirstBit; /* Position of first bit in report (counting from 0) */
	int LastBit; /* Position of last bit in report (counting from 0) */
	rd_node_type TypeOfNode; /* Information if caps index refers to the array of button caps, value caps,
	                            or if the node is just a padding element to fill unused bit positions.
	                            The node can also be a collection node without any bits in the report. */
	int CapsIndex; /* Index in the array of caps */
	int CollectionIndex; /* Index in the array of link collections */
	rd_main_items MainItemType; /* Input, Output, Feature, Collection or Collection End */
	unsigned char ReportID;
	struct rd_main_item_node* next;
};

typedef struct hid_pp_caps_info_ {
	USHORT FirstCap;
	USHORT NumberOfCaps; // Includes empty caps after LastCap 
	USHORT LastCap;
	USHORT ReportByteLength;
} hid_pp_caps_info, *phid_pp_caps_info;

typedef struct hid_pp_link_collection_node_ {
	USAGE  LinkUsage;
	USAGE  LinkUsagePage;
	USHORT Parent;
	USHORT NumberOfChildren;
	USHORT NextSibling;
	USHORT FirstChild;
	ULONG  CollectionType : 8;
	ULONG  IsAlias : 1;
	ULONG  Reserved : 23;
	// Same as the public API structure HIDP_LINK_COLLECTION_NODE, but without PVOID UserContext at the end
} hid_pp_link_collection_node, *phid_pp_link_collection_node;

// Note: This is risk-reduction-measure for this specific struct, as it has ULONG bit-field.
//       Although very unlikely, it might still be possible that the compiler creates a memory layout that is
//       not binary compatile.
//       Other structs are not checked at the time of writing.
static_assert(sizeof(struct hid_pp_link_collection_node_) == 16,
    "Size of struct hid_pp_link_collection_node_ not as expected. This might break binary compatibility");

typedef struct hidp_unknown_token_ {
	UCHAR Token; /* Specifies the one-byte prefix of a global item. */
	UCHAR Reserved[3];
	ULONG BitField; /* Specifies the data part of the global item. */
} hidp_unknown_token, * phidp_unknown_token;

typedef struct hid_pp_cap_ {
	USAGE   UsagePage;
	UCHAR   ReportID;
	UCHAR   BitPosition;
	USHORT  ReportSize; // WIN32 term for this is BitSize
	USHORT  ReportCount;
	USHORT  BytePosition;
	USHORT  BitCount;
	ULONG   BitField;
	USHORT  NextBytePosition;
	USHORT  LinkCollection;
	USAGE   LinkUsagePage;
	USAGE   LinkUsage;

	// Start of 8 Flags in one byte
	BOOLEAN IsMultipleItemsForArray:1;

	BOOLEAN IsPadding:1;
	BOOLEAN IsButtonCap:1;
	BOOLEAN IsAbsolute:1;
	BOOLEAN IsRange:1;
	BOOLEAN IsAlias:1; // IsAlias is set to TRUE in the first n-1 capability structures added to the capability array. IsAlias set to FALSE in the nth capability structure.
	BOOLEAN IsStringRange:1;
	BOOLEAN IsDesignatorRange:1;
	// End of 8 Flags in one byte
	BOOLEAN Reserved1[3];

	hidp_unknown_token UnknownTokens[4]; // 4 x 8 Byte

	union {
		struct {
			USAGE  UsageMin;
			USAGE  UsageMax;
			USHORT StringMin;
			USHORT StringMax;
			USHORT DesignatorMin;
			USHORT DesignatorMax;
			USHORT DataIndexMin;
			USHORT DataIndexMax;
		} Range;
		struct {
			USAGE  Usage;
			USAGE  Reserved1;
			USHORT StringIndex;
			USHORT Reserved2;
			USHORT DesignatorIndex;
			USHORT Reserved3;
			USHORT DataIndex;
			USHORT Reserved4;
		} NotRange;
	};
	union {
		struct {
			LONG    LogicalMin;
			LONG    LogicalMax;
		} Button;
		struct {
			BOOLEAN HasNull;
			UCHAR   Reserved4[3];
			LONG    LogicalMin;
			LONG    LogicalMax;
			LONG    PhysicalMin;
			LONG    PhysicalMax;
		} NotButton;
	};
	ULONG   Units;
	ULONG   UnitsExp;

} hid_pp_cap, *phid_pp_cap;

typedef struct hidp_preparsed_data_ {
	UCHAR MagicKey[8];
	USAGE Usage;
	USAGE UsagePage;
	USHORT Reserved[2];

	// CAPS structure for Input, Output and Feature
	hid_pp_caps_info caps_info[3];

	USHORT FirstByteOfLinkCollectionArray;
	USHORT NumberLinkCollectionNodes;

#ifndef _MSC_VER
	// MINGW fails with: Flexible array member in union not supported
	// Solution: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
	union {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
		hid_pp_cap caps[0];
		hid_pp_link_collection_node LinkCollectionArray[0];
#pragma GCC diagnostic pop
	};
#else
	union {
		hid_pp_cap caps[];
		hid_pp_link_collection_node LinkCollectionArray[];
	};
#endif

} hidp_preparsed_data;

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#endif
