/**
 * DreamStack ESP-NOW Transport — Ultra-Low Latency Binary Protocol
 *
 * Sub-1ms signal delivery over ESP-NOW (WiFi direct, no router).
 * Binary packed frames instead of JSON for minimal overhead.
 *
 * Transport strategy:
 *   ESP-NOW: real-time signals + events (<1ms, ≤250 bytes)
 *   UDP:     initial IR push + large payloads (~2ms, ≤1472 bytes)
 */

#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "esp_now.h"

// ─── ESP-NOW Frame Types ───
#define DS_NOW_SIG           0x20  // Single signal update (hub → panel)
#define DS_NOW_SIG_BATCH     0x21  // Batch signal update (hub → panel)
#define DS_NOW_TOUCH         0x30  // Touch event (panel → hub)
#define DS_NOW_ACTION        0x31  // Button/widget action (panel → hub)
#define DS_NOW_PING          0xFE  // Heartbeat (bidirectional)
#define DS_NOW_PONG          0xFD  // Heartbeat response

// ─── UDP Frame Types ───
#define DS_UDP_IR_PUSH       0x40  // Full IR JSON push (hub → panel)
#define DS_UDP_IR_FRAG       0x41  // IR fragment for payloads > MTU
#define DS_UDP_DISCOVER      0x42  // Panel discovery broadcast

// ─── UDP Port ───
#define DS_UDP_PORT          9200

// ─── ESP-NOW Channel ───
#define DS_ESPNOW_CHANNEL   1

// ─── Max ESP-NOW payload ───
#define DS_ESPNOW_MAX_DATA  250

// ─── Signal Update Frame (7 bytes) ───
// Hub → Panel: update a single signal value
typedef struct __attribute__((packed)) {
    uint8_t  type;        // DS_NOW_SIG
    uint16_t signal_id;   // which signal (0-65535)
    int32_t  value;       // new value
} ds_sig_frame_t;

// ─── Signal Batch Frame (3 + 6*N bytes) ───
// Hub → Panel: update multiple signals at once
typedef struct __attribute__((packed)) {
    uint16_t id;
    int32_t  val;
} ds_sig_entry_t;

typedef struct __attribute__((packed)) {
    uint8_t  type;        // DS_NOW_SIG_BATCH
    uint8_t  count;       // number of signals (max ~40 in 250B)
    uint8_t  seq;         // sequence number (wrapping u8)
    // followed by `count` ds_sig_entry_t entries
} ds_sig_batch_t;

// ─── Touch Event Frame (8 bytes) ───
// Panel → Hub: touch on the display
typedef struct __attribute__((packed)) {
    uint8_t  type;        // DS_NOW_TOUCH
    uint8_t  node_id;     // which UI node (from IR)
    uint8_t  event;       // 0=click, 1=long_press, 2=release, 3=drag
    uint8_t  seq;         // sequence number
    uint16_t x;           // touch X coordinate
    uint16_t y;           // touch Y coordinate
} ds_touch_now_t;

// ─── Action Event Frame (4 bytes) ───
// Panel → Hub: widget action (button click, toggle, etc.)
typedef struct __attribute__((packed)) {
    uint8_t  type;        // DS_NOW_ACTION
    uint8_t  node_id;     // which widget
    uint8_t  action;      // 0=click, 1=toggle, 2=slide_change
    uint8_t  seq;         // sequence number
} ds_action_frame_t;

// ─── Heartbeat Frame (2 bytes) ───
typedef struct __attribute__((packed)) {
    uint8_t  type;        // DS_NOW_PING or DS_NOW_PONG
    uint8_t  seq;         // echo back on pong
} ds_heartbeat_t;

// ─── UDP IR Push Header (4 bytes + payload) ───
typedef struct __attribute__((packed)) {
    uint8_t  magic[2];    // 0xD5, 0x7A
    uint16_t length;      // JSON payload length
    // followed by `length` bytes of IR JSON
} ds_ir_push_t;

// ─── UDP IR Fragment Header (6 bytes + payload) ───
typedef struct __attribute__((packed)) {
    uint8_t  magic[2];    // 0xD5, 0x7A
    uint8_t  type;        // DS_UDP_IR_FRAG
    uint8_t  frag_id;     // fragment index (0-based)
    uint8_t  frag_total;  // total fragments
    uint8_t  seq;         // group sequence
    // followed by fragment data (up to 1466 bytes)
} ds_ir_frag_t;

// ─── Callbacks ───
typedef void (*ds_signal_cb_t)(uint16_t signal_id, int32_t value);
typedef void (*ds_ir_cb_t)(const char *ir_json, size_t length);

// ─── Configuration ───
typedef struct {
    uint8_t         hub_mac[6];     // Hub MAC address (set to FF:FF:FF:FF:FF:FF for broadcast)
    uint8_t         channel;        // WiFi channel (default: DS_ESPNOW_CHANNEL)
    ds_signal_cb_t  on_signal;      // Called when a signal update arrives
    ds_ir_cb_t      on_ir_push;     // Called when a full IR JSON arrives
} ds_espnow_config_t;

/**
 * Initialize ESP-NOW transport.
 * Sets up ESP-NOW, registers peer, starts UDP listener.
 * WiFi must be initialized first (STA or AP mode, no connection needed).
 */
esp_err_t ds_espnow_init(const ds_espnow_config_t *config);

/**
 * Send an action event to the hub (panel → hub).
 * Encodes as ds_action_frame_t and sends via ESP-NOW.
 */
esp_err_t ds_espnow_send_action(uint8_t node_id, uint8_t action);

/**
 * Send a touch event to the hub (panel → hub).
 */
esp_err_t ds_espnow_send_touch(uint8_t node_id, uint8_t event,
                                uint16_t x, uint16_t y);

/**
 * Send a heartbeat ping.
 */
esp_err_t ds_espnow_send_ping(void);

/**
 * Deinitialize ESP-NOW transport.
 */
void ds_espnow_deinit(void);
