/****************************************************************************
**  SIONLIB     http://www.fz-juelich.de/jsc/sionlib                       **
*****************************************************************************
**  Copyright (c) 2008-2018                                                **
**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/

#define _XOPEN_SOURCE 700

#include <ctype.h>
#include <inttypes.h>
#include <math.h>
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "sion.h"

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

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

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

  /* -------------------------------------------------------- */
  /* TEST B: write one small file with some different pattern */
  /* -------------------------------------------------------- */
  if (1) {
#define BUFSIZE 1000
#define CHUNKSIZE 100
    int64_t chunksize = CHUNKSIZE;
    int32_t fsblksize = 10;
    int globalrank = rank;
    int numfiles = 1;
    char *newfname = NULL;
    int sid;
    MPI_Comm lcomm;
    char buffer[BUFSIZE];
    int64_t bytes_written = -1;
    long sum;
    size_t bwrote;
    int i;

    for (i = 0; i < BUFSIZE; i++) {
      buffer[i] = 'A' + rank;
    }

    sid = sion_paropen_mpi(
      "testB.out", "bw", &numfiles, MPI_COMM_WORLD, &lcomm, &chunksize, &fsblksize, &globalrank, NULL, &newfname);
    if (sid >= 0) {
      bwrote = sion_fwrite(buffer, 1, CHUNKSIZE, sid);

      bytes_written = sion_get_bytes_written(sid);
      printf("on rank %d: bytes_written=%" PRId64 "\n", rank, bytes_written);
      for (i = 0, sum = 0; i < bwrote; i++) {
        sum = sum + buffer[i];
      }
      printf("on rank %d: bwrote=%3d blocksum=%8d \n", rank, (int)bwrote, (int)sum);

      sion_parclose_mpi(sid);
    } else {
      fprintf(stderr, "on rank %d: error sid = %d\n", rank, sid);
    }
  }

  fflush(stderr);
  fflush(stdout);
  MPI_Barrier(MPI_COMM_WORLD);
  printf("on rank %d: END of TEST B write\n", rank);
  MPI_Barrier(MPI_COMM_WORLD);

  /* ------------------------------------------------------ */
  /* TEST B: read small file with some different pattern    */
  /* ----------------------------------------------------- */
  if (1) {
#define BUFSIZE 1000
#define CHUNKSIZE 100
    int64_t chunksize = CHUNKSIZE;
    int32_t fsblksize = 10;
    int globalrank = rank;
    int numfiles = 1;
    char *newfname = NULL;
    int sid, new_sid;
    MPI_Comm lcomm;
    char buffer[BUFSIZE];
    int64_t bytes_avail = -1;
    size_t bytestoread, bread;
    long sum;
    int i, b;

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

    sid = sion_paropen_mpi(
      "testB.out", "br", &numfiles, MPI_COMM_WORLD, &lcomm, &chunksize, &fsblksize, &globalrank, NULL, &newfname);
    printf("on rank %d: sid           =%d\n", rank, sid);
    printf("on rank %d: numfiles      =%d\n", rank, numfiles);
    printf("on rank %d: chunksize     =%d\n", rank, (int)chunksize);
    printf("on rank %d: globalrank    =%d\n", rank, globalrank);
    printf("on rank %d: newfname      =%s\n", rank, newfname);

    if (sid >= 0) {
      new_sid = sion_dup(sid, -1);
      printf("on rank %d: new_sid       =%d\n", rank, new_sid);

      if (new_sid >= 0) {
        b = 0;
        while ((!sion_feof(new_sid))) {
          bytes_avail = sion_bytes_avail_in_block(new_sid);
          printf("on rank %d: block=%d           bytes_avail=%d\n", rank, b, (int)bytes_avail);
          bytestoread = bytes_avail;
          bread = sion_fread(buffer, 1, bytestoread, new_sid);
          for (i = 0, sum = 0; i < bytestoread; i++) {
            sum = sum + buffer[i];
          }
          printf("on rank %d: block=%d           bread=%3d blocksum=%8d \n", rank, b, (int)bread, (int)sum);
          b++;
        }
        printf("on rank %d: dedup(%d)\n", rank, new_sid);
        sion_dedup(new_sid);
      }
      sion_parclose_mpi(sid);
    } else {
      fprintf(stderr, "on rank %d: error sid = %d\n", rank, sid);
    }
  }

  fflush(stderr);
  fflush(stdout);
  MPI_Barrier(MPI_COMM_WORLD);
  printf("on rank %d: END of TEST B read collective\n", rank);
  MPI_Barrier(MPI_COMM_WORLD);

  /* -------------------------------------------------------- */
  /* TEST C: write multiple files with some different pattern */
  /* -------------------------------------------------------- */
  {
#define BUFSIZE 1000
#define CHUNKSIZE 100
    int64_t chunksize = CHUNKSIZE;
    int32_t fsblksize = 10;
    int globalrank = rank;
    int numfiles = 2;
    char *newfname = NULL;
    int sid;
    MPI_Comm lcomm;
    char buffer[BUFSIZE];
    int64_t bytes_written = -1;
    long sum;
    size_t bwrote;
    int i;

    for (i = 0; i < BUFSIZE; i++) {
      buffer[i] = 'A' + rank;
    }

    sid = sion_paropen_mpi(
      "testC.out", "bw", &numfiles, MPI_COMM_WORLD, &lcomm, &chunksize, &fsblksize, &globalrank, NULL, &newfname);
    if (sid >= 0) {
      bwrote = sion_fwrite(buffer, 1, CHUNKSIZE, sid);

      bytes_written = sion_get_bytes_written(sid);
      printf("on rank %d: bytes_written=%" PRId64 "\n", rank, bytes_written);
      for (i = 0, sum = 0; i < bwrote; i++) {
        sum = sum + buffer[i];
      }
      printf("on rank %d: bwrote=%3d blocksum=%8d \n", rank, (int)bwrote, (int)sum);

      sion_parclose_mpi(sid);
    } else {
      fprintf(stderr, "on rank %d: error sid = %d\n", rank, sid);
    }
  }

  fflush(stderr);
  fflush(stdout);
  MPI_Barrier(MPI_COMM_WORLD);
  printf("on rank %d: END of TEST C write\n", rank);
  MPI_Barrier(MPI_COMM_WORLD);

  /* ------------------------------------------------------ */
  /* TEST C: read small file with some different pattern    */
  /* ------------------------------------------------------ */
  {
#define BUFSIZE 1000
#define CHUNKSIZE 100
    int64_t chunksize = CHUNKSIZE;
    int32_t fsblksize = 10;
    int globalrank = rank;
    int numfiles = 1;
    char *newfname = NULL;
    int sid, new_sid;
    MPI_Comm lcomm;
    char buffer[BUFSIZE];
    int64_t bytes_avail = -1;
    size_t bytestoread, bread;
    long sum;
    int i, b;

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

    sid = sion_paropen_mpi(
      "testC.out", "br", &numfiles, MPI_COMM_WORLD, &lcomm, &chunksize, &fsblksize, &globalrank, NULL, &newfname);
    printf("on rank %d: sid           =%d\n", rank, sid);
    printf("on rank %d: numfiles      =%d\n", rank, numfiles);
    printf("on rank %d: chunksize     =%d\n", rank, (int)chunksize);
    printf("on rank %d: globalrank    =%d\n", rank, globalrank);
    printf("on rank %d: newfname      =%s\n", rank, newfname);

    if (sid >= 0) {
      new_sid = sion_dup(sid, SION_DUP_ALL, 0, 0);
      printf("on rank %d: new_sid       =%d\n", rank, new_sid);

      if (new_sid >= 0) {
        b = 0;
        while ((!sion_feof(new_sid))) {
          bytes_avail = sion_bytes_avail_in_block(new_sid);
          printf("on rank %d: block=%d           bytes_avail=%d\n", rank, b, (int)bytes_avail);
          bytestoread = bytes_avail;
          bread = sion_fread(buffer, 1, bytestoread, new_sid);
          for (i = 0, sum = 0; i < bytestoread; i++) {
            sum = sum + buffer[i];
          }
          printf("on rank %d: block=%d           bread=%3d blocksum=%8d \n", rank, b, (int)bread, (int)sum);
          b++;
        }
        printf("on rank %d: dedup(%d)\n", rank, new_sid);
        sion_dedup(new_sid);
      }
      sion_parclose_mpi(sid);
    } else {
      fprintf(stderr, "on rank %d: error sid = %d\n", rank, sid);
    }
  }

  fflush(stderr);
  fflush(stdout);
  MPI_Barrier(MPI_COMM_WORLD);
  printf("on rank %d: END of TEST C read collective\n", rank);
  MPI_Barrier(MPI_COMM_WORLD);

  /* -------------------------- */
  /* EPILOG */
  /* -------------------------- */
  MPI_Finalize();

  return 0;
}
