#ifndef MPI_SIMULATION_HPP
#define MPI_SIMULATION_HPP


#include <mpi.h>
#include <Simulation.hpp>
#include <BarnesHutTree.hpp>
#include <cstddef>
#include <memory>
#include <vector>

namespace nbody {
	struct SendStore {
		std::vector<Body> bodies;
		MPI_Request request{ MPI_REQUEST_NULL };
		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();
		virtual void send(std::vector<Body>&& bodies, int target);
		virtual int recv(std::vector<Body>& bodies, int source);
	public:
		MpiSimulation(const std::string& inputFile);
		virtual ~MpiSimulation();
		virtual std::size_t getNumberOfProcesses() const;
		virtual std::size_t getProcessId() const;
		virtual bool stateCorrect();
		virtual void distributeBodies();
		virtual void distributeDomains(const std::vector<Body>& localBodies);
		virtual void distributeDomains(const Box& localDomain);
		virtual void distributeDomains();
		virtual void distributeLETs();
		virtual void rebuildTree();
		virtual void buildTree();
		virtual void runStep();
		virtual MPI_Datatype* getDatatype();
	};
} // namespace nbody

#endif

