/**
 * @file SHA1.h
 * @brief Inline SHA-1 implementation (public domain).
 *
 * Used exclusively for the WebSocket HTTP upgrade handshake
 * (Sec-WebSocket-Accept computation per RFC 6455).
 * No external dependencies.
 */

#ifndef STREAMHUB_SHA1_H_
#define STREAMHUB_SHA1_H_

#include "CompilerTypes.h"
#include <string.h>

namespace StreamHub {

using MARTe::uint8;
using MARTe::uint32;
using MARTe::uint64;

/**
 * @brief Compute SHA-1 hash of @p data (len bytes) into @p digest (20 bytes).
 */
inline void SHA1(const uint8 *data, uint32 len, uint8 digest[20]) {

    uint32 h0 = 0x67452301u;
    uint32 h1 = 0xEFCDAB89u;
    uint32 h2 = 0x98BADCFEu;
    uint32 h3 = 0x10325476u;
    uint32 h4 = 0xC3D2E1F0u;

    /* ---- pre-process: append bit '1', zeros, 64-bit BE length ----------- */
    uint8 msg[128];
    memset(msg, 0, sizeof(msg));

    uint32 msgLen = len;
    if (len < 56u) {
        memcpy(msg, data, len);
        msg[len] = 0x80u;
        /* 8-byte big-endian bit count at bytes 56-63 */
        uint64 bitLen = static_cast<uint64>(len) * 8u;
        for (int i = 7; i >= 0; i--) {
            msg[56 + i] = static_cast<uint8>(bitLen & 0xFFu);
            bitLen >>= 8;
        }
        msgLen = 64u;
    } else {
        /* two-block path for inputs up to 55+64=119 bytes — sufficient for WS keys */
        memcpy(msg, data, len);
        msg[len] = 0x80u;
        uint64 bitLen = static_cast<uint64>(len) * 8u;
        for (int i = 7; i >= 0; i--) {
            msg[120 + i] = static_cast<uint8>(bitLen & 0xFFu);
            bitLen >>= 8;
        }
        msgLen = 128u;
    }

    /* ---- process each 64-byte block -------------------------------------- */
    for (uint32 blk = 0u; blk < msgLen; blk += 64u) {
        uint32 w[80];
        const uint8 *b = msg + blk;
        uint32 i;
        for (i = 0u; i < 16u; i++) {
            w[i] = (static_cast<uint32>(b[i*4u + 0u]) << 24u) |
                   (static_cast<uint32>(b[i*4u + 1u]) << 16u) |
                   (static_cast<uint32>(b[i*4u + 2u]) <<  8u) |
                   (static_cast<uint32>(b[i*4u + 3u]));
        }
        for (i = 16u; i < 80u; i++) {
            uint32 v = w[i-3u] ^ w[i-8u] ^ w[i-14u] ^ w[i-16u];
            w[i] = (v << 1u) | (v >> 31u);
        }

        uint32 a = h0, bv = h1, c = h2, d = h3, e = h4;
        for (i = 0u; i < 80u; i++) {
            uint32 f, k;
            if (i < 20u) {
                f = (bv & c) | ((~bv) & d);
                k = 0x5A827999u;
            } else if (i < 40u) {
                f = bv ^ c ^ d;
                k = 0x6ED9EBA1u;
            } else if (i < 60u) {
                f = (bv & c) | (bv & d) | (c & d);
                k = 0x8F1BBCDCu;
            } else {
                f = bv ^ c ^ d;
                k = 0xCA62C1D6u;
            }
            uint32 tmp = ((a << 5u) | (a >> 27u)) + f + e + k + w[i];
            e = d; d = c;
            c = (bv << 30u) | (bv >> 2u);
            bv = a; a = tmp;
        }
        h0 += a; h1 += bv; h2 += c; h3 += d; h4 += e;
    }

    /* ---- produce big-endian digest --------------------------------------- */
    digest[ 0] = static_cast<uint8>(h0 >> 24u); digest[ 1] = static_cast<uint8>(h0 >> 16u);
    digest[ 2] = static_cast<uint8>(h0 >>  8u); digest[ 3] = static_cast<uint8>(h0);
    digest[ 4] = static_cast<uint8>(h1 >> 24u); digest[ 5] = static_cast<uint8>(h1 >> 16u);
    digest[ 6] = static_cast<uint8>(h1 >>  8u); digest[ 7] = static_cast<uint8>(h1);
    digest[ 8] = static_cast<uint8>(h2 >> 24u); digest[ 9] = static_cast<uint8>(h2 >> 16u);
    digest[10] = static_cast<uint8>(h2 >>  8u); digest[11] = static_cast<uint8>(h2);
    digest[12] = static_cast<uint8>(h3 >> 24u); digest[13] = static_cast<uint8>(h3 >> 16u);
    digest[14] = static_cast<uint8>(h3 >>  8u); digest[15] = static_cast<uint8>(h3);
    digest[16] = static_cast<uint8>(h4 >> 24u); digest[17] = static_cast<uint8>(h4 >> 16u);
    digest[18] = static_cast<uint8>(h4 >>  8u); digest[19] = static_cast<uint8>(h4);
}

} /* namespace StreamHub */

#endif /* STREAMHUB_SHA1_H_ */
