#include <stdint.h>
#include <stddef.h>
#include "uniqid.h"
#include "atomic.h"
#include "spinlock/spinlock.h"
#include "hal/hal.h"

static const char *base62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

static uint32_t uniqstate = 0u;
SpinLock uniqstate_spinlock;

void randcrypto_uniqid_init(void) {
  spinlock_init(&uniqstate_spinlock);
}

uint32_t uniqmix32(uint32_t x) {
  x ^= x >> 16;
  x *= 0x85ebca6bU;
  x ^= x >> 13;
  x *= 0xc2b2ae35U;
  x ^= x >> 16;
  return x;
}

void uniqseed(void) {
  if (uniqstate != 0u)
    return;

  uint32_t seed = 0;
  int32_t r = hal_randnum();
  if (r != -1) {
    seed = (uint32_t)r;
  } else {
    uintptr_t p = (uintptr_t)(void *)&uniqstate;
    seed = (uint32_t)(p ^ (p>>32)) ^ 0x9E3779B9u;
  }

  uniqstate = seed ? seed : 1u;
}

void randcrypto_gen_uniqid(char *out, size_t n) {
  if (!out || n == 0) {
    return;
  }

  spinlock_acquire(&uniqstate_spinlock);

  uniqseed();

  int32_t r = hal_randnum();
  uint32_t extra = (r != -1) ? (uint32_t)r : uniqstate * 0x27d4eb2dU;

  uniqstate += 0x9E3779B1u;
  uint32_t v = uniqmix32(uniqstate ^ extra);
  
  spinlock_release(&uniqstate_spinlock);

  for (size_t i = 0; i < n; i++) {
    v = uniqmix32(v + i * 0x85ebca6bU);
    out[i] = base62[v % 62u];
  }
  out[n] = '\0';
}
