#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/param.h>
#include <string.h>
#include <sys/time.h>

#define BSIZE  (4 * 1024)
#define BALIGN (4096)

#define MAX_NAME (256)
char base_name[MAX_NAME];

char *get_name(int n)
{
  static char name[MAX_NAME + 5];
  
  sprintf(name, "%s%d", base_name, n);
  return name;
}


void display_rate(struct timeval start, struct timeval end, int len) 
{
  int d_s, d_us;
  float sec;

  d_s  = end.tv_sec  - start.tv_sec;
  d_us = end.tv_usec - start.tv_usec;

  sec = d_s + d_us / 1000000.0;

  printf("Transferred %dMb of data in %.2f seconds (%.2fMb/s)\n",
	 len, sec, len / sec);
  fflush(NULL);
}

void create_files(int n, int sz)
{
  int out[n], i;
  char tmp[BSIZE+BALIGN];
  char *buf = (char *)(((unsigned int)tmp + BALIGN - 1) & ~(BALIGN - 1));
  int pos;
  struct timeval start, end;

  printf("Writing %dMb of data to %d files in parallel\n", sz, n);
  fflush(NULL);

  for (i = 0; i < n; i++) {
    out[i] = open(get_name(i), O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (out[i] < 0) {
      perror("Creating output file");
      exit(1);
    }
  }

  memset(buf, 0, BSIZE);
  
  gettimeofday(&start, NULL);

  for (pos = 0; pos < (sz * 1024 * 1024); pos += BSIZE) {
    for(i = 0; i < n; i++) {
      if (write(out[i], buf, BSIZE) != BSIZE) {
	  fprintf(stderr, "Problem writing output file\n");
	  exit(2);
      }
    }
  }
  
  for (i=0; i<n; i++) {
    fdatasync(out[i]);
    close(out[i]);
  }

  gettimeofday(&end, NULL);

  display_rate(start, end, sz * n);
}

void read_files(int n, int sz)
{
  int fd[n], i;
  char tmp[BSIZE+BALIGN];
  char *buf = (char *)(((unsigned int)tmp + BALIGN - 1) & ~(BALIGN - 1));
  int pos;
  struct timeval o_start, o_end;

  printf("Reading files in sequence\n");
  fflush(NULL);

  for (i = 0; i < n; i++) {
    fd[i] = open(get_name(i), O_RDONLY);
    if (fd[i] < 0) {
      perror("Creating reading file");
      exit(1);
    }
  }

  gettimeofday(&o_start, NULL);

  for(i = 0; i < n; i++) {
    struct timeval start, end;

    gettimeofday(&start, NULL);
    for (pos = 0; pos < (sz * 1024 * 1024); pos += BSIZE) {
      if (read(fd[i], buf, BSIZE) != BSIZE) {
	fprintf(stderr, "Problem reading file\n");
	exit(2);
      }
    }
    gettimeofday(&end, NULL);
    display_rate(start, end, sz);

  }
  
  for (i=0; i<n; i++) {
    close(fd[i]);
  }

  gettimeofday(&o_end, NULL);

  if (n > 1)
    display_rate(o_start, o_end, sz * n);
}

void delete_files(int n)
{
  int i;
  
  for (i = 0; i < n; i++) {
    unlink(get_name(i));
  }
}

void usage(char **argv) 
{
  fprintf(stderr, "Usage: %s <name> <size>\n", argv[0]);
  fprintf(stderr, " Creates files name0, name1 ... nameN in parallel all of given size (in Mb)\n");
  exit(1);
}


void run_test(int n, int s)
{
  delete_files(n);

  create_files(n, s);

  read_files(n, s);

  delete_files(n);

  printf("\n");
  fflush(NULL);
}  

int main(int argc, char *argv[])
{
  unsigned int  s = 512;
  strcpy(base_name, "temp_");

  if (argc > 1) {
    int len = strlen(argv[1]);
    if ((len == 0) || (len >= MAX_NAME) || (*argv[1] == '-'))
      usage(argv);
    strcpy(base_name, argv[1]);
  }

#if 0
  if (argc > 2) {
    n = atoi(argv[2]);
    if ((n == 0) || (n > 10))
      usage(argv);
  }
#endif

  if (argc > 2) {
    s = atoi(argv[2]);
    if ((s == 0) || (s > 4000))
      usage(argv);
  }

  if (argc > 3) {
      usage(argv);
  }

  run_test(1, s);
  run_test(2, s / 2);
  //run_test(4, s / 4);

  return 0;
}

