#include <Connectivity.hpp>
#include <ConnectivityUtils.hpp>

#include <ConnectivityComputer.hpp>
#include <map>

void ConnectivityComputer::
computeInverseConnectivityMatrix(const ConnectivityMatrix& item_to_child_item_matrix,
                                 ConnectivityMatrix& child_item_to_item_matrix) const
{
  std::map<unsigned int, std::vector<unsigned int>> child_item_to_item_vector_map;
  const size_t& number_of_items = item_to_child_item_matrix.numRows();

  for (unsigned int j=0; j<number_of_items; ++j) {
    const auto& item_to_child_items = item_to_child_item_matrix.rowConst(j);
    for (unsigned int r=0; r<item_to_child_items.length; ++r) {
      child_item_to_item_vector_map[item_to_child_items(r)].push_back(j);
    }
  }

  {
    size_t i=0;
    for (const auto& [child_item_id, item_vector] : child_item_to_item_vector_map) {
      if (child_item_id != i) {
        std::cerr << "sparse item numerotation NIY\n";
        std::exit(0);
      }
      ++i;
    }
  }

  std::vector<std::vector<unsigned int>> child_item_to_items_vector(child_item_to_item_vector_map.size());
  for (const auto& [child_item_id, item_vector] : child_item_to_item_vector_map) {
    child_item_to_items_vector[child_item_id] = item_vector;
  }
  child_item_to_item_matrix = child_item_to_items_vector;
}

template <TypeOfItem child_item_type,
          TypeOfItem item_type,
          typename ConnectivityType>
SubItemValuePerItem<const unsigned short, item_type, child_item_type>
ConnectivityComputer::computeLocalItemNumberInChildItem(const ConnectivityType& connectivity) const
{
#warning check that these connectivities are named correctly
  const ConnectivityMatrix& child_item_to_items_matrix
      = getConnectivityMatrix<child_item_type, item_type>(connectivity);
  const ConnectivityMatrix& item_to_child_items_matrix
      = getConnectivityMatrix<item_type, child_item_type>(connectivity);

  SubItemValuePerItem<unsigned short, item_type, child_item_type> item_number_in_child_item(connectivity);
  for (unsigned int r=0; r<child_item_to_items_matrix.numRows(); ++r) {
    const auto& child_item_to_items = child_item_to_items_matrix.rowConst(r);
    for (unsigned short J=0; J<child_item_to_items.length; ++J) {
      const unsigned int j = child_item_to_items(J);
      const auto& item_to_child_items = item_to_child_items_matrix.rowConst(j);

      for (unsigned int R=0; R<item_to_child_items.length; ++R) {
        if (item_to_child_items(R) == r) {
          item_number_in_child_item(r,J) = R;
          break;
        }
      }
    }
  }

  return item_number_in_child_item;
}

// 1D

template SubItemValuePerItem<const unsigned short, TypeOfItem::cell, TypeOfItem::node>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::node,
                                  TypeOfItem::cell>(const Connectivity1D& connectivity) const;

template SubItemValuePerItem<const unsigned short, TypeOfItem::cell, TypeOfItem::face>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::face,
                                  TypeOfItem::cell>(const Connectivity1D& connectivity) const;

template SubItemValuePerItem<const unsigned short, TypeOfItem::node, TypeOfItem::cell>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::cell,
                                  TypeOfItem::node>(const Connectivity1D& connectivity) const;

// 2D

template SubItemValuePerItem<const unsigned short, TypeOfItem::cell, TypeOfItem::node>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::node,
                                  TypeOfItem::cell>(const Connectivity2D& connectivity) const;

template SubItemValuePerItem<const unsigned short, TypeOfItem::cell, TypeOfItem::face>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::face,
                                  TypeOfItem::cell>(const Connectivity2D& connectivity) const;

template SubItemValuePerItem<const unsigned short, TypeOfItem::node, TypeOfItem::cell>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::cell,
                                  TypeOfItem::node>(const Connectivity2D& connectivity) const;

// 3D

template SubItemValuePerItem<const unsigned short, TypeOfItem::cell, TypeOfItem::node>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::node,
                                  TypeOfItem::cell>(const Connectivity3D& connectivity) const;


template SubItemValuePerItem<const unsigned short, TypeOfItem::cell, TypeOfItem::face>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::face,
                                  TypeOfItem::cell>(const Connectivity3D& connectivity) const;

template SubItemValuePerItem<const unsigned short, TypeOfItem::node, TypeOfItem::cell>
ConnectivityComputer::
computeLocalItemNumberInChildItem<TypeOfItem::cell,
                                  TypeOfItem::node>(const Connectivity3D& connectivity) const;