/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright (C) CNRS, INRIA, Université Bordeaux 1, Télécom SudParis
 * See COPYING in top-level directory.
 */


/* This program tests the eztrace_start/eztrace_stop fonctions that permit to
 * record events during a small part of the application.
 * to compile it, run:
 * $ mpicc -o mpi_eztrace_start mpi_eztrace_start.c -I$EZTRACE_ROOT/include -L$EZTRACE_ROOT/lib -leztrace -leztrace-mpi -lmpich
 * then run it with:
 * $ mpirun  -np 2 ./mpi_eztrace_start
 * The generated trace should contain only the final MPI_Barrier
 */
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <mpi.h>
#include <stdarg.h>
#include <eztrace.h>

// Debugging part, print out only if debugging level of the system is verbose or more
int _debug = -77;

void debug(const char *fmt, ...) {
  if (_debug == -77) {
    char *buf = getenv("EZTRACE_DEBUG");
    if (buf == NULL)
      _debug = 0;
    else
      _debug = atoi(buf);
  }
  if (_debug >= 0) { // debug verbose mode
    va_list va;
    va_start(va, fmt);
    vfprintf(stdout, fmt, va);
    va_end(va);
  }
}
// end of debugging part

#define LEN      1024
#define LOOPS    10

static int comm_rank = -1;
static int comm_size = -1;
static char host_name[1024] = "";

static unsigned char *main_buffer = NULL;

/* fill the buffer */
void fill_buffer(unsigned char* buffer, int buffer_size) {
  int i;
  for (i = 0; i < buffer_size; i++)
    buffer[i] = 'a' + (i % 26);
}

/* fill the buffer with 0 */
void zero_buffer(unsigned char* buffer, int buffer_size) {
  int i;
  for (i = 0; i < buffer_size; i++)
    buffer[i] = 0;
}

/* check wether the buffer contains errornous data
 * return 1 if it contains an error
 */
int check_buffer(unsigned char* buffer, int buffer_size) {
  int i;
  for (i = 0; i < buffer_size; i++)
    if (buffer[i] != 'a' + (i % 26))
      return 1;
  return 0;
}

void compute(unsigned usec) {
  double t1, t2;
  t1 = MPI_Wtime();
  do {
    t2 = MPI_Wtime();
  } while ((t2 - t1) * 1e6 < usec);
}

int main(int argc, char **argv) {
  int ping_side;
  int rank_dst;
  int start_len = LEN;
  int iterations = LOOPS;

  MPI_Init(&argc, &argv);

  printf("MPI init done !\n");

  MPI_Comm_size(MPI_COMM_WORLD, &comm_size);
  MPI_Comm_rank(MPI_COMM_WORLD, &comm_rank);

  if (gethostname(host_name, 1023) < 0) {
    perror("gethostname");
    exit(1);
  }

  if (comm_size < 2) {
    fprintf(stderr,
            "This program requires at least 2 MPI processes, aborting...\n");
    goto out;
  }

  debug("(%s): My rank is %d\n", host_name, comm_rank);

  ping_side = !(comm_rank & 1);

  main_buffer = malloc(start_len);
  fill_buffer(main_buffer, start_len);

  int size = 1024;
  int i;
  MPI_Barrier(MPI_COMM_WORLD);
  for (i = 0; i < iterations; i++) {
    if (!comm_rank) {
      MPI_Send(main_buffer, size, MPI_CHAR, (comm_rank + 1) % comm_size, 0,
               MPI_COMM_WORLD);
      MPI_Recv(main_buffer, size, MPI_CHAR,
               (comm_rank + comm_size - 1) % comm_size, 0, MPI_COMM_WORLD,
               MPI_STATUS_IGNORE);
    } else {
      MPI_Recv(main_buffer, size, MPI_CHAR,
               (comm_rank + comm_size - 1) % comm_size, 0, MPI_COMM_WORLD,
               MPI_STATUS_IGNORE);
      compute(100);
      MPI_Send(main_buffer, size, MPI_CHAR, (comm_rank + 1) % comm_size, 0,
               MPI_COMM_WORLD);
    }
    MPI_Barrier(MPI_COMM_WORLD);
  }

  eztrace_start();

    MPI_Barrier(MPI_COMM_WORLD);

  eztrace_stop();

  out: free(main_buffer);
  MPI_Finalize();

  return 0;
}
