#pragma once

#include <cassert>
#include <mpi.h>

class MpiEnvironment { // wrapper for creating and destroying the environment
	int _worldRank{-1};
	int _worldSize{-1};
	bool _isMaster{false};

  public:
	int worldRank() const { return _worldRank; }
	int worldSize() const { return _worldSize; }
	bool isMaster() const { return _isMaster; }

	friend void swap(MpiEnvironment& first, MpiEnvironment& second) noexcept {
		using std::swap;
		swap(first._worldRank, second._worldRank);
		swap(first._worldSize, second._worldSize);
		swap(first._isMaster, second._isMaster);
	}

	MpiEnvironment(int& argc, char* argv[], int required_thread_support) {
		int provided_thread_support;
		MPI_Init_thread(&argc, &argv, required_thread_support, &provided_thread_support);
		assert(required_thread_support == provided_thread_support);

		MPI_Comm_rank(MPI_COMM_WORLD, &_worldRank);
		MPI_Comm_size(MPI_COMM_WORLD, &_worldSize);
		_isMaster = {_worldRank == 0};
	}
	~MpiEnvironment() {
		if (_worldRank != -1) { MPI_Finalize(); }
	}
	MpiEnvironment(MpiEnvironment&) = delete;
	MpiEnvironment& operator=(MpiEnvironment&) = delete;
	MpiEnvironment(MpiEnvironment&& other) noexcept { swap(*this, other); }
	MpiEnvironment& operator=(MpiEnvironment&& other) noexcept {
		swap(*this, other);
		return *this;
	}
};
