#ifndef _MPI_INCLUDE
#include <mpi.h>
#endif

#include "plutils.h"

#define IS_MPI_TYPED					0
#define IS_MPI_MANUAL_PACK				1
#define IS_MPI_TYPED_PERSISTENT			2
#define IS_MPI_MANUAL_PACK_PERSISTENT	3

// ParLib v1.8 initialization
// -------------------------------------------------------------------------- //
void ParLib_init();
void ParLib_deinit();
// -------------------------- //

typedef struct BExchange {
	int overlap[2], send[2], recv[2];
	int sendproc[2], recvproc[2], sbind[2], rbind[2];
	MPI_Datatype btype[2];

	MPI_Comm comm;
	MPI_Request req[4];
	MPI_Aint fsize;

	// manual packing data
	// --------------------------------- //
	int ndims;
	int stride[MAX_PARLIB_MP_DIMS];

	int msize[2];
	int mdims[2][MAX_PARLIB_MP_DIMS];

	void *sbuf[2], *rbuf[2];
	// --------------------------------- //

	// memory management
	// --------------------------------- //
	int buf_id[4];
	// --------------------------------- //
} BExchange;

// BExchange list [declaration]
// -------------------------------------------------------------------------- //
#define MAX_BEXCH_HANDLES			1024

extern BExchange* bexch_hlist[MAX_BEXCH_HANDLES];	// list of BExchange handles
extern int bexch_hmode[MAX_BEXCH_HANDLES];			// Bexchange mode for each handle
extern int bexch_hptr;								// pointer to available BExchange handle
// -------------------------------------------------------------------------- //
// BExchange handle list interface
// -------------------------------------------------------------------------- //
int save_bexch_handle(BExchange *bexchange, int exch_mode);
void get_bexch_handle(BExchange** bexchange, int* exch_mode, int exch_id);
void remove_bexch_handle(int exch_id);
// -------------------------------------------------------------------------- //


// -------------------------------------------------------------------------- //

// -------------------------------------------------------------------------- //
// v.2.2: general exchange init call
//		: includes periodic only case by using additional parameter
int P_BExchange_ginit(int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, int, BExchange*);

int P_BExchange_init ( int, int*, int*, int, int*, MPI_Datatype, 
	MPI_Comm, int, BExchange* );
int P_BExchange_start ( void*, BExchange* );
int P_BExchange_end ( BExchange* );
int P_BExchange_free ( BExchange* );
int P_BExchange ( void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int );

// v.2.2: periodicity conditions only
int P_BExchange_period(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm);
// -------------------------------------------------------------------------- //

// v.1.3 - persistent exchanges //
// -------------------------------------------------------------------------- //
// v.2.2: general exchange init call
//		: includes periodic only case by using additional parameter
int PST_BExchange_ginit(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, int, BExchange*);

int PST_BExchange_init(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, BExchange*);
int PST_BExchange_start(BExchange*);
int PST_BExchange_end(BExchange*);
int PST_BExchange_free(BExchange*);
int PST_BExchange(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int);

// v.2.2: periodicity conditions only
int PST_BExchange_period(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm);
// -------------------------------------------------------------------------- //

// v.1.4 - manual packing //
// -------------------------------------------------------------------------- //
// v.2.2: general exchange init call
//		: includes periodic only case by using additional parameter
int P_BExchange_mp_ginit(int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, int, BExchange*);

int P_BExchange_mp_init(int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, BExchange*);
int P_BExchange_mp_start(void*, BExchange*);
int P_BExchange_mp_end(void*, BExchange*);
int P_BExchange_mp_free(BExchange*);
int P_BExchange_mp(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int);

// v.2.2: periodicity conditions only
int P_BExchange_period_mp(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm);
// -------------------------------------------------------------------------- //

// v.1.4 - persistent exchanges for manual packing //
// -------------------------------------------------------------------------- //
// v.2.2: general exchange init call
//		: includes periodic only case by using additional parameter
int PST_BExchange_mp_ginit(int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, int, BExchange*);

int PST_BExchange_mp_init(int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, BExchange*);
int PST_BExchange_mp_start(void*, BExchange*);
int PST_BExchange_mp_end(void*, BExchange*);
int PST_BExchange_mp_free(BExchange*);
int PST_BExchange_mp(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int);

// v.2.2: periodicity conditions only
int PST_BExchange_period_mp(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm);
// -------------------------------------------------------------------------- //


// v.1.95 - choice subroutines //
// -------------------------------------------------------------------------- //
int P_BExchange_opt_init(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, BExchange*, int);
int P_BExchange_opt_start(void*, BExchange*, int);
int P_BExchange_opt_end(void*, BExchange*, int);
int P_BExchange_opt_free(BExchange*, int);
int P_BExchange_opt(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int, int);

// v.2.2: periodicity conditions only
int P_BExchange_period_opt(void*, int, int*, int*, int, int*, MPI_Datatype,
	MPI_Comm, int);
// -------------------------------------------------------------------------- //


typedef struct Transposition {
	void *psrc, *pdest;		// used only in persistent-type communications

	MPI_Datatype *stype, *rtype;
	int *sbeg, *rbeg;
	MPI_Comm comm;

	int nproc, iproc;
	MPI_Request *req;
	MPI_Aint fsize;

	// manual packing data
	// --------------------------------- //
	int ndims;
	int sstride[MAX_PARLIB_MP_DIMS], rstride[MAX_PARLIB_MP_DIMS];

	int **sdims, **rdims;
	int *ssize, *rsize;

	void **sbuf, **rbuf;		// [nproc] buffers
	// --------------------------------- //

	// memory management
	// --------------------------------- //
	int *mem_dims;					// [nproc * ndims] pool
	void *mem_sbuf, *mem_rbuf;		// [nproc * sum(msize)] pools

	int buf_id[9];
	// --------------------------------- //
} Transposition;

// Transpose list [declaration]
// -------------------------------------------------------------------------- //
#define MAX_TRANSP_HANDLES			128

extern Transposition* transp_hlist[MAX_TRANSP_HANDLES];	// list of Transpose handles
extern int transp_hmode[MAX_TRANSP_HANDLES];			// Transpose mode for each handle
extern int transp_hptr;									// pointer to available Transpose handle
// -------------------------------------------------------------------------- //
// Transposition handle list interface
// -------------------------------------------------------------------------- //
int save_transp_handle(Transposition *transp, int exch_mode);
void get_transp_handle(Transposition** transp, int* exch_mode, int exch_id);
void remove_transp_handle(int exch_id);
// -------------------------------------------------------------------------- //

// -------------------------------------------------------------------------- //
int P_Transpose_init ( int , int, int*, int, int*, int*, int*, int*, 
	MPI_Datatype, MPI_Comm, int, Transposition* );
int P_Transpose_start ( void*, void*, Transposition* );
int P_Transpose_end ( Transposition* );
int P_Transpose_free ( Transposition* );
int P_Transpose ( int, void*, int, int*, void*, int, int*, int*, int*, 
	int*, MPI_Datatype, MPI_Comm, int );
// -------------------------------------------------------------------------- //

// v.1.95 - persistent exchanges //
// -------------------------------------------------------------------------- //
int PST_Transpose_init(int, void*, int, int*, void*, int, int*, int*, int*, int*,
	MPI_Datatype, MPI_Comm, int, Transposition*);
int PST_Transpose_start(Transposition*);
int PST_Transpose_end(Transposition*);
int PST_Transpose_free(Transposition*);
int PST_Transpose(int, void*, int, int*, void*, int, int*, int*, int*,
	int*, MPI_Datatype, MPI_Comm, int);
// -------------------------------------------------------------------------- //

// v.1.7 - manual packing //
// -------------------------------------------------------------------------- //
int P_Transpose_mp_init(int, int, int*, int, int*, int*, int*, int*,
	MPI_Datatype, MPI_Comm, int, Transposition*);
int P_Transpose_mp_start(void*, void*, Transposition*);
int P_Transpose_mp_end(void*, void*, Transposition*);
int P_Transpose_mp_free(Transposition*);
int P_Transpose_mp(int, void*, int, int*, void*, int, int*, int*, int*,
	int*, MPI_Datatype, MPI_Comm, int);
// -------------------------------------------------------------------------- //

// v.1.95 - persistent exchanges for manual packing //
// -------------------------------------------------------------------------- //
int PST_Transpose_mp_init(int, void*, int, int*, void*, int, int*, int*, int*, int*,
	MPI_Datatype, MPI_Comm, int, Transposition*);
int PST_Transpose_mp_start(void*, void*, Transposition*);
int PST_Transpose_mp_end(void*, void*, Transposition*);
int PST_Transpose_mp_free(Transposition*);
int PST_Transpose_mp(int, void*, int, int*, void*, int, int*, int*, int*,
	int*, MPI_Datatype, MPI_Comm, int);
// -------------------------------------------------------------------------- //


// v.1.95 - choice subroutines //
// -------------------------------------------------------------------------- //
int P_Transpose_opt_init(int, void*, int, int*, void*, int, int*, int*, int*, int*,
	MPI_Datatype, MPI_Comm, int, Transposition*, int);
int P_Transpose_opt_start(void*, void*, Transposition*, int);
int P_Transpose_opt_end(void*, void*, Transposition*, int);
int P_Transpose_opt_free(Transposition*, int);
int P_Transpose_opt(int, void*, int, int*, void*, int, int*, int*, int*,
	int*, MPI_Datatype, MPI_Comm, int, int);
// -------------------------------------------------------------------------- //