/*
    Copyright (C) 2012 Bartosz Broda, Jacek Bochenek, Łukasz Biłeńkij, 
    Damian Jaworski, Wojciech Mazur, Dominik Piasecki, Maciej Piasecki, 
    Radosław Ramocki
    
    This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your option)
any later version.

    This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. 

    See the LICENSE and COPYING files for more details.
*/

#ifndef MPI_HANDLER_H
#define MPI_HANDLER_H
#include <mpi.h>
#include <vector>
#include <unistd.h>


namespace parallel
{
	class MPI_Handler
	{
	public:

		template<class T> void sendItem(T item,MPI_Datatype dataType,int targetNode)
		{
			MPI_Send(&item,1,dataType,targetNode,0,MPI_COMM_WORLD);
		}
		template<class T> void sendArray(T* itemArray,int length,MPI_Datatype dataType,int targetNode)
		{
			MPI_Send(&length,1,MPI_INT,targetNode,0,MPI_COMM_WORLD);
			MPI_Send(itemArray,length,dataType,targetNode,0,MPI_COMM_WORLD);
		}
		template<class T> void sendVector(std::vector<T> itemVector,MPI_Datatype dataType,int targetNode)
		{
			int length = itemVector.size();
			T *itemArray = new T[length];
			for(int i=0;i<length;i++)
				itemArray[i]=itemVector[i];
			sendArray(itemArray,length,dataType,targetNode);
			delete itemArray;
		}
		template<class T> int recvItem(T &item,MPI_Datatype dataType,int sourceNode=-1)
		{
			if(sourceNode==-1)
			{
				MPI_Recv(&item,1,dataType,MPI_ANY_SOURCE,0,MPI_COMM_WORLD,&stat);
			}
			else
				MPI_Recv(&item,1,dataType,sourceNode,0,MPI_COMM_WORLD,&stat);
			return stat.MPI_SOURCE;
		}


		template<class T> int recvArray(T* &itemArray,int &length,MPI_Datatype dataType,int sourceNode=-1)
		{
			if(sourceNode==-1)
				MPI_Recv(&length,1,MPI_INT,MPI_ANY_SOURCE,0,MPI_COMM_WORLD,&stat);
			else
				MPI_Recv(&length,1,MPI_INT,sourceNode,0,MPI_COMM_WORLD,&stat);
			int source=stat.MPI_SOURCE;
			if(itemArray!=NULL)
				delete itemArray;
			itemArray = new T[length];
			MPI_Recv(itemArray,length,dataType,source,0,MPI_COMM_WORLD,&stat);
			return source;
		}

		template<class T> int recvVector(std::vector<T> &itemVector,MPI_Datatype dataType,int sourceNode=-1)
		{
			T* itemArray;
			int length;
			int source=recvArray(itemArray,length,dataType,sourceNode);
			for(int i=0;i<length;i++)
				itemVector.push_back(itemArray[i]);
			return source;
		}

		bool probeForMessage(int source=-1);
		MPI_Handler();
	private:
		MPI_Status stat;
	};
}
#endif // MPI_HANDLER_H
