#ifndef ITEM_TO_ITEM_MATRIX_HPP
#define ITEM_TO_ITEM_MATRIX_HPP

#include <mesh/ConnectivityMatrix.hpp>
#include <mesh/ItemId.hpp>
#include <mesh/ItemType.hpp>

#include <utils/PugsUtils.hpp>

template <ItemType SourceItemType, ItemType TargetItemType>
class ItemToItemMatrix
{
 public:
  using SourceItemId = ItemIdT<SourceItemType>;
  using TargetItemId = ItemIdT<TargetItemType>;

  template <typename RowType>
  class SubItemList
  {
   private:
    const RowType m_row;

   public:
    PUGS_INLINE
    size_t
    size() const
    {
      return m_row.length;
    }

    PUGS_INLINE
    TargetItemId
    operator[](size_t j) const
    {
      Assert(j < m_row.length);
      return m_row(j);
    }

    PUGS_INLINE
    SubItemList(const RowType& row) : m_row{row}
    {
      ;
    }

    PUGS_INLINE
    SubItemList& operator=(const SubItemList&) = default;

    PUGS_INLINE
    SubItemList& operator=(SubItemList&&) = default;

    PUGS_INLINE
    SubItemList(const SubItemList&) = default;

    PUGS_INLINE
    SubItemList(SubItemList&&) = default;

    PUGS_INLINE
    ~SubItemList() = default;
  };

 private:
  const ConnectivityMatrix& m_connectivity_matrix;

 public:
  auto
  numberOfEntries() const
  {
    return m_connectivity_matrix.numEntries();
  }

  auto
  entries() const
  {
    return m_connectivity_matrix.entries();
  }

  PUGS_INLINE
  auto
  operator[](const SourceItemId& source_id) const
  {
    Assert(source_id < m_connectivity_matrix.numRows());
    using RowType = decltype(m_connectivity_matrix.rowConst(source_id));
    return SubItemList<RowType>(m_connectivity_matrix.rowConst(source_id));
  }

  template <typename IndexType>
  PUGS_INLINE const auto&
  operator[](const IndexType& source_id) const
  {
    Assert(source_id < m_connectivity_matrix.numRows());
    static_assert(std::is_same_v<IndexType, SourceItemId>, "ItemToItemMatrix must be indexed using correct ItemId");
    using RowType = decltype(m_connectivity_matrix.rowConst(source_id));
    return SubItemList<RowType>(m_connectivity_matrix.rowConst(source_id));
  }

  PUGS_INLINE
  ItemToItemMatrix(const ConnectivityMatrix& connectivity_matrix) : m_connectivity_matrix{connectivity_matrix}
  {
    ;
  }

  PUGS_INLINE
  ItemToItemMatrix& operator=(const ItemToItemMatrix&) = default;

  PUGS_INLINE
  ItemToItemMatrix& operator=(ItemToItemMatrix&&) = default;

  PUGS_INLINE
  ItemToItemMatrix(ItemToItemMatrix&&) = default;

  PUGS_INLINE
  ItemToItemMatrix(const ItemToItemMatrix&) = default;

  PUGS_INLINE
  ~ItemToItemMatrix() = default;
};

#endif   // ITEM_TO_ITEM_MATRIX_HPP