// SHA-1 (FIPS 180-4 §6.1) — needed for the WebSocket opening handshake's Sec-WebSocket-Accept
// (RFC 6455 §4.2.2), which hashes the client's key with the protocol GUID. SHA-1 is broken for
// signatures but WebSocket uses it only as a fixed handshake token, so this is the correct
// primitive there. Self-built on 32-bit words, mirroring the SHA-256 structure.

///|
/// Rotate a 32-bit word left by `n`.
fn rotl32(x : UInt, n : Int) -> UInt {
  (x << n) | (x >> (32 - n))
}

///|
/// The SHA-1 digest of `msg` (20 bytes).
pub fn sha1(msg : Bytes) -> Bytes {
  let h : Array[UInt] = [
    0x67452301U, 0xEFCDAB89U, 0x98BADCFEU, 0x10325476U, 0xC3D2E1F0U,
  ]
  // Pad: 0x80, zeros to length % 64 == 56, then the 64-bit big-endian bit length.
  let padded = Buffer()
  padded.write_bytes(msg[:])
  padded.write_byte(b'\x80')
  while padded.length() % 64 != 56 {
    padded.write_byte(b'\x00')
  }
  let bitlen = msg.length().to_int64() * 8L
  for i = 7; i >= 0; i = i - 1 {
    padded.write_byte((bitlen >> (i * 8)).to_byte())
  }
  let data = padded.to_bytes()
  let nblocks = data.length() / 64
  for blk = 0; blk < nblocks; blk = blk + 1 {
    let base = blk * 64
    let w : Array[UInt] = Array::make(80, 0U)
    for i = 0; i < 16; i = i + 1 {
      let j = base + i * 4
      w[i] = (data[j].to_int().reinterpret_as_uint() << 24) |
        (data[j + 1].to_int().reinterpret_as_uint() << 16) |
        (data[j + 2].to_int().reinterpret_as_uint() << 8) |
        data[j + 3].to_int().reinterpret_as_uint()
    }
    for i = 16; i < 80; i = i + 1 {
      w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1)
    }
    let mut a = h[0]
    let mut b = h[1]
    let mut c = h[2]
    let mut d = h[3]
    let mut e = h[4]
    for i = 0; i < 80; i = i + 1 {
      let (f, k) = if i < 20 {
        ((b & c) | (b.lnot() & d), 0x5A827999U)
      } else if i < 40 {
        (b ^ c ^ d, 0x6ED9EBA1U)
      } else if i < 60 {
        ((b & c) | (b & d) | (c & d), 0x8F1BBCDCU)
      } else {
        (b ^ c ^ d, 0xCA62C1D6U)
      }
      let temp = rotl32(a, 5) + f + e + k + w[i]
      e = d
      d = c
      c = rotl32(b, 30)
      b = a
      a = temp
    }
    h[0] = h[0] + a
    h[1] = h[1] + b
    h[2] = h[2] + c
    h[3] = h[3] + d
    h[4] = h[4] + e
  }
  let out = Buffer()
  for i = 0; i < 5; i = i + 1 {
    out.write_byte((h[i] >> 24).to_byte())
    out.write_byte((h[i] >> 16).to_byte())
    out.write_byte((h[i] >> 8).to_byte())
    out.write_byte(h[i].to_byte())
  }
  out.to_bytes()
}