#ifndef ITEM_TO_ITEM_MATRIX_HPP
#define ITEM_TO_ITEM_MATRIX_HPP

#include <ItemType.hpp>
#include <ItemId.hpp>

#include <Kokkos_Core.hpp>
#include <ConnectivityMatrix.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:

    KOKKOS_INLINE_FUNCTION
    size_t size() const
    {
      return m_row.length;
    }

    KOKKOS_INLINE_FUNCTION
    TargetItemId operator[](const size_t& j) const
    {
      return m_row(j);
    }

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

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

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

    KOKKOS_INLINE_FUNCTION
    SubItemList(const SubItemList&) = default;

    KOKKOS_INLINE_FUNCTION
    SubItemList(SubItemList&&) = default;

    KOKKOS_INLINE_FUNCTION
    ~SubItemList() = default;
  };

 private:
  const ConnectivityMatrix& m_connectivity_matrix;

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

  template <typename IndexType>
  KOKKOS_INLINE_FUNCTION
  const auto operator[](const IndexType& source_id) const
  {
    static_assert(std::is_same<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));
  }

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

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

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

  KOKKOS_INLINE_FUNCTION
  ItemToItemMatrix(ItemToItemMatrix&&) = default;

  KOKKOS_INLINE_FUNCTION
  ItemToItemMatrix(const ItemToItemMatrix&) = default;

  KOKKOS_INLINE_FUNCTION
  ~ItemToItemMatrix() = default;
};

#endif // ITEM_TO_ITEM_MATRIX_HPP