// vim:fileencoding=utf-8:foldmethod=marker

#include "uuid.h"
#include "../common/aliases/aliases.h"
#include "../common/assert/assert.h"
#include "../base/strings/str8/str8.h"
#include "../prng/xorshift/xorshift.h"
#include <inttypes.h>

#define UUID_STR_FORMAT ("%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.12" PRIx64)

typedef struct uuid4 UUID4;
struct uuid4 {
  u64 high;
  u64 low;
};

wapp_intern UUID4 generate_uuid4(void);
wapp_intern void  uuid4_to_uuid(const UUID4* uuid4, WUUID *uuid);

WUUID *wapp_uuid_init_uuid4(WUUID *uuid) {
  wapp_debug_assert(uuid != NULL, "`uuid` should not be NULL");

  UUID4 uuid4 = generate_uuid4();
  uuid4_to_uuid(&uuid4, uuid);

  return uuid;
}

wapp_intern UUID4 generate_uuid4(void) {
  wapp_persist XOR256State state = {0};
  wapp_persist b8 initialised = false;

  if (!initialised) {
    initialised = true;
    state       = wapp_prng_xorshift_init_state();
  }

  UUID4 uuid = (UUID4){
    .high = wapp_prng_xorshift_256(&state),
    .low  = wapp_prng_xorshift_256(&state),
  };

  uuid.high = (uuid.high & 0xffffffffffff0fff) | 0x0000000000004000;
  uuid.low  = (uuid.low  & 0x3fffffffffffffff) | 0x8000000000000000;

  return uuid;
}

wapp_intern void uuid4_to_uuid(const UUID4* uuid4, WUUID *uuid) {
  u64 group1 = uuid4->high >> 32;
  u64 group2 = (uuid4->high << 32) >> 48;
  u64 group3 = (uuid4->high << 48) >> 48;
  u64 group4 = uuid4->low >> 48;
  u64 group5 = (uuid4->low << 16) >> 16;

  wapp_str8_format(&(uuid->uuid), UUID_STR_FORMAT, group1, group2, group3, group4, group5);
}
