#ifndef USBDATAHANDLER_H
#define USBDATAHANDLER_H

#include "stdbool.h"
#include "stdint.h"
#include "pb_common.h"
#ifdef __cplusplus
extern "C" {
#endif


#define ASSERT_SIZE(x) \
  { \
    if (!(x)) \
      Error_Handler(); \
  }


#define DATA_CLBK_SETUP(name) \
  { \
    ASSERT_SIZE(length == sizeof(name)); \
    memcpy(&msg_##name, msg, sizeof(name)); \
  }




/**
 * @struct UsbDataPacketHead
 * @brief This structure represents the header of a USB data packet.
 *
 * The header includes the length and type of the packet, as well as a check byte.
 * The structure is packed to ensure that the memory layout is exactly as defined,
 * without any padding bytes.
 */
typedef struct {
  uint16_t length; // Length of the USB data packet
  uint16_t type;   // Type of the USB data packet(used to parse the message content).
  uint8_t check;   // Check byte of the USB data packet.
} __attribute__((packed)) UsbDataPacketHead;

#define MAX_PACKET_SIZE 512 - sizeof(UsbDataPacketHead) // Define your maximum packet size


/**
 * @struct UsbDataPacket
 * @brief This structure represents a USB data packet.
 *
 * The packet includes a header and a data array. The structure is packed to ensure
 * that the memory layout is exactly as defined, without any padding bytes.
 *
 * @var UsbDataPacket::head
 *
 *
 * @var UsbDataPacket::data
 *
 */
typedef struct {
  UsbDataPacketHead head;        // Header of the USB data packet.
  uint8_t data[MAX_PACKET_SIZE]; // Data of the USB data packet.
} __attribute__((packed)) UsbDataPacket;

/**
 * @brief Calculate the chekcsum of the header of a USB data packet.
 *
 * @param p Pointer to the USB data packet.
 * @return Sum of the header of the USB data packet.
 */
uint8_t UsbDataPacket_head_sum(const UsbDataPacket* p);

/**
 * @brief Check the header of a USB data packet.
 *
 * @param p Pointer to the USB data packet.
 * @return True if the header is valid, false otherwise.
 */
bool UsbDataPacket_head_check(const UsbDataPacket* p);


/**
 * @brief Sends a message over USB using a data packet
 * 
 * @param type The type of the message to be sent
 * @param buffer Pointer to the USB data packet
 * @param fields Pointer to the protocol buffer message descriptor
 * @param msg Pointer to the message to be sent
 * 
 * @return True if the message was successfully sent, false otherwise
 */
bool UsbDataPacketSendMessage(uint32_t type , UsbDataPacket * buffer ,const pb_msgdesc_t * fields ,const void* msg);

/**
 * @brief This function starts the USB data handler.
 *
 * It initializes any necessary hardware or software resources
 * and prepares the data handler to process USB data.
 *
 * @return void
 */
void UsbDataHandler_Start();

/**
 * @brief This function runs the USB data handler.
 *
 * It processes any pending USB data and performs any necessary
 * tasks related to USB data handling. It should be called
 * periodically in the main loop of your program.
 *
 * @return void
 */
void UsbDataHandler_Runner();

/**
 * @brief This function is a callback that is invoked when USB data is received.
 *
 * It processes the received data and performs any necessary tasks.
 *
 * @param Buf A pointer to the buffer that contains the received data.
 * @param Len The length of the received data, in bytes.
 *
 * @return int Returns 0 on success, or a non-zero error code on failure.
 */
int UsbDataHandler_RxCallback(uint8_t* Buf, uint32_t Len);


void DataClbk_cls_firmware_Start(void* msg, uint32_t length);
void DataClbk_cls_firmware_Package(void* msg, uint32_t length);
void DataClbk_cls_firmware_PackageAck(void* msg, uint32_t length);
void DataClbk_cls_firmware_Done(void* msg, uint32_t length);
void DataClbk_cls_device_ResponseList(void* msg, uint32_t length);
void DataClbk_cls_device_RequestList(void* msg, uint32_t length);
void DataClbk_cls_light_GlobalBrightness(void* msg, uint32_t length);
void DataClbk_cls_light_GlobalTheme(void* msg, uint32_t length);
void DataClbk_cls_light_ThemeSettings(void* msg, uint32_t length);
void DataClbk_cls_light_SaveThemeSettings(void* msg, uint32_t length);

#include "usb.pb.h"
void USBDataResonse(void * msg, const pb_msgdesc_t *fields, cls_usb_PackageType typeid);

#ifdef __cplusplus
}
#endif

#endif /* USBDATAHANDLER_H */