/****************************************************************************
**  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       **
****************************************************************************/

#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 A: write a empty file and get info */
  /* -------------------------- */
  {
    int sid;
    sion_mpi_options *options = sion_mpi_options_new();
    if (!options)
      return 1;

    sion_mpi_options_set_chunksize(options, 100);
    sion_mpi_options_set_fsblksize(options, 10);

    sid = sion_paropen_mpi("testA.out", SION_OPEN_WRITE, MPI_COMM_WORLD, options);
    sion_mpi_options_delete(options);

    sion_parclose_mpi(sid);
  }

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

  /* -------------------------- */
  /* TEST A: write a small file with different chunks  */
  /* -------------------------- */
  {
    int sid;
    int i;
    char buffer[1000];
    char cbuffer[1000];

    int64_t bytes_written = -1;
    sion_mpi_options *options = sion_mpi_options_new();
    if (!options)
      return 1;

    sion_mpi_options_set_chunksize(options, 1024);
    sion_mpi_options_set_fsblksize(options, 10);

    sid = sion_paropen_mpi("testA.out", SION_OPEN_WRITE, MPI_COMM_WORLD, options);
    sion_mpi_options_delete(options);

    /* write full chunk */
    for (i = 0; i < 1000; i++) {
      buffer[i] = 'A';
    }
    sion_fwrite(buffer, 1, 1000, sid);
    bytes_written = sion_get_bytes_written(sid);
    printf("on rank %d: bytes_written=%" PRId64 "\n", rank, bytes_written);

    /* write small chunk */
    for (i = 0; i < 1000; i++) {
      buffer[i] = 'B';
    }
    strcpy(cbuffer, "This is a small test for seek!");
    sion_fwrite(buffer, 1, 40, sid);
    sion_fwrite(cbuffer, 1, 36, sid);
    sion_fwrite(buffer, 1, 256, sid);
    bytes_written = sion_get_bytes_written(sid);
    printf("on rank %d: bytes_written=%" PRId64 "\n", rank, bytes_written);

    /* write half full chunk */
    for (i = 0; i < 1000; i++) {
      buffer[i] = 'C';
    }
    sion_fwrite(buffer, 1, 512, sid);
    bytes_written = sion_get_bytes_written(sid);
    printf("on rank %d: bytes_written=%" PRId64 "\n", rank, bytes_written);

    /* write small variable size chunk */
    for (i = 0; i < 1000; i++) {
      buffer[i] = 'D';
    }
    sion_fwrite(buffer, 1, 40 + rank * 14, sid);
    bytes_written = sion_get_bytes_written(sid);
    printf("on rank %d: bytes_written=%" PRId64 "\n", rank, bytes_written);

    sion_parclose_mpi(sid);
  }

  fflush(stderr);
  fflush(stdout);
  MPI_Barrier(MPI_COMM_WORLD);
  printf("on rank %d: END of TEST A small file with different chunks\n", rank);
  MPI_Barrier(MPI_COMM_WORLD);

#ifndef DOALL
  /* -------------------------- */
  /* TEST B: read part of file, with sion_seek, position in chunk */
  /* -------------------------- */
  {
    int sid;
    int i;
    char buffer[1000];
    char cbuffer[1000];
    int currentchunknr;
    int64_t currentpos;
    int maxchunks;
    int64_t *chunksizes;

    int64_t bytes_read = -1;

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

    sid = sion_paropen_mpi("testA.out", SION_OPEN_READ, MPI_COMM_WORLD, NULL);

    sion_get_current_location(sid, &currentchunknr, &currentpos, &maxchunks, &chunksizes);
    printf("on rank %d: currentchunknr=%d\n", rank, currentchunknr);
    printf("on rank %d: currentpos    =%" PRId64 "\n", rank, currentpos);
    printf("on rank %d: maxchunks     =%d\n", rank, maxchunks);
    for (i = 0; i < maxchunks; i++) {
      printf("on rank %d: chunksize[%d] = %" PRId64 "\n", rank, i, chunksizes[i]);
    }

    /* read part of first chunk */
    sion_fread(buffer, 1, 200, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 "\n", rank, bytes_read);

    /* goto chunk 1, read part of this chunk */
    for (i = 0; i < 1000; i++) {
      cbuffer[i] = '\0';
    }
    sion_seek_chunk(sid, 1, SION_SEEK_BEGINNING);
    sion_seek(sid, 10, SION_SEEK_CHUNK_BEGINNING);

    sion_fread(cbuffer, 1, 36, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 " read=%s\n", rank, bytes_read, cbuffer);

    /* read a further part of this chunk */
    sion_fread(buffer, 1, 200, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 "\n", rank, bytes_read);

    /* goto chunk 1, read again part of this chunk */
    for (i = 0; i < 1000; i++) {
      cbuffer[i] = '\0';
    }
    sion_seek_chunk(sid, 1, SION_SEEK_BEGINNING);
    sion_seek(sid, 10, SION_SEEK_CHUNK_BEGINNING);

    sion_fread(cbuffer, 1, 36, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 " read=%s\n", rank, bytes_read, cbuffer);

    /* goto chunk 1, read again part of this chunk */
    for (i = 0; i < 1000; i++) {
      cbuffer[i] = '\0';
    }
    sion_seek_chunk(sid, 1, SION_SEEK_BEGINNING);
    sion_seek(sid, 20, SION_SEEK_CHUNK_BEGINNING);

    sion_fread(cbuffer, 1, 10, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 " read=%s\n", rank, bytes_read, cbuffer);

    sion_parclose_mpi(sid);
  }

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

  /* -------------------------- */
  /* TEST C: read part of file, with sion_seek, absolute position in written bytes */
  /* -------------------------- */
  {
    int sid;
    int i;
    char buffer[1000];
    char cbuffer[1000];

    int64_t bytes_read = -1;
    sion_mpi_options *options = sion_mpi_options_new();
    if (!options)
      return 1;

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

    sion_mpi_options_set_chunksize(options, 1000);
    sion_mpi_options_set_fsblksize(options, 10);

    sid = sion_paropen_mpi("testA.out", SION_OPEN_READ, MPI_COMM_WORLD, options);
    sion_mpi_options_delete(options);

    /* read part of first chunk */
    sion_fread(buffer, 1, 200, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 "\n", rank, bytes_read);

    /* goto chunk 1, read part of this chunk */
    for (i = 0; i < 1000; i++) {
      cbuffer[i] = '\0';
    }
    sion_seek(sid, 1040, SION_SEEK_BEGINNING);

    sion_fread(cbuffer, 1, 36, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 " read=%s (absolute)\n", rank, bytes_read, cbuffer);

    /* read a further part of this chunk */
    sion_fread(buffer, 1, 200, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 "\n", rank, bytes_read);

    /* goto chunk 1, read again part of this chunk */
    for (i = 0; i < 1000; i++) {
      cbuffer[i] = '\0';
    }
    sion_seek_chunk(sid, 1, SION_SEEK_BEGINNING);
    sion_seek(sid, 10, SION_SEEK_CHUNK_BEGINNING);

    sion_fread(cbuffer, 1, 36, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 " read=%s (absolute)\n", rank, bytes_read, cbuffer);

    /* goto chunk 1, read again part of this chunk */
    for (i = 0; i < 1000; i++) {
      cbuffer[i] = '\0';
    }
    sion_seek(sid, 1050, SION_SEEK_BEGINNING);

    sion_fread(cbuffer, 1, 10, sid);
    bytes_read = sion_get_bytes_read(sid);
    printf("on rank %d: bytes_read=%" PRId64 " read=%s (absolute)\n", rank, bytes_read, cbuffer);

    sion_parclose_mpi(sid);
  }

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

#endif

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

  return 0;
}
