/****************************************************************************
**  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 <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "sion.h"

int main(int argc, char **argv)
{
  /* -------------------------- */
  /* PROLOG */
  /* -------------------------- */

  /* -------------------------- */
  /* TEST A: test with wrong parameters */
  /* -------------------------- */
  {
    int32_t fsblksize = 10;
    int64_t *chunksizes = NULL;
    int *globalranks = NULL;
    int ntasks = 4;
    int nfiles = 1;
    int outsid;
    int mainv, subv, patchl, formatv;

    assert(SION_SUCCESS == sion_get_version(&mainv, &subv, &patchl, &formatv));

    outsid = sion_open("testA.out", "wb", &ntasks, &nfiles, NULL, &fsblksize, &globalranks, NULL);
    if (outsid >= 0) {
      sion_close(outsid);
    } else {
      fprintf(stderr, "TEST A: open returned %d\n", outsid);
    }

    chunksizes = NULL;
    outsid = sion_open("testA.out", "wb", &ntasks, &nfiles, &chunksizes, &fsblksize, &globalranks, NULL);
    if (outsid >= 0) {
      sion_close(outsid);
    } else {
      fprintf(stderr, "TEST A: open returned %d\n", outsid);
    }
    free(chunksizes);
    free(globalranks);
  }

  /* -------------------------- */
  /* TEST B: create an empty file */
  /* -------------------------- */
  {
    int32_t fsblksize = 10;
    int64_t *chunksizes = NULL;
    int ntasks = 4;
    int outsid;
    int t;
    sion_options options = sion_options_new();

    chunksizes = malloc(ntasks * sizeof(int64_t));
    if (chunksizes == NULL) {
      fprintf(stderr, "TEST B: cannot allocate chunksizes of size %lu, aborting ...\n", (unsigned long)ntasks * sizeof(int64_t));
      return 1;
    }
    for (t = 0; t < ntasks; t++) {
      chunksizes[t] = 1000 + t * 200;
    }

    sion_options_set_chunksizes(&options, ntasks, chunksizes);
    sion_options_set_fsblksize(&options, fsblksize);

    outsid = sion_open_with_options("testB.out", SION_OPEN_WRITE, ntasks, &options);
    if (outsid >= 0) {
      assert(sion_is_serial_opened(outsid));
      assert(1 == sion_get_number_of_files(outsid));
      assert(1 == sion_get_filenumber(outsid));
      assert(SION_SUCCESS == sion_optimize_fp_buffer(outsid));
      assert(SION_SUCCESS == sion_optimize_fp_buffer(outsid));
      sion_close(outsid);
    } else {
      fprintf(stderr, "TEST B: open returned %d\n", outsid);
    }
    free(chunksizes);
  }

  /* ------------------------------------- */
  /* TEST C: create an file with some data */
  /* ------------------------------------- */
  {
#define BUFSIZE 10000
    int32_t fsblksize = 10;
    int64_t *chunksizes = NULL;
    int ntasks = 4;
    int outsid;
    int t, i, j, c;
    char buffer[BUFSIZE];
    size_t bwrote;
    long sum;
    sion_options options = sion_options_new();

    chunksizes = malloc(ntasks * sizeof(int64_t));
    if (chunksizes == NULL) {
      fprintf(stderr, "TEST C: cannot allocate chunksizes of size %lu, aborting ...\n", (unsigned long)ntasks * sizeof(int64_t));
      return 1;
    }
    for (t = 0; t < ntasks; t++) {
      chunksizes[t] = 1000 + t * 200;
    }
    sion_options_set_chunksizes(&options, ntasks, chunksizes);
    sion_options_set_fsblksize(&options, fsblksize);
    sion_options_set_lowlevel_api(&options, SION_LOWLEVEL_API_POSIX);

    outsid = sion_open_with_options("testC.out", SION_OPEN_WRITE, ntasks, &options);
    if (outsid >= 0) {
      assert(SION_SUCCESS == sion_optimize_fp_buffer(outsid));

      for (j = 0; j < 2; j++) {
        for (t = 0; t < ntasks; t++) {
          for (i = 0; i < chunksizes[t]; i++) {
            buffer[i] = 'A' + t;
          }
          sion_switch_logical_file(outsid, t);
          int64_t position = sion_get_position(outsid);
          printf("on rank %d: starting to write at position %" PRId64 "\n", t, (int64_t)position);
          /* write nothing to check 0 as input */
          bwrote = sion_fwrite(buffer, 0, chunksizes[t] - 9, outsid);
          bwrote = sion_fwrite(buffer, 1, 0, outsid);
          /* write something */
          bwrote = sion_fwrite(buffer, 1, chunksizes[t] - 9, outsid);
          assert(SION_SUCCESS == sion_flush(outsid));
          for (c = 0, sum = 0; c < bwrote; c++) {
            sum = sum + buffer[c];
          }
          printf("on rank %d: bytes_written=%d blocksum=%ld\n", t, (int)bwrote, sum);
        }
      }

      sion_close(outsid);
    } else {
      fprintf(stderr, "TEST C: open returned %d\n", outsid);
    }
    free(chunksizes);
  }

  /* ------------------------------------- */
  /* TEST C: read an file with some data   */
  /* ------------------------------------- */
  {
#define BUFSIZE 10000
    int64_t *chunksizes = NULL;
    int64_t *_globalranks = NULL;
    int ntasks;
    int _maxchunks;
    int64_t _globalskip;
    int64_t _start_of_varheader;
    int64_t *_blockcount;
    int64_t *_blocksizes;
    int outsid;
    int t, i, j, c;
    char buffer[BUFSIZE];
    size_t bread;
    long sum;

    //outsid = sion_open("testC.out", "rb", &ntasks, &nfiles, &chunksizes, &fsblksize, &globalranks, &outfp);
    outsid = sion_open_with_options("testC.out", SION_OPEN_READ, 42, NULL);
    if (outsid >= 0) {
      sion_get_locations(
        outsid, &ntasks, &_maxchunks, &_globalskip, &_start_of_varheader, &chunksizes, &_globalranks, &_blockcount, &_blocksizes);

      for (j = 0; j < 2; j++) {
        for (t = 0; t < ntasks; t++) {
          for (i = 0; i < chunksizes[t]; i++) {
            buffer[i] = '-';
          }
          sion_switch_logical_file(outsid, t);
          /* read nothing to check 0 as input */
          bread = sion_fread(buffer, 0, chunksizes[t] - 9, outsid);
          bread = sion_fread(buffer, 1, 0, outsid);

          bread = sion_fread(buffer, 1, chunksizes[t] - 9, outsid);
          for (c = 0, sum = 0; c < bread; c++) {
            sum = sum + buffer[c];
          }
          printf("TESTC read: rank=%d chunksizes=%ld  bread=%d blocksum=%ld\n", t, (long)chunksizes[t], (int)bread, sum);
        }
      }

      sion_close(outsid);
    } else {
      fprintf(stderr, "TEST C: open returned %d\n", outsid);
    }
  }

  /* -------------------------- */
  /* EPILOG */
  /* -------------------------- */

  return 0;
}
