#include "uulib/gen.h"
#include "uulib/chacha.h"
#include "uulib/clock.h"
#include "uulib/node.h"

static const U64 EPOCH = (((U64) 0x01B21DD2) << 32) | 0x13814000;

static IV v1unique  = 0;             /* aligned 4 or 8 */
static U8 v1node[6] = {0,0,0,0,0,0}; /* needs to be aligned 4 */


/* randomize v1node */
static void _v1_randomize(void) {
  cc_rand32((U32*)&v1node[0]);
  cc_rand16((U16*)&v1node[4]);
  v1node[0] |= 0x01; /* set mcast */
}

/* call at boot */
void uu_v1gen_init() {
  /* get the real node or randomize it */
  if (uu_get_node_id((U8*)&v1node) != 1)
    _v1_randomize();
}

void uu_v1set_rand() {
  v1unique = 0;
  _v1_randomize();
}

void uu_v1set_uniq() {
  v1unique = 1;
}


void uu_v1gen(struct uu *out) {
  U64   clock_reg;
  U16   clock_seq;

  uu_clock(&clock_reg, &clock_seq);
  out->time_low              = (U32)clock_reg;
  out->time_mid              = (U16)(clock_reg >> 32 & 0xffff);
  out->time_high_and_version = (U16)(clock_reg >> 48 & 0x0fff | 0x1000);
  out->clock_seq_and_variant = clock_seq & 0x3fff | 0x8000;

  if (v1unique) _v1_randomize();
  out->node[0] = v1node[0];
  out->node[1] = v1node[1];
  out->node[2] = v1node[2];
  out->node[3] = v1node[3];
  out->node[4] = v1node[4];
  out->node[5] = v1node[5];
}

void uu_v4gen(struct uu *out) {
  U64 *cp;
  cp = (U64*)out;
  cc_rand64(cp++);
  cc_rand64(cp);
  out->time_high_and_version = out->time_high_and_version & 0x0fff | 0x4000;
  out->clock_seq_and_variant = out->clock_seq_and_variant & 0x3fff | 0x8000;
}

/* this should try v4 and fall back to v1 if CSRNG not available */
void uu_vXgen(struct uu *out) {
  U64 *cp;
  cp = (U64*)out;
  cc_rand64(cp++);
  cc_rand64(cp);
  out->time_high_and_version = out->time_high_and_version & 0x0fff | 0x4000;
  out->clock_seq_and_variant = out->clock_seq_and_variant & 0x3fff | 0x8000;
}

/* ex:set ts=2 sw=2 itab=spaces: */
