// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
// SPDX-License-Identifier: AGPL-3.0-or-later

/**
 * @file rawl2.h
 * @brief Raw Layer-2 Ethernet frame codec (DETWS_ENABLE_RAWL2).
 *
 * The host-testable core of raw-L2 frame TX/RX: build and parse Ethernet II frames (and 802.1Q
 * VLAN-tagged frames) so the app can inject/receive arbitrary L2 frames - the basis for the raw-L2
 * industrial protocols (PROFINET DCP, IEC 61850 GOOSE, POWERLINK, SERCOS) and for custom management /
 * proprietary MAC framing. On device the bytes go out via `esp_eth_transmit()` (wired) or
 * `esp_wifi_80211_tx()` (Wi-Fi); the MAC normally appends the FCS, so the builder emits the frame
 * without it and `detws_eth_fcs` is provided for the cases that need it.
 *
 *   Ethernet II:  [dst MAC 6][src MAC 6][ethertype 2][payload]
 *   802.1Q:       [dst 6][src 6][0x8100][TCI 2][ethertype 2][payload]
 *
 * Pure, zero heap, no stdlib, host-testable.
 */

#ifndef DETERMINISTICESPASYNCWEBSERVER_RAWL2_H
#define DETERMINISTICESPASYNCWEBSERVER_RAWL2_H

#include "ServerConfig.h"
#include <stddef.h>
#include <stdint.h>

#if DETWS_ENABLE_RAWL2

enum
{
    ETH_ALEN = 6,            ///< MAC address length.
    ETH_HDR_LEN = 14,        ///< dst + src + ethertype.
    ETH_VLAN_HDR_LEN = 18,   ///< with the 4-octet 802.1Q tag.
    ETH_TPID_8021Q = 0x8100, ///< 802.1Q tag protocol id.
    ETHERTYPE_IPV4 = 0x0800,
    ETHERTYPE_ARP = 0x0806,
    ETHERTYPE_PROFINET = 0x8892, ///< PROFINET RT / DCP.
    ETHERTYPE_GOOSE = 0x88B8,    ///< IEC 61850 GOOSE.
    ETHERTYPE_POWERLINK = 0x88AB
};

/**
 * @brief Build an Ethernet II frame (no FCS).
 * @return the frame length (14 + payload_len), or 0 if it won't fit or a pointer is null.
 */
size_t detws_eth_build(const uint8_t *dst, const uint8_t *src, uint16_t ethertype, const uint8_t *payload,
                       size_t payload_len, uint8_t *out, size_t cap);

/**
 * @brief Build an 802.1Q VLAN-tagged Ethernet frame (no FCS).
 * @param pcp   priority code point (0..7).
 * @param dei   drop-eligible indicator.
 * @param vid   VLAN id (0..4095).
 * @return the frame length (18 + payload_len), or 0 on overflow.
 */
size_t detws_eth_build_vlan(const uint8_t *dst, const uint8_t *src, uint8_t pcp, bool dei, uint16_t vid,
                            uint16_t ethertype, const uint8_t *payload, size_t payload_len, uint8_t *out, size_t cap);

/** @brief A parsed Ethernet frame (pointers into the input). */
struct EthFrame
{
    const uint8_t *dst;
    const uint8_t *src;
    bool vlan;
    uint8_t pcp;
    uint16_t vid;
    uint16_t ethertype;
    const uint8_t *payload;
    size_t payload_len;
};

/** @brief Parse an Ethernet II / 802.1Q frame (FCS not expected). @return true if well-formed. */
bool detws_eth_parse(const uint8_t *frame, size_t len, EthFrame *out);

/** @brief IEEE 802.3 frame check sequence (CRC-32, reflected, init 0xFFFFFFFF, xorout 0xFFFFFFFF). */
uint32_t detws_eth_fcs(const uint8_t *bytes, size_t len);

#endif // DETWS_ENABLE_RAWL2
#endif // DETERMINISTICESPASYNCWEBSERVER_RAWL2_H
