#ifndef ITEM_ID_HPP
#define ITEM_ID_HPP

#include <PastisMacros.hpp>
#include <ItemType.hpp>

template <ItemType item_type>
class ItemIdT
{
 public:
  using base_type = unsigned int;

 private:
  base_type m_id;

 public:
  PASTIS_INLINE
  constexpr operator const base_type&() const
  {
    return m_id;
  }

  PASTIS_INLINE
  constexpr ItemIdT operator++(int)
  {
    ItemIdT item_id(m_id);
    ++m_id;
    return std::move(item_id);
  }

  PASTIS_INLINE
  constexpr ItemIdT& operator++()
  {
    ++m_id;
    return *this;
  }

  PASTIS_INLINE
  constexpr ItemIdT& operator=(const base_type& id)
  {
    m_id = id;
    return *this;
  }

  PASTIS_INLINE
  constexpr ItemIdT& operator=(const ItemIdT&) = default;

  PASTIS_INLINE
  constexpr ItemIdT& operator=(ItemIdT&&) = default;

  PASTIS_INLINE
  constexpr ItemIdT(const base_type& id) : m_id{id}{}

  PASTIS_INLINE
  constexpr ItemIdT(const ItemIdT&) = default;

  PASTIS_INLINE
  constexpr ItemIdT(ItemIdT&&) = default;

  PASTIS_INLINE
  constexpr ItemIdT() = default;

  PASTIS_INLINE
  ~ItemIdT() = default;

  // forbidden rules
  template <ItemType other_item_type>
  ItemIdT(const ItemIdT<other_item_type>&)
  {
    static_assert(other_item_type == item_type,
                  "wrong type of item");
  }

  template <ItemType other_item_type>
  ItemIdT& operator=(const ItemIdT<other_item_type>&)
  {
    static_assert(other_item_type == item_type,
                  "wrong type of item");
  }


  template <ItemType other_item_type>
  ItemIdT& operator=(ItemIdT<other_item_type>&&)
  {
    static_assert(other_item_type == item_type,
                  "wrong type of item");
  }
};

using NodeId = ItemIdT<ItemType::node>;
using EdgeId = ItemIdT<ItemType::edge>;
using FaceId = ItemIdT<ItemType::face>;
using CellId = ItemIdT<ItemType::cell>;

#endif // ITEM_ID_HPP