#ifndef UU_TYPE_H
#define UU_TYPE_H

#ifndef PERL_VERSION
# undef SUBVERSION /* OS/390 */
# include <patchlevel.h>
# ifndef SUBVERSION
#   define SUBVERSION 0
# endif
# if !defined(PATCHLEVEL)))
#   include <could_not_find_Perl_patchlevel.h>
# endif
# define PERL_REVISION    5
# define PERL_VERSION     PATCHLEVEL
# define PERL_SUBVERSION  SUBVERSION
#endif

#ifndef PERL_VERSION_DECIMAL
# define PERL_VERSION_DECIMAL(r,v,s) (r*1000000 + v*1000 + s)
#endif
#ifndef PERL_DECIMAL_VERSION
# define PERL_DECIMAL_VERSION \
    PERL_VERSION_DECIMAL(PERL_REVISION,PERL_VERSION,PERL_SUBVERSION)
#endif

#ifndef PERL_VERSION_LT
# define PERL_VERSION_LT(r,v,s) \
    (PERL_DECIMAL_VERSION < PERL_VERSION_DECIMAL(r,v,s))
#endif
#ifndef PERL_VERSION_EQ
# define PERL_VERSION_EQ(r,v,s) \
    (PERL_DECIMAL_VERSION == PERL_VERSION_DECIMAL(r,v,s))
#endif


/* related from SO for gcc:
* https://stackoverflow.com/questions/1188939/representing-128-bit-numbers-in-c
*******************************************************************************
  typedef unsigned int uint128_t __attribute__((mode(TI)));

   uint64_t x = 0xABCDEF01234568;
   uint64_t y = ~x;

   uint128_t result = ((uint128_t) x * y);

   printf("%016llX * %016llX -> ", x, y);

   uint64_t r1 = (result >> 64);
   uint64_t r2 = result;

   printf("%016llX %016llX\n", r1, r2);
*/


/* Quad_t/U64 first appear in 5.00563 (8175356b44).
    #ifdef Quad_t
      typedef I64TYPE I64;
      typedef U64TYPE U64;
    #endif
 * U64TYPE also first appears in 8175356b44.
 * HAS_QUAD also appears in 5.00563 (de1c261475),
 * but did not take over the typedefs until 6b8eaf9322,
 * (also 5.00563) where U64 type was restricted to core.
 * QUADKIND also first appears in 6b8eaf9322.
 *
 * U64 made available outside core in 5.27.7.
*/
#if PERL_VERSION_LT(5, 27, 7)
#  ifdef U64TYPE
     typedef U64TYPE U64;
#  else
     typedef uint64_t U64;
#  endif
#endif

typedef union {
  struct {
    /* keep this struct first for namespace init */
    U32 time_low;
    U16 time_mid;
    U16 time_high_and_version;
    U16 clock_seq_and_variant;
    U8  node[6];
  } v1;
  struct {
    U64 low;
    U64 high;
  } v0;
  struct {
    U32 md5_high32;
    U16 md5_high16;
    U16 md5_mid_and_version;
    U32 md5_low_and_variant;
    U32 md5_low;
  } v3;
  struct {
    U32 rand_a;
    U32 rand_b_and_version;
    U32 rand_c_and_variant;
    U32 rand_d;
  } v4;
  struct {
    U32 sha1_high32;
    U16 sha1_high16;
    U16 sha1_mid_and_version;
    U32 sha1_low_and_variant;
    U32 sha1_low;
  } v5;
  struct {
    U32 time_high;
    U16 time_mid;
    U16 time_low_and_version;
    U16 clock_seq_and_variant;
    U8  node[6];
  } v6;
  struct {
    U32 time_high;
    U16 time_low;
    U16 rand_a_and_version;
    U64 rand_b_and_variant;
  } v7;
  U64 __align;
} struct_uu_t;

typedef unsigned char UCHAR;

#define CC_STATESZ  16           /* words: 4 constant, 8 key, 2 counter, 2 nonce */
#define CC_KEYSZ    40           /* bytes of user supplied key+nonce */
#define CC_CORESZ   64           /* bytes output by core */
#define CC_BUFSZ    16*CC_CORESZ /* bytes we get at a time (1024) */
#define CC_ROUNDS   20

typedef struct {
  U32           state[CC_STATESZ];
  UCHAR         buf[CC_BUFSZ];
  U16           have;
  unsigned int  pid;
  U64           __align;
} cc_st;

typedef struct {
  char    *path;
  STRLEN  len;
} struct_pathlen_t;

/* this should be aligned at least 4 bytes, better yet 16 */
typedef U8 uu_t[16];

typedef struct {
  U64               xo_s[4];
  U64               sm_x;
  U64               gen_epoch;
  U8                gen_node[6];  /* need 64bit align */
  U16               __align;
  U8                gen_real_node[6];  /* need 64bit align */
  void              (*myU2time)(pTHX_ UV ret[2]);
  int               gen_has_real_node;
  int               gen_use_unique;
  cc_st             cc;  /* aligned 64bit */
  int               clock_state_fd;
  FILE              *clock_state_f;
  struct_pathlen_t  clock_pathlen;
  int               clock_adj;
  struct timeval    clock_last;
  U16               clock_seq;
  UV                thread_id;
  U64               clock_defer_100ns;
  U64               clock_prev_reg;
} my_cxt_t;

#define pUCXT pTHX_ my_cxt_t *my_cxtp
#define aUCXT aTHX_ my_cxtp
#define UCXT  (*my_cxtp)

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