// python-esp-bridge — frame codec, FreeRTOS task plumbing, dispatch.
//
// Task model (core placement: see the task-layout note in config.h):
//   tx_task   — CORE_RADIO: sole link writer; drains one outbound frame queue
//               PER LINK with non-blocking writes, so a stalled link (BLE
//               congestion, full serial ring) never delays the other link.
//   rx_task   — CORE_APP: decodes frames; runs fast handlers (SYS/GPIO/analog/
//               PWM/I2C/SPI/UART/NVS/MCPWM and the IRQ-masking 1-Wire) inline;
//               pumps GPIO edge + UART RX events.
//   slow_task — CORE_RADIO: owns Wi-Fi/NET/ESP-NOW/BLE + FS/OTA/cam state;
//               executes their (possibly blocking) handlers from a request
//               queue and polls sockets/scan results.
// proto_reply*/proto_send_event are therefore safe from ANY task or callback.
#pragma once
#include <Arduino.h>
#include "config.h"

void proto_init();    // create queues (before any proto_* call)
void proto_start();   // spawn the tx/rx/slow tasks (end of setup())

// Route IDF log output (Wi-Fi/BT stacks) away from UART0 — which IS the
// protocol link — into SYS_LOG events. Install first thing in setup().
void proto_log_hook_install();

// All functions below are thread-safe: they enqueue frames for tx_task to write.
// They may be called from any task or interrupt callback.
void proto_reply(uint8_t seq, uint16_t cmd, const uint8_t* data, uint16_t len);
void proto_reply_ok(uint8_t seq, uint16_t cmd);
void proto_reply_err(uint8_t seq, uint16_t cmd, uint8_t status);
void proto_send_event(uint16_t cmd, const uint8_t* data, uint16_t len);
void proto_log(uint8_t level, const char* msg);
void proto_log_heap(const char* stage);  // sends a SYS_LOG event: "<stage>, N B free heap"

// Argument-length guard for command handlers: reply ST_BAD_ARGS and return from
// the handler when the payload is shorter than n bytes. Requires seq, cmd and len
// in scope (the standard handler signature). Expands inline — no runtime cost.
#define NEED(n) do { if (len < (n)) { proto_reply_err(seq, cmd, ST_BAD_ARGS); return; } } while (0)

// Block until every queued frame is on the wire (baud switch / reset).
void proto_tx_flush();

uint32_t proto_dropped_events();

uint16_t crc16_ccitt(const uint8_t* data, uint16_t len);

// Big-endian read/write helpers used throughout the protocol layer.
static inline uint16_t read_be16(const uint8_t* p) { return ((uint16_t)p[0] << 8) | p[1]; }
static inline uint32_t read_be32(const uint8_t* p) {
  return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | p[3];
}
static inline void write_be16(uint8_t* p, uint16_t v) { p[0] = v >> 8; p[1] = v; }
static inline void write_be32(uint8_t* p, uint32_t v) { p[0] = v >> 24; p[1] = v >> 16; p[2] = v >> 8; p[3] = v; }
