/***************************************************************************
 *   Copyright (C) 2008-2017 by Simon Prunet, Christophe Pichon et al      *
 *   prunet iap.fr                                                         *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA              *
 *   or see https://www.gnu.org/licenses/licenses.html#GPL .               *
 **************************************************************************/

#define _XOPEN_SOURCE 500
#include "../config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>


//#include <mpi.h>
//#define OFFSET_MAX 274877906944 // 4*4096^3
#define OFFSET_MAX ((int64_t)2199023255552) // 4*8192^3
int parallel_write(char*,size_t,int64_t,void*);
int parallel_read(char*,size_t,int64_t,void*);
#if defined ADD0US
void f77_parallel_read(char*,int32_t*,size_t*,int64_t*,void*);
void f77_parallel_write(char*,int32_t*,size_t*,int64_t*,void*);
#elif defined ADD2US
void f77_parallel_read__(char*,int32_t*,size_t*,int64_t*,void*);
void f77_parallel_write__(char*,int32_t*,size_t*,int64_t*,void*);
#else
void f77_parallel_read_(char*,int32_t*,size_t*,int64_t*,void*);
void f77_parallel_write_(char*,int32_t*,size_t*,int64_t*,void*);
#endif

// This routine writes size bytes of buffer at position offset
// in the file filename.
// Can be used in multiple concurrent access

int parallel_write(char *filename, size_t size, int64_t offset, void *buffer) {


  int fd; //file descriptor
  ssize_t i_stat; /* I/O status */ /* Comment: stat.h has a stat(...) function */
  if ((int64_t)size + offset > OFFSET_MAX) {
    fprintf(stderr,"You are trying to access a file location\n");
    /* lld is needed here because some systems have long int = 32 bits: */
    fprintf(stderr,"which is bigger than %lld\n",(long long int)OFFSET_MAX);
    fprintf(stderr,"Verify your code and/or change OFFSET_MAX\n");
    return(1);
  }
  fd = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
  i_stat=pwrite(fd,buffer,size,offset);
  close(fd);
  if (-1 == i_stat || (size_t)i_stat != size) return(1);
  else return(0);
}

// This routine reads size bytes of buffer at position offset
// in the file filename.
// Can be used in multiple concurrent access

int parallel_read(char *filename, size_t size, int64_t offset, void *buffer) {

  int fd; //file descriptor
  ssize_t i_stat; // I/O status
  if ((int64_t)size + offset > OFFSET_MAX) {
    fprintf(stderr,"You are trying to access a file location\n");
    fprintf(stderr,"which is bigger than %lld\n",(long long int)OFFSET_MAX);
    fprintf(stderr,"Verify your code and/or change OFFSET_MAX\n");
    return(1);
  }
  fd = open(filename,O_RDONLY);
  i_stat = pread(fd,buffer,size,offset);
  close(fd);
  if (-1 == i_stat || (size_t)i_stat != size) return(1);
  else return(0);
}

// Fortran wrappers

#if defined ADD0US
void f77_parallel_read(char *filename, int32_t *fnamelen, size_t *size,
                        int64_t *offset, void *buffer) {
#elif defined ADD2US
void f77_parallel_read__(char *filename, int32_t *fnamelen, size_t *size,
                        int64_t *offset, void *buffer) {
#else
void f77_parallel_read_(char *filename, int32_t *fnamelen, size_t *size,
                        int64_t *offset, void *buffer) {
#endif
  int i_stat;
  char *filename_null_terminated;

  filename_null_terminated = (char*)malloc((size_t)(*fnamelen)+1);
  filename_null_terminated[0] = '\0';
  strncat(filename_null_terminated, filename, (size_t)(*fnamelen));
  /* filename[*fnamelen]='\0'; */ /* writing to unallocated memory is risky */
  i_stat = parallel_read(filename_null_terminated,*size,*offset,buffer);
  if (i_stat !=0) fprintf(stderr,"Failure to read %s with parallel_read\n",
                          filename_null_terminated
                          );
  free(filename_null_terminated);
}

#if defined ADD0US
void f77_parallel_write(char *filename, int32_t *fnamelen, size_t *size,
                         int64_t *offset, void *buffer) {
#elif defined ADD2US
void f77_parallel_write__(char *filename, int32_t *fnamelen, size_t *size,
                         int64_t *offset, void *buffer) {
#else
void f77_parallel_write_(char *filename, int32_t *fnamelen, size_t *size,
                         int64_t *offset, void *buffer) {
#endif
  int i_stat;
  char *filename_null_terminated;

  filename_null_terminated = (char*)malloc((size_t)(*fnamelen)+1);
  filename_null_terminated[0] = '\0';
  strncat(filename_null_terminated, filename, (size_t)(*fnamelen));
  /* filename[*fnamelen]='\0'; */ /* writing to unallocated memory is risky */
  i_stat = parallel_write(filename_null_terminated,*size,*offset,buffer);
  if (i_stat !=0) fprintf(stderr,"Failure to write %s with parallel_read\n",
                          filename_null_terminated
                          );
  free(filename_null_terminated);
}



///
/*
TMP_main(int argc, char **argv) {

  int fd;
  char filename[128];
  long nsize, noffset;
  float *buffer;
  int i, i_stat;
  int myid, nproc;

  MPI_Init(&argc,&argv);
  MPI_Comm_size(MPI_COMM_WORLD,&nproc);
  MPI_Comm_rank(MPI_COMM_WORLD,&myid);

  if (argc != 3 && myid==0) {
    fprintf(stderr,"io filename nsize\n");
    MPI_Finalize();
    exit(1);
  }


  strcpy(filename,argv[1]);
  nsize=atoi(argv[2]); // number of floats to read/write
  // noffset=atoi(argv[3]); // offset in sizeof(float)
  noffset=myid*nsize;


  fd = open(filename,O_RDWR|O_CREAT,S_IRUSR|S_IWUSR);
  buffer = (float*)malloc(nsize*sizeof(float));
  for (i=0;i<nsize;i++) buffer[i] = (float)(i+nsize*myid);
  i_stat=pwrite(fd,(void*)buffer,nsize*sizeof(float),noffset*sizeof(float));
  close(fd);
  fprintf(stderr,"coucou from proc # %d: i_stat = %d\n",myid,i_stat);
  MPI_Barrier(MPI_COMM_WORLD);
  return;



  fd = open(filename,O_RDONLY);
  buffer = (float*)malloc(nsize*sizeof(float));
  i_stat=pread(fd,(void*)buffer,nsize*sizeof(float),noffset*sizeof(float));
  for (i=0;i<nsize;i++) fprintf(stderr,"%d %f\n",i+nsize*myid,buffer[i]);
  close(fd);
  return;

  MPI_Finalize();

}
*/
