/****************************************************************************
**  SIONLIB     http://www.fz-juelich.de/jsc/sionlib                       **
*****************************************************************************
**  Copyright (c) 2008-2019                                                **
**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/
/*
 * test_hyb.c
 *
 *  Created on: Jun 29, 2010
 *      Author: dmontoya
 */

#define _XOPEN_SOURCE 700

#include <ctype.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <math.h>
#include <time.h>

#ifdef SION_OMPI
#include <mpi.h>
#include <omp.h>
#endif

#include "sion.h"

int main(int argc, char **argv)
{
#ifdef SION_OMPI
  int rank, size;

  /* -------------------------- */
  /* PROLOG */
  /* -------------------------- */

  MPI_Init(&argc, &argv);
  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);

  /* -------------------------------------------------------------------------------------------- */
  /* TEST A: write a small file with some different pattern, with collective and individual calls */
  /* -------------------------------------------------------------------------------------------- */

#define BUFSIZE 1000
#define CHUNKSIZE 100

  sion_ompi_options *options = sion_ompi_options_new();
  if (!options) {
    return 1;
  }
  sion_ompi_options_set_chunksize(options, CHUNKSIZE);
  sion_ompi_options_set_fsblksize(options, 16);
  sion_ompi_options_set_multifile_number(options, 3);

#pragma omp parallel
  {
    int threadnum = omp_get_thread_num();

    int sid = sion_paropen_ompi("testB.out", SION_OPEN_WRITE, MPI_COMM_WORLD, options);
    if (sid >= 0) {
      int globalrank = sion_get_globalrank(sid);
      printf("on MPI rank %d, OMP thread %d: globalrank=%d\n", rank, threadnum, globalrank);

      /* test buffer which exact fits */
      char buffer[BUFSIZE];
      for (int i = 0; i < BUFSIZE; i++) {
        buffer[i] = 'A' + globalrank * 3;
      }
      int b = 0;
      int rc = sion_ensure_free_space(sid, CHUNKSIZE);
      fprintf(stderr, "on MPI rank %d, OMP thread %d: ensure_free_space (%d) rc = %d\n", rank, threadnum, CHUNKSIZE, rc);
      size_t bwrote = 0;
      if (rc) {
        bwrote = sion_coll_fwrite(buffer, 1, CHUNKSIZE, sid);
      }
      int64_t bytes_written = sion_get_bytes_written(sid);
      printf("on MPI rank %d, OMP thread %d: bytes_written=%" PRId64 "\n", rank, threadnum, bytes_written);
      long sum = 0;
      for (int i = 0; i < bwrote; i++) {
        sum = sum + buffer[i];
      }
      printf("on MPI rank %d, OMP thread %d: block=%d           bwrote=%3d blocksum=%8d \n", rank, threadnum, b, (int)bwrote,
        (int)sum);
      /* test half size buffer */
      for (int i = 0; i < BUFSIZE; i++) {
        buffer[i] = 'A' + globalrank * 3 + 1;
      }
      b = 1;
      rc = sion_ensure_free_space(sid, CHUNKSIZE / 2);
      fprintf(stderr, "on MPI rank %d, OMP thread %d: ensure_free_space (%d) rc = %d\n", rank, threadnum, CHUNKSIZE / 2, rc);
      if (rc) {
        bwrote = sion_fwrite(buffer, 1, CHUNKSIZE / 2, sid);
      }
      bytes_written = sion_get_bytes_written(sid);
      printf("on MPI rank %d, OMP thread %d: bytes_written=%" PRId64 "\n", rank, threadnum, bytes_written);
      sum = 0;
      for (int i = 0; i < bwrote; i++) {
        sum = sum + buffer[i];
      }
      printf("on MPI rank %d, OMP thread %d: block=%d           bwrote=%3d blocksum=%8d \n", rank, threadnum, b, (int)bwrote,
        (int)sum);
      /* test buffer which exact fits, leaving gap */
      for (int i = 0; i < BUFSIZE; i++) {
        buffer[i] = 'A' + globalrank * 3 + 2;
      }
      b = 2;
      rc = sion_ensure_free_space(sid, CHUNKSIZE);
      fprintf(stderr, "on MPI rank %d, OMP thread %d: ensure_free_space (%d) rc = %d\n", rank, threadnum, CHUNKSIZE, rc);
      if (rc) {
        bwrote = sion_coll_fwrite(buffer, 1, CHUNKSIZE, sid);
      }
      bytes_written = sion_get_bytes_written(sid);
      printf("on MPI rank %d, OMP thread %d: bytes_written=%" PRId64 "\n", rank, threadnum, bytes_written);
      sum = 0;
      for (int i = 0; i < bwrote; i++) {
        sum = sum + buffer[i];
      }
      printf("on MPI rank %d, OMP thread %d: block=%d           bwrote=%3d blocksum=%8d \n", rank, threadnum, b, (int)bwrote,
        (int)sum);
      sion_parclose_ompi(sid);
    } else {
      fprintf(stderr, "on MPI rank %d, OMP thread %d: error sid = %d\n", rank, threadnum, sid);
    }

#pragma omp master
    {
      MPI_Barrier(MPI_COMM_WORLD);
    }
#pragma omp barrier
    printf("on MPI rank %d, OMP thread %d: END of TEST B write\n", rank, threadnum);

  } /* omp parallel */

  fflush(stderr);
  fflush(stdout);

  MPI_Barrier(MPI_COMM_WORLD);

  /* ------------------------------------------------------ */
  /* TEST B: read small file with some different pattern    */
  /* ------------------------------------------------------ */

#pragma omp parallel
  {
    int threadnum = omp_get_thread_num();

    int sid = sion_paropen_ompi("testB.out", SION_OPEN_READ, MPI_COMM_WORLD, NULL);

    if (sid >= 0) {
      int globalrank = sion_get_globalrank(sid);

      if (rank >= 0) {
        printf("on MPI rank %d, OMP thread %d: sid           =%d\n", rank, threadnum, sid);
        printf("on MPI rank %d, OMP thread %d: numfiles      =%d\n", rank, threadnum, sion_get_number_of_files(sid));
        printf("on MPI rank %d, OMP thread %d: chunksize     =%d\n", rank, threadnum, (int)sion_get_chunksize(sid));
        printf("on MPI rank %d, OMP thread %d: globalrank    =%d\n", rank, threadnum, globalrank);
        printf("on MPI rank %d, OMP thread %d: newfname      =%s\n", rank, threadnum, sion_get_fname(sid));
      }

      char buffer[BUFSIZE];
      for (int i = 0; i < BUFSIZE; i++) {
        buffer[i] = 'Q' + globalrank;
      }

      int b = 0;
      while ((!sion_feof(sid))) {
        int64_t bytes_avail = sion_bytes_avail_in_block(sid);
        if (rank >= 0) {
          printf("on MPI rank %d, OMP thread %d: block=%d           bytes_avail=%d\n", rank, threadnum, b, (int)bytes_avail);
        }
        size_t bytestoread = bytes_avail;
        size_t bread = sion_coll_fread(buffer, 1, bytestoread, sid);
        long sum = 0;
        for (int i = 0; i < bytestoread; i++) {
          sum = sum + buffer[i];
        }
        if (rank >= 0) {
          printf("on MPI rank %d, OMP thread %d: block=%d           bread=%3d blocksum=%8d \n", rank, threadnum, b, (int)bread,
            (int)sum);
        }
        b++;
      }

      sion_parclose_ompi(sid);
    } else {
      fprintf(stderr, "on MPI rank %d, OMP thread %d: error sid = %d\n", rank, threadnum, sid);
    }

#pragma omp master
    {
      MPI_Barrier(MPI_COMM_WORLD);
    }
#pragma omp barrier
    if (rank == 0) {
      printf("on MPI rank %d, OMP thread %d: END of TEST B read collective\n", rank, threadnum);
    }
  } /* omp parallel */

  sion_ompi_options_delete(options);

  MPI_Finalize();
#endif
  return 0;
}
