/*
 * Copyright 2011 Steven Gribble
 *
 *  This file is part of the UW CSE 333 course project sequence
 *  (333proj).
 *
 *  333proj 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  333proj 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 333proj.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>

void PrintBandwidth(struct timeval start_t,
                    struct timeval stop_t,
                    int numbytes);

#define BUFSIZE 65536
#define WRITELEN (65536*4096)
#define FILENAME "writefile.bin"

int main(int argc, char **argv) {
  int fd_out;
  char buf[BUFSIZE];
  ssize_t writelen;
  int writecount;
  struct timeval start_t, stop_t;

  // Open the output file
  while (1) {
    fd_out = open(FILENAME,
                  O_WRONLY | O_TRUNC | O_CREAT,
                  S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (fd_out == -1) {
      if ((errno == EINTR) || (errno == EWOULDBLOCK)) {
        // Try again.
        continue;
      }
      // Fail.
      perror("Couldn't open writefile.bin for writing");
      exit(EXIT_FAILURE);
    }
    // The open succeeded, so break out of the loop.
    break;
  }

  // Grab a start timestamp.
  assert(gettimeofday(&start_t, NULL) == 0);

  // Write WRITELEN bytes
  writecount = 0;
  while (writecount < WRITELEN) {
    // Try to write BUFSIZE bytes to the output file.
    writelen = write(fd_out,
                     (void *) buf,
                     (size_t) BUFSIZE);
    if (writelen == -1) {
      if ((errno == EINTR) ||
          (errno == EWOULDBLOCK) ||
          (errno == EAGAIN))
        continue;

      perror("write failed");
      exit(EXIT_FAILURE);
    }
    writecount += writelen;
  }

  // flush the output file
  if (fsync(fd_out) != 0) {
    perror("fsync of output file failed");
    exit(EXIT_FAILURE);
  }

  // Grab a stop timestamp.
  assert(gettimeofday(&stop_t, NULL) == 0);

  // Done!
  PrintBandwidth(start_t, stop_t, WRITELEN);

  // Clean up; close the file descriptor, delete
  // the file we created.
  close(fd_out);
  assert(unlink(FILENAME) == 0);

  return EXIT_SUCCESS;  // defined in stdlib.h
}

void PrintBandwidth(struct timeval start_t,
                    struct timeval stop_t,
                    int numbytes) {
  double total_seconds, bw;

  total_seconds = (stop_t.tv_sec - start_t.tv_sec);
  total_seconds += (stop_t.tv_usec - start_t.tv_usec) / 1000000.0;

  bw = ((double) numbytes) / total_seconds;
  bw = bw / 1000000.0;

  fprintf(stdout, "Bandwidth: %f MB/s\n", bw);
}