#ifndef AI_WIRE_H
#define AI_WIRE_H

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define AIW_MAX_CHANNELS 122u
#define AIW_HEADER_BYTES 8u
#define AIW_TICKET_PAYLOAD_BYTES 26u
#define AIW_RESULT_FIXED_PAYLOAD_BYTES 35u
#define AIW_CRC_BYTES 2u
#define AIW_MAX_PACKET_BYTES (AIW_HEADER_BYTES + AIW_RESULT_FIXED_PAYLOAD_BYTES + AIW_MAX_CHANNELS + AIW_CRC_BYTES)
#define AIW_RESULT_FLAG_HAND_PRESENT 0x01u
#define AIW_RESULT_ALLOWED_FLAGS AIW_RESULT_FLAG_HAND_PRESENT
#define AIW_UI_COMMAND_MAX_BUTTON 8u
#define AIW_UI_ACK_FLAG_RUNNING 0x01u
#define AIW_UI_ACK_FLAG_DUPLICATE 0x02u
#define AIW_UI_ACK_ALLOWED_FLAGS (AIW_UI_ACK_FLAG_RUNNING | AIW_UI_ACK_FLAG_DUPLICATE)

typedef enum {
    AIW_UI_ACK_ACCEPTED = 0,
    AIW_UI_ACK_NOT_READY = 1,
    AIW_UI_ACK_INVALID_COMMAND = 2,
    AIW_UI_ACK_BAD_SESSION = 3,
    AIW_UI_ACK_STALE_SEQUENCE = 4,
    AIW_UI_ACK_SEQUENCE_COLLISION = 5,
    AIW_UI_ACK_SESSION_OPENED = 6,
    AIW_UI_ACK_SESSION_REJECTED = 7
} aiw_ui_ack_status;

typedef struct {
    uint64_t session_id;
    uint32_t ticket_id;
    uint32_t calibration_id;
    uint16_t channels;
    uint64_t min_source_frame_seq;
} aiw_ticket;

typedef struct {
    uint64_t session_id;
    uint32_t ticket_id;
    uint32_t calibration_id;
    uint16_t channels;
    uint64_t capture_baseline_seq;
    uint64_t source_frame_seq;
    uint8_t status_flags;
    uint8_t pwm[AIW_MAX_CHANNELS];
} aiw_result;

typedef struct {
    uint64_t session_id;
} aiw_ui_session_open;

typedef struct {
    uint64_t session_id;
    uint32_t sequence;
    uint8_t button;
} aiw_ui_command;

typedef struct {
    uint64_t session_id;
    uint32_t sequence;
    aiw_ui_ack_status status;
    uint8_t selected_mode;
    uint8_t flags;
    uint32_t remaining_ms;
} aiw_ui_ack;

typedef enum {
    AIW_MESSAGE_RESULT,
    AIW_MESSAGE_UI_SESSION_OPEN,
    AIW_MESSAGE_UI_COMMAND,
    AIW_MESSAGE_UI_ACK
} aiw_message_type;

typedef struct {
    aiw_message_type type;
    union {
        aiw_result result;
        aiw_ui_session_open ui_session_open;
        aiw_ui_command ui_command;
        aiw_ui_ack ui_ack;
    } value;
} aiw_message;

/* Version-2 AI_REQUEST and AI_RESULT wire formats: NLAI magic, version/type, payload length,
   LE session/ticket/calibration/channel/frame fields, PWM bytes, CRC-16/CCITT-FALSE.
   AI_RESULT inserts status_flags after source_frame_seq. Hand-absent results must carry
   an all-zero PWM vector. This serializer does not establish UART timing or prove a new capture. */
bool aiw_encode_ticket(const aiw_ticket *ticket, uint8_t *out, size_t capacity,
                       size_t *written);
bool aiw_decode_ticket(const uint8_t *packet, size_t length, aiw_ticket *out);
bool aiw_encode_result(const aiw_result *result, uint8_t *out, size_t capacity,
                       size_t *written);
bool aiw_decode_result(const uint8_t *packet, size_t length, aiw_result *out);
bool aiw_encode_ui_session_open(const aiw_ui_session_open *open, uint8_t *out,
                                size_t capacity, size_t *written);
bool aiw_decode_ui_session_open(const uint8_t *packet, size_t length,
                                aiw_ui_session_open *out);
bool aiw_encode_ui_command(const aiw_ui_command *command, uint8_t *out,
                           size_t capacity, size_t *written);
bool aiw_decode_ui_command(const uint8_t *packet, size_t length,
                           aiw_ui_command *out);
bool aiw_encode_ui_ack(const aiw_ui_ack *ack, uint8_t *out, size_t capacity,
                       size_t *written);
bool aiw_decode_ui_ack(const uint8_t *packet, size_t length, aiw_ui_ack *out);
bool aiw_decode_message(const uint8_t *packet, size_t length, aiw_message *out);
uint16_t aiw_crc16_ccitt_false(const uint8_t *data, size_t length);

#endif
