#ifdef __cplusplus
extern "C" {
#endif

#include "ulib/chacha.h"
#include "ulib/clock.h"
#include "ulib/gen.h"
#include "ulib/md5.h"
#include "ulib/node.h"
#include "ulib/sha1.h"

#ifdef __cplusplus
}
#endif


/* randomize uu_node */
static void uu_gen_randomize(pUCXT) {
  cc_rand32(aUCXT, (U32*)&UCXT.gen_node[0]);
  cc_rand16(aUCXT, (U16*)&UCXT.gen_node[4]);
  UCXT.gen_node[0] |= 0x01; /* set mcast */
}

/* call at boot */
void uu_gen_init(pUCXT) {
  UCXT.gen_epoch = (((U64) 0x01B21DD2) << 32) | 0x13814000; /* unused */
  UCXT.gen_use_unique = 0;
  UCXT.gen_has_real_node = 0;
  UCXT.gen_node[0] = 0;;
  UCXT.gen_node[1] = 0;;
  UCXT.gen_node[2] = 0;;
  UCXT.gen_node[3] = 0;;
  UCXT.gen_node[4] = 0;;
  UCXT.gen_node[5] = 0;;
  UCXT.gen_real_node[0] = 0;
  UCXT.gen_real_node[1] = 0;
  UCXT.gen_real_node[2] = 0;
  UCXT.gen_real_node[3] = 0;
  UCXT.gen_real_node[4] = 0;
  UCXT.gen_real_node[5] = 0;

  /* get the real node or randomize it */
  if (uu_get_node_id(aUCXT, (U8*)&UCXT.gen_node) == 1) {
    UCXT.gen_has_real_node = 1;
    UCXT.gen_real_node[0] = UCXT.gen_node[0];
    UCXT.gen_real_node[1] = UCXT.gen_node[1];
    UCXT.gen_real_node[2] = UCXT.gen_node[2];
    UCXT.gen_real_node[3] = UCXT.gen_node[3];
    UCXT.gen_real_node[4] = UCXT.gen_node[4];
    UCXT.gen_real_node[5] = UCXT.gen_node[5];
  }
  else {
    UCXT.gen_has_real_node = 0;
    uu_gen_randomize(aUCXT);
  }
}

void uu_gen_setrand(pUCXT) {
  UCXT.gen_use_unique = 0;
  uu_gen_randomize(aUCXT);
}

void uu_gen_setuniq(pUCXT) {
  UCXT.gen_use_unique = 1;
}

/* returns 1 if has real node, or 0 */
int uu_realnode(pUCXT, struct_uu_t *out) {
  uu_v0gen(aUCXT, out, NULL);
  out->v1.node[0] = UCXT.gen_real_node[0];
  out->v1.node[1] = UCXT.gen_real_node[1];
  out->v1.node[2] = UCXT.gen_real_node[2];
  out->v1.node[3] = UCXT.gen_real_node[3];
  out->v1.node[4] = UCXT.gen_real_node[4];
  out->v1.node[5] = UCXT.gen_real_node[5];
  return UCXT.gen_has_real_node;
}


void uu_v0gen(pUCXT, struct_uu_t *out, char *dptr) {
  out->v0.low  = 0;
  out->v0.high = 0;
}

void uu_v1gen(pUCXT, struct_uu_t *out, char *dptr) {
  U64   clock_reg;
  U16   clock_seq;

  uu_clock(aUCXT, &clock_reg, &clock_seq);
  clock_reg += (((U64)0x01b21dd2) << 32) + 0x13814000;

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

  if (UCXT.gen_use_unique) uu_gen_randomize(aUCXT);
  out->v1.node[0] = UCXT.gen_node[0];
  out->v1.node[1] = UCXT.gen_node[1];
  out->v1.node[2] = UCXT.gen_node[2];
  out->v1.node[3] = UCXT.gen_node[3];
  out->v1.node[4] = UCXT.gen_node[4];
  out->v1.node[5] = UCXT.gen_node[5];
}

void uu_v3gen(pUCXT, struct_uu_t *out, char *dptr) {
  /* out points to a v1 uuid to use as namespace. */
  /* out also points to output buffer for v3 uuid. */
  /* dptr points to string to use as name. */
  uu_md5_hash(aUCXT, out, dptr);
}

void uu_v4gen(pUCXT, struct_uu_t *out, char *dptr) {
  U64 *cp = (U64*)out;

  cc_rand64(aUCXT, cp++);
  cc_rand64(aUCXT, cp);
  out->v4.rand_b_and_version = out->v4.rand_b_and_version & 0xffff0fff | 0x00004000;
  out->v4.rand_c_and_variant = out->v4.rand_c_and_variant & 0x3fffffff | 0x80000000;
}

void uu_v5gen(pUCXT, struct_uu_t *out, char *dptr) {
  /* out points to a v1 uuid to use as namespace. */
  /* out also points to output buffer for v5 uuid. */
  /* dptr points to string to use as name. */
  uu_sha_hash(aUCXT, out, dptr);
}

void uu_v6gen(pUCXT, struct_uu_t *out, char *dptr) {
  U64   clock_reg;
  U16   clock_seq;

  uu_clock(aUCXT, &clock_reg, &clock_seq);
  clock_reg += (((U64)0x01b21dd2) << 32) + 0x13814000;

  out->v6.time_high             = (U32)(clock_reg >> 28);
  out->v6.time_mid              = (U16)(clock_reg >> 12);
  out->v6.time_low_and_version  = (U16)clock_reg & 0x0fff | 0x6000;
  out->v6.clock_seq_and_variant = clock_seq & 0x3fff | 0x8000;

  /* use the same node as v1 */
  if (UCXT.gen_use_unique) uu_gen_randomize(aUCXT);
  out->v6.node[0] = UCXT.gen_node[0];
  out->v6.node[1] = UCXT.gen_node[1];
  out->v6.node[2] = UCXT.gen_node[2];
  out->v6.node[3] = UCXT.gen_node[3];
  out->v6.node[4] = UCXT.gen_node[4];
  out->v6.node[5] = UCXT.gen_node[5];
}

void uu_v7gen(pUCXT, struct_uu_t *out, char *dptr) {
  U64   clock_reg;
  U16   clock_seq;

  uu_clock(aUCXT, &clock_reg, &clock_seq);
  clock_reg /= 10000;

  cc_rand16(aUCXT, &out->v7.rand_a_and_version);
  cc_rand64(aUCXT, &out->v7.rand_b_and_variant);
  out->v7.time_high             = (U32)(clock_reg >> 16);
  out->v7.time_low              = (U16)(clock_reg & 0xffff);
  out->v7.rand_a_and_version = out->v7.rand_a_and_version & 0x0fff | 0x7000;
  out->v7.rand_b_and_variant = out->v7.rand_b_and_variant
    & 0x3fffffffffffffffULL
    | 0x8000000000000000ULL;
}

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