/**
 * UUID.js - RFC-compliant UUID Generator for JavaScript
 *
 * @file
 * @author  LiosK
 * @version v4.2.12
 * @license Apache License 2.0: Copyright (c) 2010-2022 LiosK
 */

/**
 * @class
 * @classdesc {@link UUID} object.
 * @hideconstructor
 */
var UUID;

UUID = (function(overwrittenUUID) {
"use strict";

// Core Component {{{

/**
 * Generates a version 4 UUID as a hexadecimal string.
 * @returns {string} Hexadecimal UUID string.
 */
UUID.generate = function() {
  var rand = UUID._getRandomInt, hex = UUID._hexAligner;
  return  hex(rand(32), 8)          // time_low
        + "-"
        + hex(rand(16), 4)          // time_mid
        + "-"
        + hex(0x4000 | rand(12), 4) // time_hi_and_version
        + "-"
        + hex(0x8000 | rand(14), 4) // clock_seq_hi_and_reserved clock_seq_low
        + "-"
        + hex(rand(48), 12);        // node
};

/**
 * Returns an unsigned x-bit random integer.
 * @private
 * @param {number} x Unsigned integer ranging from 0 to 53, inclusive.
 * @returns {number} Unsigned x-bit random integer (0 <= f(x) < 2^x).
 */
UUID._getRandomInt = function(x) {
  if (x < 0 || x > 53) { return NaN; }
  var n = 0 | Math.random() * 0x40000000; // 1 << 30
  return x > 30 ? n + (0 | Math.random() * (1 << x - 30)) * 0x40000000 : n >>> 30 - x;
};

/**
 * Converts an integer to a zero-filled hexadecimal string.
 * @private
 * @param {number} num
 * @param {number} length
 * @returns {string}
 */
UUID._hexAligner = function(num, length) {
  var str = num.toString(16), i = length - str.length, z = "0";
  for (; i > 0; i >>>= 1, z += z) { if (i & 1) { str = z + str; } }
  return str;
};

/**
 * Retains the value of 'UUID' global variable assigned before loading UUID.js.
 * @since 3.2
 * @type {any}
 */
UUID.overwrittenUUID = overwrittenUUID;

// }}}

// Advanced Random Number Generator Component {{{

(function() {

  var mathPRNG = UUID._getRandomInt;

  /**
   * Enables Math.random()-based pseudorandom number generator instead of cryptographically safer options.
   * @since v3.5.0
   * @deprecated This method is provided only to work around performance drawbacks of the safer algorithms.
   */
  UUID.useMathRandom = function() {
    UUID._getRandomInt = mathPRNG;
  };

  var crypto = null, cryptoPRNG = mathPRNG;
  if (typeof window !== "undefined" && (crypto = window.crypto || window.msCrypto)) {
    if (crypto.getRandomValues && typeof Uint32Array !== "undefined") {
      // Web Cryptography API
      cryptoPRNG = function(x) {
        if (x < 0 || x > 53) { return NaN; }
        var ns = new Uint32Array(x > 32 ? 2 : 1);
        ns = crypto.getRandomValues(ns) || ns;
        return x > 32 ? ns[0] + (ns[1] >>> 64 - x) * 0x100000000 : ns[0] >>> 32 - x;
      };
    }
  } else if (typeof require !== "undefined" && (crypto = require("crypto"))) {
    if (crypto.randomBytes) {
      // nodejs
      cryptoPRNG = function(x) {
        if (x < 0 || x > 53) { return NaN; }
        var buf = crypto.randomBytes(x > 32 ? 8 : 4), n = buf.readUInt32BE(0);
        return x > 32 ? n + (buf.readUInt32BE(4) >>> 64 - x) * 0x100000000 : n >>> 32 - x;
      };
    }
  }
  UUID._getRandomInt = cryptoPRNG;

})();

// }}}

// UUID Object Component {{{

/**
 * Names of UUID internal fields.
 * @type {string[]}
 * @constant
 * @since 3.0
 */
UUID.FIELD_NAMES = ["timeLow", "timeMid", "timeHiAndVersion",
                    "clockSeqHiAndReserved", "clockSeqLow", "node"];

/**
 * Sizes of UUID internal fields.
 * @type {number[]}
 * @constant
 * @since 3.0
 */
UUID.FIELD_SIZES = [32, 16, 16, 8, 8, 48];

/**
 * Creates a version 4 {@link UUID} object.
 * @returns {UUID} Version 4 {@link UUID} object.
 * @since 3.0
 */
UUID.genV4 = function() {
  var rand = UUID._getRandomInt;
  return new UUID()._init(rand(32), rand(16), // time_low time_mid
                          0x4000 | rand(12),  // time_hi_and_version
                          0x80   | rand(6),   // clock_seq_hi_and_reserved
                          rand(8), rand(48)); // clock_seq_low node
};

/**
 * Converts a hexadecimal UUID string to a {@link UUID} object.
 * @param {string} strId Hexadecimal UUID string ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").
 * @returns {UUID} {@link UUID} object or null.
 * @since 3.0
 */
UUID.parse = function(strId) {
  var r, p = /^\s*(urn:uuid:|\{)?([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{2})([0-9a-f]{2})-([0-9a-f]{12})(\})?\s*$/i;
  if (r = p.exec(strId)) {
    var l = r[1] || "", t = r[8] || "";
    if (((l + t) === "") ||
        (l === "{" && t === "}") ||
        (l.toLowerCase() === "urn:uuid:" && t === "")) {
      return new UUID()._init(parseInt(r[2], 16), parseInt(r[3], 16),
                              parseInt(r[4], 16), parseInt(r[5], 16),
                              parseInt(r[6], 16), parseInt(r[7], 16));
    }
  }
  return null;
};

/**
 * Initializes a {@link UUID} object.
 * @private
 * @constructs UUID
 * @param {number} [timeLow=0] time_low field (octet 0-3, uint32).
 * @param {number} [timeMid=0] time_mid field (octet 4-5, uint16).
 * @param {number} [timeHiAndVersion=0] time_hi_and_version field (octet 6-7, uint16).
 * @param {number} [clockSeqHiAndReserved=0] clock_seq_hi_and_reserved field (octet 8, uint8).
 * @param {number} [clockSeqLow=0] clock_seq_low field (octet 9, uint8).
 * @param {number} [node=0] node field (octet 10-15, uint48).
 * @returns {UUID} this.
 */
UUID.prototype._init = function() {
  var names = UUID.FIELD_NAMES, sizes = UUID.FIELD_SIZES;
  var bin = UUID._binAligner, hex = UUID._hexAligner;

  /**
   * UUID internal field values as an array of integers.
   * @type {number[]}
   */
  this.intFields = new Array(6);

  /**
   * UUID internal field values as an array of binary strings.
   * @type {string[]}
   */
  this.bitFields = new Array(6);

  /**
   * UUID internal field values as an array of hexadecimal strings.
   * @type {string[]}
   */
  this.hexFields = new Array(6);

  for (var i = 0; i < 6; i++) {
    var intValue = parseInt(arguments[i] || 0);
    this.intFields[i] = this.intFields[names[i]] = intValue;
    this.bitFields[i] = this.bitFields[names[i]] = bin(intValue, sizes[i]);
    this.hexFields[i] = this.hexFields[names[i]] = hex(intValue, sizes[i] >>> 2);
  }

  /**
   * UUID version number.
   * @type {number}
   */
  this.version = (this.intFields.timeHiAndVersion >>> 12) & 0xF;

  /**
   * 128-bit binary string representation.
   * @type {string}
   */
  this.bitString = this.bitFields.join("");

  /**
   * Non-delimited hexadecimal string representation ("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").
   * @type {string}
   * @since v3.3.0
   */
  this.hexNoDelim = this.hexFields.join("");

  /**
   * Hexadecimal string representation ("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").
   * @type {string}
   */
  this.hexString = this.hexFields[0] + "-" + this.hexFields[1] + "-" + this.hexFields[2]
                 + "-" + this.hexFields[3] + this.hexFields[4] + "-" + this.hexFields[5];

  /**
   * URN string representation ("urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").
   * @type {string}
   */
  this.urn = "urn:uuid:" + this.hexString;

  return this;
};

/**
 * Converts an integer to a zero-filled binary string.
 * @private
 * @param {number} num
 * @param {number} length
 * @returns {string}
 */
UUID._binAligner = function(num, length) {
  var str = num.toString(2), i = length - str.length, z = "0";
  for (; i > 0; i >>>= 1, z += z) { if (i & 1) { str = z + str; } }
  return str;
};

/**
 * Returns the hexadecimal string representation.
 * @returns {string} {@link UUID#hexString}.
 */
UUID.prototype.toString = function() { return this.hexString; };

/**
 * Tests if two {@link UUID} objects are equal.
 * @param {UUID} uuid
 * @returns {boolean} True if two {@link UUID} objects are equal.
 */
UUID.prototype.equals = function(uuid) {
  if (!(uuid instanceof UUID)) { return false; }
  for (var i = 0; i < 6; i++) {
    if (this.intFields[i] !== uuid.intFields[i]) { return false; }
  }
  return true;
};

/**
 * Nil UUID object.
 * @type {UUID}
 * @constant
 * @since v3.4.0
 */
UUID.NIL = new UUID()._init(0, 0, 0, 0, 0, 0);

// }}}

// UUID Version 1 Component {{{

/**
 * Creates a version 1 {@link UUID} object.
 * @returns {UUID} Version 1 {@link UUID} object.
 * @since 3.0
 */
UUID.genV1 = function() {
  if (UUID._state == null) { UUID.resetState(); }
  var now = new Date().getTime(), st = UUID._state;
  if (now != st.timestamp) {
    if (now < st.timestamp) { st.sequence++; }
    st.timestamp = now;
    st.tick = UUID._getRandomInt(12); // up to 4095, allowing 5904 tick per msec
  } else if (st.tick < 9992) {
    // advance sub-millisecond fraction up to 9999 100-nanoseconds
    st.tick += 1 + UUID._getRandomInt(3);
  } else {
    // advance seq if tick overflows in remote chance
    st.sequence++;
  }

  // format time fields
  var tf = UUID._getTimeFieldValues(st.timestamp);
  var tl = tf.low + st.tick;
  var thav = (tf.hi & 0xFFF) | 0x1000;  // set version '0001'

  // format clock sequence
  st.sequence &= 0x3FFF;
  var cshar = (st.sequence >>> 8) | 0x80; // set variant '10'
  var csl = st.sequence & 0xFF;

  return new UUID()._init(tl, tf.mid, thav, cshar, csl, st.node);
};

/**
 * Re-initializes the internal state for version 1 UUID creation.
 * @since 3.0
 */
UUID.resetState = function() {
  UUID._state = new UUIDState();
};

function UUIDState() {
  var rand = UUID._getRandomInt;
  this.timestamp = 0;
  this.tick = 0; // timestamp fraction smaller than a millisecond
  this.sequence = rand(14);
  this.node = (rand(8) | 1) * 0x10000000000 + rand(40); // set multicast bit '1'
}

/**
 * Probability to advance the timestamp fraction: the ratio of tick movements to sequence increments.
 * @private
 * @type {number}
 * @deprecated This property is no longer used since v4.2.11.
 */
UUID._tsRatio = 1 / 4;

/**
 * Persistent internal state for version 1 UUID creation.
 * @private
 * @type {UUIDState}
 */
UUID._state = null;

/**
 * @private
 * @param {Date|number} time ECMAScript Date Object or milliseconds from 1970-01-01.
 * @returns {any}
 */
UUID._getTimeFieldValues = function(time) {
  var ts = time - Date.UTC(1582, 9, 15);
  var hm = ((ts / 0x100000000) * 10000) & 0xFFFFFFF;
  return  { low: ((ts & 0xFFFFFFF) * 10000) % 0x100000000,
            mid: hm & 0xFFFF, hi: hm >>> 16, timestamp: ts };
};

// }}}

// create local namespace
function UUID() {}

// for nodejs
if (typeof module === "object" && typeof module.exports === "object") {
  module.exports = UUID;
}

return UUID;

})(UUID);

// vim: fdm=marker fmr&
