/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * KISS (Keep It Simple Stupid) protocol encoder/decoder.
 *
 * Frame format:
 *   FEND | CMD | <data with escaping> | FEND
 *
 * Escape sequences (inside frame data only):
 *   0xC0  →  0xDB 0xDC  (FESC TFEND)
 *   0xDB  →  0xDB 0xDD  (FESC TFESC)
 */
#ifndef KISS_H
#define KISS_H

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

/* Special bytes */
#define KISS_FEND   0xC0u   /* Frame delimiter */
#define KISS_FESC   0xDBu   /* Escape byte */
#define KISS_TFEND  0xDCu   /* Escaped FEND */
#define KISS_TFESC  0xDDu   /* Escaped FESC */

/* Command codes (upper nibble = port 0) */
#define KISS_CMD_DATA        0x00u
#define KISS_CMD_TXDELAY     0x01u
#define KISS_CMD_PERSISTENCE 0x02u
#define KISS_CMD_SLOTTIME    0x03u
#define KISS_CMD_TXTAIL      0x04u
#define KISS_CMD_FULLDUPLEX  0x05u
#define KISS_CMD_SETHARDWARE 0x06u
#define KISS_CMD_RETURN      0xFFu

/*
 * SetHardware (0x06) sub-commands — device-specific radio configuration.
 *
 * Frame payload format:  [sub-cmd 1 byte] [params...]
 *
 * SET commands change a parameter and apply it immediately.
 * GET commands cause the device to respond with a SetHardware frame
 * containing the same sub-command byte followed by the current value(s).
 *
 * Frequency:  uint32_t big-endian, Hz   (e.g. 0x36892580 = 915000000)
 * BW:         uint16_t big-endian, kHz  (125, 250, or 500)
 * SF:         uint8_t 7..12
 * CR:         uint8_t coding-rate denominator 5..8  (meaning 4/5 .. 4/8)
 * TX power:   int8_t  dBm
 */
#define KISS_HW_SET_FREQ    0x01u  /* payload: 4 bytes freq_hz (BE uint32) */
#define KISS_HW_GET_FREQ    0x02u  /* no params; response: sub-cmd + 4 bytes */
#define KISS_HW_SET_SF      0x03u  /* payload: 1 byte sf (7-12) */
#define KISS_HW_SET_BW      0x04u  /* payload: 2 bytes bw_khz (BE uint16: 125/250/500) */
#define KISS_HW_SET_CR      0x05u  /* payload: 1 byte cr denom (5-8) */
#define KISS_HW_SET_TXPWR   0x06u  /* payload: 1 byte tx_power_dbm (int8) */
#define KISS_HW_GET_PARAMS  0x07u  /* no params; response: sub-cmd + all params (10 bytes) */
/*
 * GET_PARAMS response payload (10 bytes after sub-cmd byte):
 *   [0..3] freq_hz  BE uint32
 *   [4]    sf       uint8
 *   [5..6] bw_khz   BE uint16
 *   [7]    cr       uint8  (denominator)
 *   [8]    tx_power int8   (dBm)
 *   [9]    preamble uint8
 */

/**
 * @brief Encode data into a KISS frame with an explicit command byte.
 *
 * Writes FEND + cmd + escaped data + FEND into @p out_buf.
 *
 * @param cmd       KISS command byte (e.g. KISS_CMD_DATA, KISS_CMD_SETHARDWARE).
 * @param data      Payload bytes (may be NULL when data_len == 0).
 * @param data_len  Length of payload.
 * @param out_buf   Output buffer for the encoded frame.
 * @param out_size  Size of the output buffer.
 * @return          Number of bytes written, or -ENOMEM if @p out_buf is too small.
 */
int kiss_encode_cmd(uint8_t cmd, const uint8_t *data, size_t data_len,
		    uint8_t *out_buf, size_t out_size);

/**
 * @brief Encode raw data into a KISS data frame (CMD = 0x00).
 *
 * Convenience wrapper around kiss_encode_cmd() using KISS_CMD_DATA.
 */
int kiss_encode(const uint8_t *data, size_t data_len,
		uint8_t *out_buf, size_t out_size);

/**
 * @brief KISS decoder context.  Maintains state between calls to kiss_decoder_feed().
 */
struct kiss_decoder_ctx {
	uint8_t *frame_buf;      /**< Caller-supplied buffer for decoded payload. */
	size_t   frame_buf_size; /**< Size of frame_buf. */
	size_t   frame_len;      /**< Number of payload bytes decoded so far. */
	bool     in_frame;       /**< True after the opening FEND has been seen. */
	bool     in_escape;      /**< True after FESC has been seen. */
	bool     cmd_received;   /**< True once the CMD byte has been consumed. */
	uint8_t  cmd;            /**< KISS command byte of the current frame. */
};

/**
 * @brief Initialize a KISS decoder context.
 *
 * @param ctx   Context to initialize.
 * @param buf   Buffer for decoded frame payload.
 * @param size  Size of @p buf.
 */
void kiss_decoder_init(struct kiss_decoder_ctx *ctx,
		       uint8_t *buf, size_t size);

/**
 * @brief Feed one byte into the KISS decoder state machine.
 *
 * @param ctx   Decoder context.
 * @param byte  Next byte from the serial stream.
 * @return      true when a complete frame has been received.
 *              ctx->frame_buf[0..ctx->frame_len-1] holds the payload.
 *              ctx->cmd holds the KISS command byte.
 */
bool kiss_decoder_feed(struct kiss_decoder_ctx *ctx, uint8_t byte);

#endif /* KISS_H */
