//
// Created by loki on 8-2-19.
//

#ifndef T_MAN_UUID_H
#define T_MAN_UUID_H

#include <random>

namespace util {
union uuid_t {
  std::uint8_t b8[16];
  std::uint16_t b16[8];
  std::uint32_t b32[4];
  std::uint64_t b64[2];

  static uuid_t generate(std::default_random_engine &engine) {
    std::uniform_int_distribution<std::uint8_t> dist(0, std::numeric_limits<std::uint8_t>::max());

    uuid_t buf;
    for (auto &el : buf.b8) {
      el = dist(engine);
    }

    buf.b8[7] &= (std::uint8_t) 0b00101111;
    buf.b8[9] &= (std::uint8_t) 0b10011111;

    return buf;
  }

  static uuid_t generate() {
    std::random_device r;

    std::default_random_engine engine{r()};

    return generate(engine);
  }

  [[nodiscard]] std::string string() const {
    std::string result;

    result.reserve(sizeof(uuid_t) * 2 + 4);

    auto hex = util::hex(*this, true);
    auto hex_view = hex.to_string_view();

    std::string_view slices[] = {
      hex_view.substr(0, 8),
      hex_view.substr(8, 4),
      hex_view.substr(12, 4),
      hex_view.substr(16, 4)
    };
    auto last_slice = hex_view.substr(20, 12);

    for(auto &slice : slices) {
      std::copy(std::begin(slice), std::end(slice), std::back_inserter(result));

      result.push_back('-');
    }

    std::copy(std::begin(last_slice), std::end(last_slice), std::back_inserter(result));

    return result;
  }

  constexpr bool operator==(const uuid_t &other) const {
    return b64[0] == other.b64[0] && b64[1] == other.b64[1];
  }

  constexpr bool operator<(const uuid_t &other) const {
    return (b64[0] < other.b64[0] || (b64[0] == other.b64[0] && b64[1] < other.b64[1]));
  }

  constexpr bool operator>(const uuid_t &other) const {
    return (b64[0] > other.b64[0] || (b64[0] == other.b64[0] && b64[1] > other.b64[1]));
  }
};
}
#endif //T_MAN_UUID_H
