#include <utils/Messenger.hpp>

#include <iostream>

namespace parallel
{
Messenger* Messenger::m_instance = nullptr;

void
Messenger::create(int& argc, char* argv[])
{
  if (Messenger::m_instance == nullptr) {
    Messenger::m_instance = new Messenger(argc, argv);
  } else {
    throw UnexpectedError("Messenger already created");
  }
}

void
Messenger::destroy()
{
  // One allows multiple destruction to handle unexpected code exit
  if (Messenger::m_instance != nullptr) {
    delete Messenger::m_instance;
    Messenger::m_instance = nullptr;
  }
}

Messenger::Messenger([[maybe_unused]] int& argc, [[maybe_unused]] char* argv[])
{
#ifdef PUGS_HAS_MPI
  MPI_Init(&argc, &argv);

  const char* coupled_color = std::getenv("PUGS_COUPLED_COLOR");
  if (coupled_color == NULL) {
    m_comm_world_pugs = MPI_COMM_WORLD;
  } else {
    int color = atoi(coupled_color);
    int global_rank;
    int global_size;
    int key = 0;

    MPI_Comm_rank(MPI_COMM_WORLD, &global_rank);
    MPI_Comm_size(MPI_COMM_WORLD, &global_size);

    auto res = MPI_Comm_split(MPI_COMM_WORLD, color, key, &m_comm_world_pugs);
    if (res) {
      MPI_Abort(MPI_COMM_WORLD, res);
    }

    int local_rank;
    int local_size;

    MPI_Comm_rank(m_comm_world_pugs, &local_rank);
    MPI_Comm_size(m_comm_world_pugs, &local_size);

    std::cout << "----------------- " << rang::fg::green << "pugs coupled info " << rang::fg::reset
              << " ----------------------" << '\n';
    std::cout << "Coupling mode activated";
    std::cout << "\n\t Global size: " << global_size;
    std::cout << "\n\t Global rank: " << global_rank + 1;
    std::cout << "\n\t Coupled color: " << color;
    std::cout << "\n\t local size: " << local_size;
    std::cout << "\n\t local rank: " << local_rank + 1 << std::endl;
    std::cout << "---------------------------------------" << '\n';
  }

  m_rank = [&]() {
    int rank;
    MPI_Comm_rank(m_comm_world_pugs, &rank);
    return rank;
  }();

  m_size = [&]() {
    int size = 0;
    MPI_Comm_size(m_comm_world_pugs, &size);
    return size;
  }();

  std::cout << "pugs process " << m_rank + 1 << "/" << m_size << std::endl;

  if (m_rank != 0) {
    // LCOV_EXCL_START
    std::cout.setstate(std::ios::badbit);
    std::cerr.setstate(std::ios::badbit);
    std::clog.setstate(std::ios::badbit);
    // LCOV_EXCL_STOP
  }
#endif   // PUGS_HAS_MPI
}

Messenger::~Messenger()
{
#ifdef PUGS_HAS_MPI
  MPI_Finalize();
#endif   // PUGS_HAS_MPI
}

void
Messenger::barrier() const
{
#ifdef PUGS_HAS_MPI
  MPI_Barrier(m_comm_world_pugs);
#endif   // PUGS_HAS_MPI
}

}   // namespace parallel