#pragma once
/* orin_serial.h — Orin↔ESP32-S3 BALANCE USB/UART serial protocol (bd-66hx)
 *
 * Frame layout (matches bd-wim1 esp32_balance_protocol.py exactly):
 *   [0xAA][LEN][TYPE][PAYLOAD × LEN bytes][CRC8-SMBUS]
 *   CRC covers LEN + TYPE + PAYLOAD bytes.
 *   All multi-byte payload fields are big-endian.
 *
 * Physical: UART0 → CH343 USB-serial → Orin /dev/esp32-balance  @ 460800 baud
 */

#include <stdint.h>
#include <stdbool.h>
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"

/* ── Frame constants ── */
#define ORIN_SYNC           0xAAu
#define ORIN_MAX_PAYLOAD    62u

/* ── Command types: Orin → ESP32 ── */
#define CMD_HEARTBEAT       0x01u
#define CMD_DRIVE           0x02u   /* int16 speed + int16 steer, BE */
#define CMD_ESTOP           0x03u   /* uint8: 1=assert, 0=clear */
#define CMD_ARM             0x04u   /* uint8: 1=arm, 0=disarm */

/* ── Telemetry types: ESP32 → Orin ── */
#define TELEM_STATUS        0x80u   /* status @ 10 Hz */
#define TELEM_VESC_LEFT     0x81u   /* VESC ID 61 (front) telemetry @ 10 Hz */
#define TELEM_VESC_RIGHT    0x82u   /* VESC ID 79 (rear)  telemetry @ 10 Hz */
#define TELEM_OTA_STATUS    0x83u   /* OTA state + progress (bd-1s1s) */
#define TELEM_VERSION_INFO  0x84u   /* firmware version report (bd-1s1s) */
#define RESP_ACK            0xA0u
#define RESP_NACK           0xA1u

/* ── OTA commands (Orin → ESP32, bd-1s1s) ── */
#define CMD_OTA_CHECK       0x10u   /* no payload: trigger Gitea version check */
#define CMD_OTA_UPDATE      0x11u   /* uint8 target: 0=balance, 1=io, 2=both */

/* ── OTA target constants ── */
#define OTA_TARGET_BALANCE  0x00u
#define OTA_TARGET_IO       0x01u
#define OTA_TARGET_BOTH     0x02u

/* ── NACK error codes ── */
#define ERR_BAD_CRC         0x01u
#define ERR_BAD_LEN         0x02u
#define ERR_ESTOP_ACTIVE    0x03u
#define ERR_DISARMED        0x04u
#define ERR_OTA_BUSY        0x05u
#define ERR_OTA_NO_UPDATE   0x06u

/* ── Drive state (mirrored from TELEM_STATUS.balance_state) ── */
typedef enum {
    BAL_DISARMED = 0,
    BAL_ARMED    = 1,
    BAL_ESTOP    = 3,
} bal_state_t;

/* ── Shared state written by RX task, consumed by main/vesc tasks ── */
typedef struct {
    volatile int16_t  speed;        /* -1000..+1000 */
    volatile int16_t  steer;        /* -1000..+1000 */
    volatile uint32_t updated_ms;   /* esp_timer tick at last CMD_DRIVE */
} orin_drive_t;

typedef struct {
    volatile bool     armed;
    volatile bool     estop;
    volatile uint32_t hb_last_ms;   /* esp_timer tick at last CMD_HEARTBEAT/CMD_DRIVE */
} orin_control_t;

/* ── TX frame queue item ── */
typedef struct {
    uint8_t type;
    uint8_t len;
    uint8_t payload[ORIN_MAX_PAYLOAD];
} orin_tx_frame_t;

/* ── Globals (defined in orin_serial.c, extern here) ── */
extern orin_drive_t   g_orin_drive;
extern orin_control_t g_orin_ctrl;

/* ── API ── */
void orin_serial_init(void);

/* Tasks — pass tx_queue as arg to both */
void orin_serial_rx_task(void *arg);   /* arg = QueueHandle_t tx_queue */
void orin_serial_tx_task(void *arg);   /* arg = QueueHandle_t tx_queue */

/* Enqueue outbound frames */
void orin_send_status(QueueHandle_t q,
                      int16_t pitch_x10, int16_t motor_cmd,
                      uint16_t vbat_mv, bal_state_t state, uint8_t flags);
void orin_send_vesc(QueueHandle_t q, uint8_t telem_type,
                    int32_t erpm, uint16_t voltage_mv,
                    int16_t current_ma, uint16_t temp_c_x10);
void orin_send_ack(QueueHandle_t q, uint8_t cmd_type);
void orin_send_nack(QueueHandle_t q, uint8_t cmd_type, uint8_t err);

/* OTA telemetry helpers (bd-1s1s) */
void orin_send_ota_status(QueueHandle_t q, uint8_t target,
                           uint8_t state, uint8_t progress, uint8_t err);
void orin_send_version_info(QueueHandle_t q, uint8_t target,
                             const char *current, const char *available);
