#include <Connectivity.hpp>
#include <map>

#include <Messenger.hpp>

template<size_t Dimension>
Connectivity<Dimension>::Connectivity() {}

template<size_t Dimension>
void Connectivity<Dimension>::
_buildFrom(const ConnectivityDescriptor& descriptor)
{
#warning All of these should be checked by ConnectivityDescriptor
  Assert(descriptor.cell_to_node_vector.size() == descriptor.cell_type_vector.size());
  Assert(descriptor.cell_number_vector.size() == descriptor.cell_type_vector.size());
  if constexpr (Dimension>1) {
    Assert(descriptor.cell_to_face_vector.size() == descriptor.cell_type_vector.size());
    Assert(descriptor.face_to_node_vector.size() == descriptor.face_number_vector.size());
    Assert(descriptor.face_owner_vector.size() == descriptor.face_number_vector.size());
  }

  auto& cell_to_node_matrix
      = m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::node)];
  cell_to_node_matrix = descriptor.cell_to_node_vector;

  {
    WeakCellValue<CellType> cell_type(*this);
    parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j){
        cell_type[j] = descriptor.cell_type_vector[j];
      });
    m_cell_type = cell_type;
  }

  {
    WeakCellValue<int> cell_number(*this);
    cell_number = convert_to_array(descriptor.cell_number_vector);
    m_cell_number = cell_number;
  }

  {
    WeakNodeValue<int> node_number(*this);
    node_number = convert_to_array(descriptor.node_number_vector);
    m_node_number = node_number;
  }

  {
    WeakCellValue<int> cell_global_index(*this);
#warning index must start accounting number of global indices of other procs
#warning must take care of ghost cells
    int first_index = 0;
    parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
      cell_global_index[j] = first_index+j;
      });
    m_cell_global_index = cell_global_index;
  }


  {
    WeakCellValue<double> inv_cell_nb_nodes(*this);
    parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
        const auto& cell_nodes = cell_to_node_matrix.rowConst(j);
        inv_cell_nb_nodes[j] = 1./cell_nodes.length;
      });
    m_inv_cell_nb_nodes = inv_cell_nb_nodes;
  }

  {
    WeakCellValue<int> cell_owner(*this);
    cell_owner = convert_to_array(descriptor.cell_owner_vector);
    m_cell_owner = cell_owner;
  }

  {
    const int rank = parallel::rank();
    WeakCellValue<bool> cell_is_owned(*this);
    parallel_for(this->numberOfCells(), PASTIS_LAMBDA(const CellId& j) {
        cell_is_owned[j] = (m_cell_owner[j] == rank);
      });
    m_cell_is_owned = cell_is_owned;
  }

  {
    WeakNodeValue<int> node_owner(*this);
    node_owner = convert_to_array(descriptor.node_owner_vector);
    m_node_owner = node_owner;
  }

  {
    const int rank = parallel::rank();
    WeakNodeValue<bool> node_is_owned(*this);
    parallel_for(this->numberOfNodes(), PASTIS_LAMBDA(const NodeId& r) {
        node_is_owned[r] = (m_node_owner[r] == rank);
      });
    m_node_is_owned = node_is_owned;
  }

  if constexpr (Dimension>1) {
    auto& face_to_node_matrix
        = m_item_to_item_matrix[itemTId(ItemType::face)][itemTId(ItemType::node)];
    face_to_node_matrix = descriptor.face_to_node_vector;

    auto& cell_to_face_matrix
        = m_item_to_item_matrix[itemTId(ItemType::cell)][itemTId(ItemType::face)];
    cell_to_face_matrix = descriptor.cell_to_face_vector;

    {
      FaceValuePerCell<bool> cell_face_is_reversed(*this);
      for (CellId j=0; j<descriptor.cell_face_is_reversed_vector.size(); ++j) {
        const auto& face_cells_vector = descriptor.cell_face_is_reversed_vector[j];
        for (unsigned short lj=0; lj<face_cells_vector.size(); ++lj) {
          cell_face_is_reversed(j,lj) = face_cells_vector[lj];
        }
      }

      m_cell_face_is_reversed = cell_face_is_reversed;
    }
    {
      WeakFaceValue<int> face_number(*this);
      face_number = convert_to_array(descriptor.face_number_vector);
      m_face_number = face_number;
    }
    {
      WeakFaceValue<int> face_owner(*this);
      face_owner = convert_to_array(descriptor.face_owner_vector);
      m_face_owner = face_owner;
    }

    {
      const int rank = parallel::rank();
      WeakFaceValue<bool> face_is_owned(*this);
      parallel_for(this->numberOfFaces(), PASTIS_LAMBDA(const FaceId& l) {
          face_is_owned[l] = (m_face_owner[l] == rank);
        });
      m_face_is_owned = face_is_owned;
    }

    m_ref_node_list_vector = descriptor.template refItemListVector<ItemType::node>();
    m_ref_edge_list_vector = descriptor.template refItemListVector<ItemType::edge>();
    m_ref_face_list_vector = descriptor.template refItemListVector<ItemType::face>();
    m_ref_cell_list_vector = descriptor.template refItemListVector<ItemType::cell>();
  }
}

template Connectivity1D::Connectivity();
template Connectivity2D::Connectivity();
template Connectivity3D::Connectivity();

template void Connectivity1D::_buildFrom(const ConnectivityDescriptor&);
template void Connectivity2D::_buildFrom(const ConnectivityDescriptor&);
template void Connectivity3D::_buildFrom(const ConnectivityDescriptor&);