/**
 * @file        compute_checksum_impl.c
 * @brief       Reference implementation of the bmcli `ComputeChecksum` DLL API.
 *
 * Compile into a DLL/.so and pass to bmcli via `--algo=./compute_checksum.dll`.
 * This reference implements CRC-32 IEEE (the same algorithm as the built-in
 * `crc32-ieee`), so it is mostly a template: replace the algorithm body with
 * your OEM-specific checksum and recompile. The signature MUST stay stable.
 *
 * Build (MinGW, Windows):
 *   gcc -O2 -shared -o compute_checksum.dll compute_checksum_impl.c
 * Build (Linux):
 *   gcc -O2 -fPIC -shared -o libcompute_checksum.so compute_checksum_impl.c
 *
 * Signature mirrors the style of Vector's seed/key DLL (GenerateKeyEx) already
 * used by `bmcli uds unlock`: standard explicit params for the well-known
 * inputs (polynomial / init / xorout for a CRC) plus a trailing opaque
 * `void* userArg` for whatever caller-side context the algorithm needs.
 */
#include <stdint.h>

#if defined(_WIN32)
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __attribute__((visibility("default")))
#endif

/**
 * @brief  Compute a checksum over `data`.
 *
 * @param  data          Input bytes.
 * @param  dataLen       Number of input bytes.
 * @param  outChecksum   Caller-provided output buffer.
 * @param  maxOutSize    Capacity of outChecksum in bytes.
 * @param  outSize       [out] Number of bytes written to outChecksum.
 * @param  polynomial    CRC polynomial (LSB-first reflected form). 0 = use default.
 * @param  initValue     CRC initial value. 0 = use default.
 * @param  xorOutValue   Final XOR value. 0 = no xor / use default.
 * @param  userArg       Opaque caller context (may be NULL). Interpretation is
 *                       algorithm-specific; bmcli currently always passes NULL.
 * @return 0 on success, non-zero error code otherwise.
 */
EXPORT int ComputeChecksum(const uint8_t *data, uint32_t dataLen,
                           uint8_t *outChecksum, uint32_t maxOutSize,
                           uint32_t *outSize,
                           uint32_t polynomial, uint32_t initValue,
                           uint32_t xorOutValue, void *userArg)
{
    (void)userArg;

    /* Defaults: CRC-32 IEEE 802.3. */
    if (polynomial  == 0) polynomial  = 0xEDB88320u;  /* reflected 0x04C11DB7 */
    if (initValue   == 0) initValue   = 0xFFFFFFFFu;
    if (xorOutValue == 0) xorOutValue = 0xFFFFFFFFu;

    /* The standard CRC-32 output is 4 bytes. */
    if (!outChecksum || maxOutSize < 4 || !outSize) return 1;

    uint32_t crc = initValue;
    for (uint32_t i = 0; i < dataLen; i++) {
        crc ^= data[i];
        for (int b = 0; b < 8; b++) {
            if (crc & 1u) crc = (crc >> 1) ^ polynomial;
            else          crc = (crc >> 1);
        }
    }
    crc ^= xorOutValue;

    /* Emit big-endian (matches how UDS routines carry the checksum on the wire). */
    outChecksum[0] = (uint8_t)((crc >> 24) & 0xFF);
    outChecksum[1] = (uint8_t)((crc >> 16) & 0xFF);
    outChecksum[2] = (uint8_t)((crc >> 8)  & 0xFF);
    outChecksum[3] = (uint8_t)(crc & 0xFF);
    *outSize = 4;
    return 0;
}
