// The MIT License (MIT)
//
// Copyright (C) 2026 Scott Duensing
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.

// Packetized serial transport with HDLC-style framing and sliding window
// Provides reliable, ordered delivery over an unreliable serial link.
//
// Why HDLC framing:
//   HDLC's flag-byte + byte-stuffing scheme is the simplest way to delimit
//   variable-length frames on a raw byte stream. The 0x7E flag byte marks
//   frame boundaries; byte-stuffing (escaping 0x7E and 0x7D within data)
//   ensures the flag is unambiguous. This is proven, lightweight, and
//   requires zero buffering at the framing layer. Alternative approaches
//   like length-prefixed framing are fragile on noisy links because a
//   corrupted length field desynchronizes the receiver permanently.
//
// Why Go-Back-N ARQ:
//   Go-Back-N is simpler than Selective Repeat (no out-of-order reassembly
//   buffer needed on the receiver) and works well for the low bandwidth-delay
//   product of a serial link. On a 115200 bps local serial connection, the
//   round-trip time is negligible, so the window rarely fills. GBN's
//   retransmit-all-from-NAK behavior wastes bandwidth on lossy links, but
//   serial links are nearly lossless  -- the CRC check is primarily a safety
//   net for electrical noise, not a routine error recovery mechanism.
//
// CRC-16-CCITT:
//   Standard polynomial used in HDLC/X.25. Table-driven for speed
//   (256-entry lookup table trades 512 bytes of ROM for ~10x faster CRC
//   than the bit-by-bit method, important on a 486).

#ifndef PACKET_H
#define PACKET_H

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

// Maximum payload per packet (excluding header/CRC). 255 bytes fits in a
// single LEN byte and keeps total frame size manageable for serial buffers.
#define PKT_MAX_PAYLOAD         255

// Default sliding window size (1-8). 4 allows up to 4 unacknowledged
// frames in flight, providing good throughput without excessive buffering.
#define PKT_DEFAULT_WINDOW      4

// Maximum window size. Limited to 8 because sequence numbers are 8-bit
// and we need seq space > 2*window to avoid ambiguity (255 > 2*8).
#define PKT_MAX_WINDOW          8

// Error codes
#define PKT_SUCCESS             0
#define PKT_ERR_INVALID_PARAM  -6
#define PKT_ERR_TX_FULL        -7
#define PKT_ERR_DISCONNECTED   -9

// Callback for received packets
// ctx:  user context pointer
// data: payload (valid only during callback)
// len:  payload length
typedef void (*PktRecvCallbackT)(void *ctx, const uint8_t *data, int len);

// Connection handle (opaque)
typedef struct PktConnS PktConnT;


// Open a packetized connection over a COM port.
// com:        RS232 COM port index (RS232_COM1..RS232_COM4), must already be open
// windowSize: sliding window size (1..PKT_MAX_WINDOW), 0 for default
// callback:   called when a complete, verified packet is received
// callbackCtx: user pointer passed to callback
// Returns connection handle, or 0 on failure.
PktConnT *pktOpen(int com, int windowSize, PktRecvCallbackT callback, void *callbackCtx);

// Close a packetized connection. Does not close the underlying COM port.
void pktClose(PktConnT *conn);

// Send a packet. Returns PKT_SUCCESS or error.
// Blocks if the transmit window is full and block is true.
// Returns PKT_ERR_TX_FULL if window is full and block is false.
int pktSend(PktConnT *conn, const uint8_t *data, int len, bool block);

// Poll for incoming data, process received frames, handle retransmits.
// Must be called frequently (e.g. in your main loop).
// Returns the number of valid data packets delivered to the callback.
int pktPoll(PktConnT *conn);

// Reset the connection state (sequence numbers, buffers).
// Sends a RST frame to the remote side.
int pktReset(PktConnT *conn);

// Check if there is room in the transmit window.
bool pktCanSend(PktConnT *conn);

// Get number of unacknowledged packets in the transmit window.
int pktGetPending(PktConnT *conn);

#endif
