#pragma once
#include <stdint.h>
#include <string.h>

static const uint8_t SS_KEY[32] = {
    0x4e,0x91,0x2c,0x7b,0xd0,0x58,0xa3,0x16,
    0xef,0x6a,0x04,0xb9,0x3d,0x82,0xc7,0x11,
    0x5f,0xe4,0x29,0x9c,0x70,0x08,0xad,0x46,
    0xf3,0x1b,0x65,0xda,0x37,0x80,0xce,0x52
};

static inline uint32_t ss_rotl(uint32_t x, int n) {
    return (x << n) | (x >> (32 - n));
}

static inline void ss_qr(uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d) {
    *a += *b; *d ^= *a; *d = ss_rotl(*d, 16);
    *c += *d; *b ^= *c; *b = ss_rotl(*b, 12);
    *a += *b; *d ^= *a; *d = ss_rotl(*d, 8);
    *c += *d; *b ^= *c; *b = ss_rotl(*b, 7);
}

static void ss_block(uint8_t out[64], const uint8_t key[32], const uint8_t nonce[12], uint32_t counter) {
    uint32_t s[16];
    s[0] = 0x61707865; s[1] = 0x3320646e; s[2] = 0x79622d32; s[3] = 0x6b206574;
    memcpy(s + 4, key, 32);
    s[12] = counter;
    memcpy(s + 13, nonce, 12);
    uint32_t w[16];
    memcpy(w, s, 64);
    for (int i = 0; i < 10; ++i) {
        ss_qr(w+0, w+4, w+8,  w+12);
        ss_qr(w+1, w+5, w+9,  w+13);
        ss_qr(w+2, w+6, w+10, w+14);
        ss_qr(w+3, w+7, w+11, w+15);
        ss_qr(w+0, w+5, w+10, w+15);
        ss_qr(w+1, w+6, w+11, w+12);
        ss_qr(w+2, w+7, w+8,  w+13);
        ss_qr(w+3, w+4, w+9,  w+14);
    }
    for (int i = 0; i < 16; ++i) w[i] += s[i];
    memcpy(out, w, 64);
}

static void ss_crypt(uint8_t* data, size_t n, const uint8_t nonce[12]) {
    uint8_t block[64];
    uint32_t counter = 1;
    size_t off = 0;
    while (off < n) {
        ss_block(block, SS_KEY, nonce, counter++);
        size_t chunk = n - off;
        if (chunk > 64) chunk = 64;
        for (size_t i = 0; i < chunk; ++i)
            data[off + i] ^= block[i];
        off += chunk;
    }
}

static uint64_t ss_fnv64(const uint8_t* p, size_t n) {
    uint64_t h = 14695981039346656037ull;
    for (size_t i = 0; i < n; ++i) {
        h ^= p[i];
        h *= 1099511628211ull;
    }
    return h;
}
