/****************************************************************************
**  SIONLIB     http://www.fz-juelich.de/jsc/sionlib                       **
*****************************************************************************
**  Copyright (c) 2008-2009                                                **
**  Forschungszentrum Juelich, Juelich Supercomputing Centre               **
**                                                                         **
**  See the file COPYRIGHT in the package base directory for details       **
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "sion.h"

#define FILENAME_LENGTH 1024

void usage(char *name)
{
  fprintf(stderr, "Usage: %s options <sionfn> <prefix>\n\nwith the following optional options (default values in parathesis):\n\n", name);

  fprintf(stderr, "  [-v]                   verbose mode \n");
  fprintf(stderr, "  [-g]                   use global rank for numbering files\n");
  fprintf(stderr, "  [-d <num>]             number of digits for filename generation (default 5) \n");
  exit(1);
}


int main(int argc, char **argv)
{
  FILE     *fp, *outfp;
  char      infilename[FILENAME_LENGTH];
  char      outfilename[FILENAME_LENGTH];
  char      prefix[FILENAME_LENGTH];
  char      fnmask[FILENAME_LENGTH];

  int       i, rank, blknum;
  char     *localbuffer;
  sion_int64 chunksize = 0;
  sion_int64 left, bread, bsumread, bwrote, bsumwrote;

  /* options */
  int       digits = 5;
  int       verbose = 0;
  int       useglobalranks = 0;

  /* for file infomation */
  int       sid, ntasks, nfiles, maxblocks;
  sion_int32 fsblksize;
  sion_int64 globalskip;
  sion_int64 start_of_varheader;
  sion_int64 *sion_localsizes;
  sion_int64 *sion_globalranks;
  sion_int64 *sion_blockcount;
  sion_int64 *sion_blocksizes;
  sion_int64 *siondump_sum_bytes_per_task;
  sion_int64 siondump_sum_bytes;
  sion_int64 siondump_filesize;
  /* parse command line */
  i = 1;
  if (argc < 3)
    usage(argv[0]);

  while (i < argc) {
    if (argv[i][0] == '-') {
      switch (argv[i][1]) {
      case 'd':
        digits = atoi(argv[++i]);
        break;
      case 'g':
        useglobalranks=1;
        break;
      case 'v':
        verbose++;
        break;
      default:
        usage(argv[0]);
      }
    }
    i++;
  }

  strcpy(infilename, argv[argc - 2]);
  strcpy(prefix, argv[argc - 1]);

  printf("sionsplit: filename:                  %-30s\n", infilename);
  printf("sionsplit: prefix:                    %-30s\n", prefix);

  sid = sion_open(infilename, "rb", &nfiles, &ntasks, NULL, &fsblksize, NULL, &fp);

  printf("sionsplit: sid:                       %d\n", sid);
  printf("sionsplit: filename:                  %-30s\n", infilename);
  printf("sionsplit: number of tasks:           %d\n", ntasks);
  printf("sionsplit: number of digits:          %d\n", digits);
  printf("sionsplit: use global ranks:          %d\n", useglobalranks);
  printf("sionsplit: current endianness:        %s\n", (sion_get_endianness())? "big" : "little");
  printf("sionsplit: file endianness:           %s\n", (sion_get_file_endianness(sid)) ? "big" : "little");
  printf("sionsplit: fsblksize:                 %lu bytes (%6.2f MB)\n", (unsigned long) fsblksize, fsblksize / 1024.0 / 1024.0);

  sion_get_locations(sid, &ntasks, &maxblocks, &globalskip, &start_of_varheader, &sion_localsizes, &sion_globalranks, &sion_blockcount,
                     &sion_blocksizes);

  printf("sionsplit: max number of chunks:      %d\n", maxblocks);

  /* analysis */
  siondump_sum_bytes_per_task = (sion_int64 *) malloc(ntasks * sizeof(sion_int64));
  for (rank = 0; rank < ntasks; rank++)
    siondump_sum_bytes_per_task[rank] = 0;
  for (rank = 0; rank < ntasks; rank++) {
    for (blknum = 0; blknum < maxblocks; blknum++) {
      siondump_sum_bytes_per_task[rank] += sion_blocksizes[ntasks * blknum + rank];
    }
  }

  siondump_sum_bytes = 0;
  for (rank = 0; rank < ntasks; rank++)
    siondump_sum_bytes += siondump_sum_bytes_per_task[rank];

  printf("sionsplit: datasize in file (aggr.):  %lld bytes  (%6.2f MB)\n", siondump_sum_bytes, siondump_sum_bytes / 1024.0 / 1024.0);

  siondump_filesize = start_of_varheader + (maxblocks + 1) * rank * sizeof(sion_int64);

  printf("sionsplit: start_of_varheader:        %lld bytes  (%6.2f MB)\n", start_of_varheader, start_of_varheader / 1024.0 / 1024.0);
  printf("sionsplit: size of file:              %lld bytes  (%6.2f MB)\n", siondump_filesize, siondump_filesize / 1024.0 / 1024.0);

  if (siondump_filesize > 0) {
    printf("sionsplit: file usage:                %8.6f%%\n", (double) siondump_sum_bytes / (double) siondump_filesize * 100.0);
  }
  chunksize = 0;
  
  for (rank = 0; rank < ntasks; rank++) {
    if (chunksize<sion_localsizes[rank]) chunksize=sion_localsizes[rank];
    if (chunksize<sion_blocksizes[rank]) chunksize=sion_blocksizes[rank];
  }
  printf("sionsplit: max chunksize:             %lld\n", chunksize);
  localbuffer = (char *) malloc(chunksize * sizeof(char));
  if (localbuffer == NULL) {
    fprintf(stderr, "cannot allocate localbuffer of size %lld , aborting ...\n", chunksize * sizeof(char));
    return (1);
  }


  sprintf(fnmask, "%s%%0%dd", prefix, digits);
  printf("sionsplit: filename mask:             %-s\n", fnmask);

  for (rank = 0; rank < ntasks; rank++) {
    if(useglobalranks) {
      sprintf(outfilename, fnmask, (int) sion_globalranks[rank]);
    } else {
      sprintf(outfilename, fnmask, rank);
    }


    printf("sionsplit: generating file:           %-s\n", outfilename);
    outfp = _sion_create_and_open_file_for_write(outfilename);
    if (outfp == NULL) {
      fprintf(stderr, "cannot open outfile %s , aborting ...\n", outfilename);
      return (1);
    }

    for (blknum = 0; blknum < sion_blockcount[rank]; blknum++) {

      /* seek position of block */
      sion_seek(sid, rank, blknum, 0);
      DPRINTFP((1, "sionsplit", 0, "after sion_seek sid=%d rank=%d blknum=%d fileposition=%lld\n", sid, rank, blknum, sion_get_position(sid)));

      /* read data from block */
      left = sion_blocksizes[ntasks * blknum + rank];
      bsumread = 0;
      while (left > 0) {
        DPRINTFP((8, "sionsplit", 0, "will read %lld bytes localbuffer+%lld\n", left, bsumread));
        bread = fread(localbuffer + bsumread, 1, left, fp);
        left -= bread;
        bsumread += bread;
        printf("sionsplit:                            %lld read left=%lld \n", bread, left);
      }

      /* write data to outfile */
      left = sion_blocksizes[ntasks * blknum + rank];
      bsumwrote = 0;
      while (left > 0) {
        bwrote = fwrite(localbuffer + bsumwrote, 1, left, outfp);
        left -= bwrote;
        bsumwrote += bwrote;
        printf("sionsplit:                            %lld wrote left=%lld \n", bwrote, left);
      }

    }

    fclose(outfp);

  }

  free(siondump_sum_bytes_per_task);
  free(localbuffer);
  sion_close(sid);

  return (0);
}
