#include <mpi.h>
#include <vector>
#include <iostream>
#include <cstdlib>
#include "parallelization/MpiSimulation.hpp"
#include "parallelization/MpiBodyComm.hpp"
#include "datastructures/Body.hpp"

typedef struct _A {
	double a[3];
	double b[3];
	double leap[3];
	double c;
	double d[2];
} A;

using namespace nbody;
using namespace std;

int main(int argc, char** argv) {
	Body b[2];
	int blocklengths[7] = {1, 3, 3, 3, 1, 1, 1};
	MPI::Datatype datatypes[7] = {MPI::LB, MPI::DOUBLE, MPI::DOUBLE, MPI::DOUBLE, MPI::DOUBLE, MPI::BOOL, MPI::UB};
	MPI::Aint displacements[7];
	MPI::Datatype btype = MPI::DATATYPE_NULL;

	MPI::Init(argc, argv);
	displacements[0] = MPI::Get_address(&(b[0])) - MPI::Get_address(&(b[0]));
	displacements[1] = MPI::Get_address(&(b[0].position[0])) - MPI::Get_address(&(b[0]));
	displacements[2] = MPI::Get_address(&(b[0].velocity[0])) - MPI::Get_address(&(b[0]));
	displacements[3] = MPI::Get_address(&(b[0].acceleration[0])) - MPI::Get_address(&(b[0]));
	displacements[4] = MPI::Get_address(&(b[0].mass)) - MPI::Get_address(&(b[0]));
	displacements[5] = MPI::Get_address(&(b[0].refinement)) - MPI::Get_address(&(b[0]));
	displacements[6] = MPI::Get_address(&(b[1])) - MPI::Get_address(&(b[0]));
	btype = btype.Create_struct(7, blocklengths, displacements, datatypes);
	btype.Commit();
	srandom(1234);
	if (MPI::COMM_WORLD.Get_rank() == 0) {
		b[0].mass = (float) random() / (float) random();
		for (int i = 0; i < 3; i++) {
			b[0].position[i] = (float) random() / (float) random();
			b[1].position[i] = (float) random() / (float) random();
			b[0].acceleration[i] = (float) random() / (float) random();
			b[1].acceleration[i] = (float) random() / (float) random();
			b[0].velocity[i] = (float) random() / (float) random();
			b[1].velocity[i] = (float) random() / (float) random();
			b[0].refinement = ((random() % 2) != 0);
			b[1].refinement = ((random() % 2) != 0);
		}

		b[0].print();
		b[1].print();
		cout << "---" << endl;
		MPI::Request request;

		MPI::COMM_WORLD.Send(b, 2, btype, 1, 0);
		request = MPI::COMM_WORLD.Isend(b, 2, btype, 1, 0);
		request.Wait();
	} else if (MPI::COMM_WORLD.Get_rank() == 1) {
		MPI::COMM_WORLD.Recv(b, 2, btype, 0, 0);
		MPI::COMM_WORLD.Recv(b, 2, btype, 0, 0);
		b[0].print();
		b[1].print();
	}
	btype.Free();
	//MpiSimulation simulation(argc, argv);
	//MpiBodyComm comm(simulation.getDatatype());

	MPI::Finalize();
	return 0;

}
