#pragma once

#include "Regs.h"
#include "metrolib/core/Types.h"

//-----------------------------------------------------------------------------

inline bool posedge(bool a, bool b) {
  return !bit0(a) && bit0(b);
}

inline bool negedge(bool a, bool b) {
  return bit0(a) && !bit0(b);
}

//-----------------------------------------------------------------------------

inline void combine_hash(uint64_t& a, uint64_t b) {
  a = byteswap((a ^ b) * 0xff51afd7ed558ccd);
}

uint64_t hash_all_bits(const void* key, const int len, uint64_t seed);
uint64_t hash_low_bit(const void* key, const int len, uint64_t seed);

//-----------------------------------------------------------------------------

inline uint8_t pack(bool a, bool b) {
  return (bit0(a) << 0) | (bit0(b) << 1);
}

inline uint8_t pack(bool a, bool b, bool c, bool d) {
  return (bit0(a) << 0) | (bit0(b) << 1) | (bit0(c) << 2) | (bit0(d) << 3);
}

inline uint8_t pack(wire a, wire b, wire c, wire d, wire e, wire f, wire g, wire h) {
  return (bit0(a) << 0) | (bit0(b) << 1) | (bit0(c) << 2) | (bit0(d) << 3) | (bit0(e) << 4) | (bit0(f) << 5) | (bit0(g) << 6) | (bit0(h) << 7);
}

//-----------------------------------------------------------------------------

template<typename SRC>
inline uint32_t bit_pack(const SRC* psrc, int c) {
  uint8_t* src = (uint8_t*)psrc;
  uint32_t result = 0;
  for (int i = 0; i < c; i++) result |= (bit0(src[i]) << i);
  return result;
}

template<typename SRC>
inline uint32_t bit_pack(const SRC& src) {
  return bit_pack(&src, sizeof(SRC));
}

//-----------------------------------------------------------------------------

template<typename SRC>
inline uint32_t bit_pack_inv(const SRC* psrc, int c) {
  uint8_t* src = (uint8_t*)psrc;
  uint32_t result = 0;
  for (int i = 0; i < c; i++) result |= (!bit0(src[i]) << i);
  return result;
}

template<class SRC>
inline uint32_t bit_pack_inv(const SRC& src) {
  return bit_pack_inv(&src, sizeof(SRC));
}

//-----------------------------------------------------------------------------

template<class DST>
inline void bit_unpack(DST* pdst, int c, const uint32_t src) {
  uint8_t* dst = (uint8_t*)pdst;
  for (int i = 0; i < c; i++) {
    dst[i] &= ~1;
    dst[i] |= bit(src, i);
  }
}

template<class DST>
inline void bit_unpack(DST& dst, const uint32_t src) {
  bit_unpack(&dst, sizeof(DST), src);
}

//-----------------------------------------------------------------------------

template<class DST>
inline void bit_init(DST* pdst, int c, const uint8_t flags, const uint32_t src) {
  uint8_t* dst = (uint8_t*)pdst;
  for (int i = 0; i < c; i++) {
    dst[i] = flags | bit(src, i);
  }
}

template<class DST>
inline void bit_init(DST& dst, const uint8_t flags, const uint32_t src) {
  bit_init(&dst, sizeof(DST), flags, src);
}

template<class DST>
inline void bit_init2(DST& dst, const uint8_t flags, const uint32_t src) {
  bit_init(&dst, sizeof(DST), flags, src);
}

//-----------------------------------------------------------------------------

template<class DST>
inline void bit_unpack_inv(DST* pdst, int c, const uint32_t src) {
  uint8_t* dst = (uint8_t*)pdst;
  for (int i = 0; i < c; i++) {
    dst[i] &= ~1;
    dst[i] |= !bit(src, i);
  }
}

template<class DST>
inline void bit_unpack_inv(DST& dst, const uint32_t src) {
  bit_unpack_inv(&dst, sizeof(DST), src);
}

//-----------------------------------------------------------------------------

template<typename T>
inline void bit_cat(T& dst, int bit_min, int bit_max, uint32_t src) {

  int mask = (1 << (bit_max + 1)) - (1 << bit_min);

  dst = T((dst & ~mask) | ((src << bit_min) & mask));
}

//-----------------------------------------------------------------------------

template<typename T>
void bit_mask(T& a, uint8_t mask) {
  auto dst = (uint8_t*)&a;
  for (auto i = 0; i < sizeof(T); i++) dst[i] &= mask;
}

template<typename T>
void bit_set(T& a, uint8_t mask) {
  auto dst = (uint8_t*)&a;
  for (auto i = 0; i < sizeof(T); i++) dst[i] |= mask;
}

//-----------------------------------------------------------------------------

template<typename T>
T bit_purge(const T& a) {
  T b = a;
  bit_mask(b, 0x01);
  return b;
}

//-----------------------------------------------------------------------------

template<typename T>
void bit_check(T& a) {
  uint8_t* pa = (uint8_t*)&a;
  for (size_t i = 0; i < sizeof(T); i++) {
    if (pa[i] & ~1) {
      LOG_R("bit_check found non-bit at offset %d - 0x%02x\n",  (int)i, pa);
      //debugbreak();
    }
  }
}

//-----------------------------------------------------------------------------
