/**
 * @file UDPSProtocol.h
 * @brief Shared UDPS binary streaming protocol definitions.
 *
 * This header is free of MARTe2-specific types (other than CompilerTypes.h)
 * so it can be included by both UDPStreamer (DataSource) and DebugService
 * (Interface), as well as any future component that needs to produce or
 * consume UDPS packets.
 *
 * Protocol overview
 * -----------------
 * Every packet starts with a 17-byte packed header (UDPSPacketHeader).
 * Large payloads are split into multiple fragments sharing the same counter.
 *
 * Packet types:
 *   DATA       (0) - signal sample values, Server → Client
 *   CONFIG     (1) - signal metadata, Server → Client (sent when set changes)
 *   ACK        (2) - counter acknowledgment, Client → Server (optional)
 *   CONNECT    (3) - session initiation, Client → Server
 *   DISCONNECT (4) - session teardown, Client → Server or Server → Client
 *
 * CONFIG payload:
 *   [uint32 numSigs]
 *   numSigs × UDPSSignalDescriptor (136 bytes each, packed)
 *   [uint8 publishMode]   (PublishModeStrict / Accumulate / Decimate)
 *
 * DATA payload (Strict / Decimate):
 *   [uint64 HRT timestamp]
 *   per-signal data in CONFIG order (quantised or raw, no padding)
 *
 * DATA payload (Accumulate):
 *   [uint64 HRT timestamp]
 *   [uint32 numSamples]
 *   for each signal: if scalar → numSamples elements; else → NumElements once
 */

#ifndef UDPS_PROTOCOL_H_
#define UDPS_PROTOCOL_H_

#include "CompilerTypes.h"

namespace MARTe {

/*---------------------------------------------------------------------------*/
/*                         Magic and packet types                            */
/*---------------------------------------------------------------------------*/

/** Magic number: ASCII 'UDPS' stored little-endian (0x53504455). */
static const uint32 UDPS_MAGIC = 0x53504455u;

/** Packet type constants — must match the Go-side PktXxx constants. */
static const uint8 UDPS_TYPE_DATA       = 0u; ///< Server → Client: signal data
static const uint8 UDPS_TYPE_CONFIG     = 1u; ///< Server → Client: signal config
static const uint8 UDPS_TYPE_ACK        = 2u; ///< Client → Server: ack counter
static const uint8 UDPS_TYPE_CONNECT    = 3u; ///< Client → Server: connect request
static const uint8 UDPS_TYPE_DISCONNECT = 4u; ///< Client → Server: disconnect

/*---------------------------------------------------------------------------*/
/*                        Wire packet header (17 bytes)                      */
/*---------------------------------------------------------------------------*/

/**
 * @brief 17-byte packed UDP packet header, little-endian.
 *
 * All fields use unsigned integer types; no padding.  Placed at offset 0
 * of every UDPS datagram.
 */
#pragma pack(push, 1)
struct UDPSPacketHeader {
    uint32 magic;          ///< Must equal UDPS_MAGIC
    uint8  type;           ///< One of UDPS_TYPE_*
    uint32 counter;        ///< Per-update sequence counter (same for all fragments)
    uint16 fragmentIdx;    ///< 0-based index of this fragment
    uint16 totalFragments; ///< Total fragments for this update (1 = no fragmentation)
    uint32 payloadBytes;   ///< Payload bytes immediately following this header
};
#pragma pack(pop)

static const uint32 UDPS_HEADER_SIZE = 17u; ///< sizeof(UDPSPacketHeader)

/*---------------------------------------------------------------------------*/
/*                       Signal type codes (CONFIG wire format)              */
/*---------------------------------------------------------------------------*/

/** Compact type codes stored in UDPSSignalDescriptor::typeCode. */
static const uint8 UDPS_TYPECODE_UINT8   = 0u;
static const uint8 UDPS_TYPECODE_INT8    = 1u;
static const uint8 UDPS_TYPECODE_UINT16  = 2u;
static const uint8 UDPS_TYPECODE_INT16   = 3u;
static const uint8 UDPS_TYPECODE_UINT32  = 4u;
static const uint8 UDPS_TYPECODE_INT32   = 5u;
static const uint8 UDPS_TYPECODE_UINT64  = 6u;
static const uint8 UDPS_TYPECODE_INT64   = 7u;
static const uint8 UDPS_TYPECODE_FLOAT32 = 8u;
static const uint8 UDPS_TYPECODE_FLOAT64 = 9u;
static const uint8 UDPS_TYPECODE_UNKNOWN = 255u;

/*---------------------------------------------------------------------------*/
/*                     Quantisation and time-mode codes                      */
/*---------------------------------------------------------------------------*/

/** Quantisation codes stored in UDPSSignalDescriptor::quantType. */
static const uint8 UDPS_QUANT_NONE   = 0u; ///< No quantisation; raw wire format
static const uint8 UDPS_QUANT_UINT8  = 1u; ///< Map to uint8  [0, 255]
static const uint8 UDPS_QUANT_INT8   = 2u; ///< Map to int8   [-127, 127]
static const uint8 UDPS_QUANT_UINT16 = 3u; ///< Map to uint16 [0, 65535]
static const uint8 UDPS_QUANT_INT16  = 4u; ///< Map to int16  [-32767, 32767]

/** Time-mode codes stored in UDPSSignalDescriptor::timeMode. */
static const uint8 UDPS_TIMEMODE_PACKET      = 0u; ///< Use packet-level HRT timestamp
static const uint8 UDPS_TIMEMODE_FULL_ARRAY  = 1u; ///< TimeSignal has NumElements timestamps
static const uint8 UDPS_TIMEMODE_FIRST_SAMPLE = 2u; ///< TimeSignal scalar = timestamp of element [0]
static const uint8 UDPS_TIMEMODE_LAST_SAMPLE  = 3u; ///< TimeSignal scalar = timestamp of element [N-1]

/** Sentinel value for UDPSSignalDescriptor::timeSignalIdx when no time signal. */
static const uint32 UDPS_NO_TIME_SIGNAL = 0xFFFFFFFFu;

/*---------------------------------------------------------------------------*/
/*                      Publish mode codes (CONFIG trailing byte)            */
/*---------------------------------------------------------------------------*/

static const uint8 UDPS_PUBLISH_STRICT     = 0u; ///< One packet per Synchronise() call
static const uint8 UDPS_PUBLISH_ACCUMULATE = 1u; ///< Variable batch; flush on size or time
static const uint8 UDPS_PUBLISH_DECIMATE   = 2u; ///< One packet per Ratio calls

/*---------------------------------------------------------------------------*/
/*                   CONFIG payload — per-signal descriptor                  */
/*---------------------------------------------------------------------------*/

/** Maximum length of signal name and unit strings in the CONFIG payload. */
static const uint32 UDPS_MAX_SIGNAL_NAME = 64u;
static const uint32 UDPS_MAX_UNIT_LEN    = 32u;

/** Byte size of one serialised UDPSSignalDescriptor on the wire. */
static const uint32 UDPS_SIGNAL_DESC_SIZE = 136u;

/**
 * @brief Wire-format per-signal descriptor embedded in the CONFIG payload.
 *
 * Contains only plain C types so it can be memcpy'd directly to/from
 * the network buffer (little-endian on all supported platforms).
 *
 * Size: 64 + 1 + 1 + 1 + 4 + 4 + 8 + 8 + 1 + 8 + 4 + 32 = 136 bytes.
 */
#pragma pack(push, 1)
struct UDPSSignalDescriptor {
    char8   name[UDPS_MAX_SIGNAL_NAME]; ///< Null-terminated signal name
    uint8   typeCode;                    ///< UDPS_TYPECODE_*
    uint8   quantType;                   ///< UDPS_QUANT_*
    uint8   numDimensions;               ///< 0=scalar, 1=array, 2=matrix
    uint32  numRows;                     ///< Number of rows (or elements for 1D)
    uint32  numCols;                     ///< Number of columns (1 for 1D/scalar)
    float64 rangeMin;                    ///< Physical range minimum (for quantisation)
    float64 rangeMax;                    ///< Physical range maximum (for quantisation)
    uint8   timeMode;                    ///< UDPS_TIMEMODE_*
    float64 samplingRate;                ///< Hz; used for FirstSample/LastSample modes
    uint32  timeSignalIdx;               ///< Index of time-ref signal; UDPS_NO_TIME_SIGNAL if none
    char8   unit[UDPS_MAX_UNIT_LEN];     ///< Null-terminated physical unit string
};
#pragma pack(pop)

/*---------------------------------------------------------------------------*/
/*                    TypeDescriptor → UDPS type-code mapping                */
/*---------------------------------------------------------------------------*/

/**
 * @brief Maps a MARTe2 TypeDescriptor to the compact UDPS_TYPECODE_* value.
 *
 * Returns UDPS_TYPECODE_UNKNOWN for types that have no UDPS equivalent.
 * Defined inline to avoid a separate translation unit for this trivial table.
 */
inline uint8 UDPSTypeDescriptorToCode(TypeDescriptor td) {
    if (td == UnsignedInteger8Bit)   return UDPS_TYPECODE_UINT8;
    if (td == SignedInteger8Bit)     return UDPS_TYPECODE_INT8;
    if (td == UnsignedInteger16Bit)  return UDPS_TYPECODE_UINT16;
    if (td == SignedInteger16Bit)    return UDPS_TYPECODE_INT16;
    if (td == UnsignedInteger32Bit)  return UDPS_TYPECODE_UINT32;
    if (td == SignedInteger32Bit)    return UDPS_TYPECODE_INT32;
    if (td == UnsignedInteger64Bit)  return UDPS_TYPECODE_UINT64;
    if (td == SignedInteger64Bit)    return UDPS_TYPECODE_INT64;
    if (td == Float32Bit)            return UDPS_TYPECODE_FLOAT32;
    if (td == Float64Bit)            return UDPS_TYPECODE_FLOAT64;
    return UDPS_TYPECODE_UNKNOWN;
}

/**
 * @brief Returns the wire byte size of one element for the given type code.
 * Returns 0 for UDPS_TYPECODE_UNKNOWN.
 */
inline uint32 UDPSTypeCodeByteSize(uint8 typeCode) {
    switch (typeCode) {
        case UDPS_TYPECODE_UINT8:  case UDPS_TYPECODE_INT8:   return 1u;
        case UDPS_TYPECODE_UINT16: case UDPS_TYPECODE_INT16:  return 2u;
        case UDPS_TYPECODE_UINT32: case UDPS_TYPECODE_INT32:
        case UDPS_TYPECODE_FLOAT32:                           return 4u;
        case UDPS_TYPECODE_UINT64: case UDPS_TYPECODE_INT64:
        case UDPS_TYPECODE_FLOAT64:                           return 8u;
        default:                                              return 0u;
    }
}

/*---------------------------------------------------------------------------*/
/*                        Inline packet-building helpers                     */
/*---------------------------------------------------------------------------*/

/**
 * @brief Serialise a UDPSPacketHeader into the first 17 bytes of @p buf.
 * @param buf    Must have room for at least UDPS_HEADER_SIZE bytes.
 * @param type   Packet type (UDPS_TYPE_*).
 * @param counter   Per-update sequence counter.
 * @param fragIdx   0-based fragment index.
 * @param totalFrags  Total fragments for this update.
 * @param payloadBytes  Bytes of payload that follow this header.
 */
inline void UDPSBuildHeader(uint8 *buf,
                             uint8  type,
                             uint32 counter,
                             uint16 fragIdx,
                             uint16 totalFrags,
                             uint32 payloadBytes)
{
    UDPSPacketHeader hdr;
    hdr.magic          = UDPS_MAGIC;
    hdr.type           = type;
    hdr.counter        = counter;
    hdr.fragmentIdx    = fragIdx;
    hdr.totalFragments = totalFrags;
    hdr.payloadBytes   = payloadBytes;
    memcpy(buf, &hdr, UDPS_HEADER_SIZE);
}

} /* namespace MARTe */

#endif /* UDPS_PROTOCOL_H_ */
