#include "common.h"
#include "mpi.h"
#include <time.h>

#define FREE_NOT_NULL(x) if(x){free(x); x = NULL;}

void get_context(global_context_t* ctx)
{
	MPI_Comm_size(ctx -> mpi_communicator, &(ctx -> world_size));
	MPI_Get_processor_name(ctx -> processor_mame, &(ctx -> __processor_name_len));
	MPI_Comm_rank(ctx -> mpi_communicator, &(ctx -> mpi_rank));
	ctx -> local_data = NULL;
	ctx -> lb_box 	  = NULL;
	ctx -> ub_box 	  = NULL;
    ctx -> rank_n_points  = (int*)malloc(ctx -> world_size * sizeof(int));
    ctx -> rank_idx_start = (int*)malloc(ctx -> world_size * sizeof(int));
    ctx -> idx_halo_points_recv = NULL;
    ctx -> idx_halo_points_send = NULL;
    ctx -> n_halo_points_recv = NULL;
    ctx -> n_halo_points_send = NULL;
    ctx -> halo_datapoints  = NULL;
    ctx -> local_datapoints = NULL;
    ctx -> __recv_heap_buffers = NULL;
}

void free_context(global_context_t* ctx)
{

    FREE_NOT_NULL(ctx -> local_data);
    FREE_NOT_NULL(ctx -> ub_box);
    FREE_NOT_NULL(ctx -> lb_box);

    if(ctx -> local_datapoints)
    {
        for(int i = 0; i < ctx -> local_n_points; ++i) FREE_NOT_NULL(ctx -> local_datapoints[i].ngbh.data);
    }

    FREE_NOT_NULL(ctx -> local_datapoints);
    if(ctx -> halo_datapoints)
    {
        for(int i = 0; i < ctx -> world_size; ++i) 
        {
            /*
            for(int j = 0; j < ctx -> n_halo_points_recv[i]; ++j)
            {
                FREE_NOT_NULL(ctx -> halo_datapoints[i][j].ngbh.data);
            }
            */
            FREE_NOT_NULL(ctx -> halo_datapoints[i]);
        }
    }
    FREE_NOT_NULL(ctx -> halo_datapoints);
    FREE_NOT_NULL(ctx -> __recv_heap_buffers);

    if(ctx -> idx_halo_points_recv)
    {
        for(int i = 0; i < ctx -> world_size; ++i) FREE_NOT_NULL(ctx -> idx_halo_points_recv[i]);
    }
    FREE_NOT_NULL(ctx -> idx_halo_points_recv);

    if(ctx -> idx_halo_points_send)
    {
        for(int i = 0; i < ctx -> world_size; ++i) FREE_NOT_NULL(ctx -> idx_halo_points_send[i]);
    }
    FREE_NOT_NULL(ctx -> idx_halo_points_send);
    FREE_NOT_NULL(ctx -> n_halo_points_recv);
    FREE_NOT_NULL(ctx -> n_halo_points_send);
    FREE_NOT_NULL(ctx -> rank_n_points);
    FREE_NOT_NULL(ctx -> rank_idx_start);
}

void free_pointset(pointset_t* ps)
{
	if(ps -> data) 
	{
		free(ps -> data);
		ps -> data = NULL;
	}

	if(ps -> ub_box)
	{
		free(ps -> ub_box);
		ps -> ub_box = NULL;	
	}

	if(ps -> lb_box)
	{
		free(ps -> lb_box);
		ps -> lb_box = NULL;	
	}
}


void mpi_printf(global_context_t* ctx, const char *fmt, ...)
{
	if(ctx -> mpi_rank == 0)
	{
		va_list l;
		va_start(l, fmt);
//		printf("[MASTER]: ");
		vprintf(fmt, l);
		//        myflush(stdout);
		va_end(l);
	}
}

void generate_random_matrix(
		float_t** data,
		int dimensions,
		size_t n,
		global_context_t* ctx)
{
	/* seed the random number generator */
	srand((unsigned)time(NULL) + ctx -> mpi_rank * ctx -> world_size + ctx -> __processor_name_len);

	//size_t n = rand() % (nmax - nmin) + nmin;
	float_t* local_data = (float_t*)malloc(dimensions*n*sizeof(float_t));
	for(size_t i = 0; i < dimensions*n; ++i) local_data[i] = (float_t)rand()/(float_t)RAND_MAX;
	*data = local_data;
	
	ctx -> dims = dimensions;
	ctx -> local_n_points = n;

	return;
}

