#ifndef UART_PROTOCOL_H
#define UART_PROTOCOL_H

/*
 * uart_protocol.h — UART command protocol for Jetson-STM32 communication (Issue #629)
 *
 * Frame format:
 *   [STX][LEN][CMD][PAYLOAD...][CRC8][ETX]
 *    0x02  1B   1B   0-12 B     1B   0x03
 *
 * CRC8-SMBUS: poly=0x07, init=0x00, computed over CMD+PAYLOAD bytes.
 *
 * Physical layer: UART5 (PC12=TX / PD2=RX), GPIO_AF8_UART5, 115200 baud, no hw flow.
 * NOTE: Spec requested USART1 @ 115200, but USART1 is occupied by JLink @ 921600.
 *       Implemented on UART5 instead; Jetson must connect to PC12/PD2.
 *
 * DMA: DMA1_Stream0_Channel4, circular 256-byte ring buffer.
 * Heartbeat: if no frame received in UART_PROT_HB_TIMEOUT_MS (500 ms), Jetson is
 *            considered lost; caller must handle estop if needed.
 */

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

/* ── Frame delimiters ─────────────────────────────────────────────────────── */
#define UPROT_STX  0x02u
#define UPROT_ETX  0x03u

/* ── Command IDs (host → STM32) ───────────────────────────────────────────── */
#define UCMD_SET_VELOCITY  0x01u  /* payload: int16 left_rpm, int16 right_rpm (4 B) */
#define UCMD_GET_STATUS    0x02u  /* payload: none */
#define UCMD_SET_PID       0x03u  /* payload: float kp, float ki, float kd    (12 B) */
#define UCMD_ESTOP         0x04u  /* payload: none */
#define UCMD_CLEAR_ESTOP   0x05u  /* payload: none */

/* ── Response IDs (STM32 → host) ──────────────────────────────────────────── */
#define URESP_ACK    0x80u  /* payload: 1 B — echoed CMD */
#define URESP_NACK   0x81u  /* payload: 2 B — CMD, error_code */
#define URESP_STATUS 0x82u  /* payload: sizeof(uart_prot_status_t) = 8 B */

/* ── NACK error codes ─────────────────────────────────────────────────────── */
#define UERR_BAD_CRC    0x01u
#define UERR_BAD_LEN    0x02u
#define UERR_BAD_ETX    0x03u
#define UERR_ESTOP      0x04u  /* command rejected — estop active */
#define UERR_DISARMED   0x05u  /* velocity rejected — not armed */

/* ── STATUS payload (URESP_STATUS, 8 bytes packed) ───────────────────────── */
typedef struct __attribute__((packed)) {
    int16_t  pitch_x10;      /* pitch angle ×10 deg (balance controller) */
    int16_t  motor_cmd;      /* ESC motor command -1000..+1000 */
    uint16_t vbat_mv;        /* battery voltage in mV */
    uint8_t  balance_state;  /* BalanceState enum (0=DISARMED, 1=ARMED, …) */
    uint8_t  estop_active;   /* non-zero if remote estop is latched */
} uart_prot_status_t;

/* ── Shared state (read by main.c) ────────────────────────────────────────── */
typedef struct {
    volatile uint8_t  vel_updated;      /* 1 when SET_VELOCITY received */
    volatile int16_t  left_rpm;
    volatile int16_t  right_rpm;

    volatile uint8_t  pid_updated;      /* 1 when SET_PID received */
    volatile float    pid_kp;
    volatile float    pid_ki;
    volatile float    pid_kd;

    volatile uint8_t  estop_req;        /* 1 on UCMD_ESTOP */
    volatile uint8_t  estop_clear_req;  /* 1 on UCMD_CLEAR_ESTOP */

    volatile uint32_t last_rx_ms;       /* HAL_GetTick() of last valid frame */
} UartProtState;

extern UartProtState uart_prot_state;

/* ── API ───────────────────────────────────────────────────────────────────── */

/**
 * uart_protocol_init() — configure UART5 + DMA, start circular receive.
 * Must be called once during system init, before main loop.
 */
void uart_protocol_init(void);

/**
 * uart_protocol_process() — drain DMA ring buffer, parse frames, dispatch commands.
 * Call once per main loop iteration (every ~1 ms).
 */
void uart_protocol_process(void);

/**
 * uart_protocol_send_status() — build and TX a URESP_STATUS frame.
 * @param s  Pointer to status payload to send.
 */
void uart_protocol_send_status(const uart_prot_status_t *s);

#endif /* UART_PROTOCOL_H */
