#ifndef MPI_SIMULATION_HPP
#define MPI_SIMULATION_HPP

#include <cstddef>
#include <memory>
#include <mpi.h>
#include <vector>

#include "Simulation.hpp"

namespace nbody {
	struct SendStore {
		std::vector<Body> bodies;
		MPI_Request request{ MPI_REQUEST_NULL };
		explicit SendStore(std::vector<Body> b):bodies(std::move(b)) {}
	};

	//MPI simulation
	class MpiSimulation : public Simulation {
	protected:
		MPI_Datatype vec3Type{MPI_DATATYPE_NULL};
		MPI_Datatype bodyType{MPI_DATATYPE_NULL};
		MPI_Datatype boxType{MPI_DATATYPE_NULL};
		std::vector<Box> domains;
		Box overallDomain;
		std::vector<SendStore> sendStores;

		void flushSendStore();
		void send(std::vector<Body> bodies, int target);
		int recv(std::vector<Body>& bodies, int source);
	public:
		explicit MpiSimulation(const std::string& inputFile);
		~MpiSimulation() override;
		std::size_t getProcessId() const override;
		void distributeBodies();
		void distributeDomains(const std::vector<Body>& localBodies);
		void distributeDomains(Box localDomain);
		void distributeDomains();
		void distributeLETs();
		void rebuildTree();
		void buildTree();
		void runStep() override;
	};
} // namespace nbody

#endif

