#ifndef ITEM_ARRAY_UTILS_HPP
#define ITEM_ARRAY_UTILS_HPP

#include <utils/Messenger.hpp>

#include <mesh/Connectivity.hpp>
#include <mesh/ItemArray.hpp>
#include <mesh/Synchronizer.hpp>
#include <mesh/SynchronizerManager.hpp>

#include <iostream>

template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void
synchronize(ItemArray<DataType, item_type, ConnectivityPtr>& item_array)
{
  static_assert(not std::is_const_v<DataType>, "cannot synchronize ItemArray of const data");
  if (parallel::size() > 1) {
    auto& manager                     = SynchronizerManager::instance();
    const IConnectivity* connectivity = item_array.connectivity_ptr().get();
    Synchronizer& synchronizer        = manager.getConnectivitySynchronizer(connectivity);
    synchronizer.synchronize(item_array);
  }
}

template <typename DataType, ItemType item_type, typename ConnectivityPtr>
bool
isSynchronized(ItemArray<const DataType, item_type, ConnectivityPtr> item_array)
{
  bool is_synchronized = true;

  if (parallel::size() > 1) {
    ItemArray<std::remove_const_t<DataType>, item_type> item_array_copy = copy(item_array);
    synchronize(item_array_copy);
    for (ItemIdT<item_type> item_id = 0; item_id < item_array_copy.numberOfItems(); ++item_id) {
      for (size_t i = 0; i < item_array.sizeOfArrays(); ++i) {
        if (item_array_copy[item_id][i] != item_array[item_id][i]) {
          is_synchronized = false;
        }
      }
    }

    is_synchronized = parallel::allReduceAnd(is_synchronized);
  }

  return is_synchronized;
}

#endif   // ITEM_ARRAY_UTILS_HPP