#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

extern "C" {
	#include <locale.h>
	#include <getopt.h>
	#include <fcntl.h>
	#include <sys/uio.h>
}

#include <unistd.h>

#include <pthread.h>

#include "../benchcltr.hfa"

int fd;
volatile bool run = false;
volatile size_t count = 0;

unsigned long int buflen = 50;

void * reader_main( void * arg ) {
      pthread_barrier_wait( (pthread_barrier_t*) arg );
	/* paranoid */ assert( true == __atomic_load_n(&run, __ATOMIC_RELAXED) );

	char data[buflen];
	struct iovec iov = { data, buflen };

	while(__atomic_load_n(&run, __ATOMIC_RELAXED)) {
		int r = preadv2(fd, &iov, 1, 0, 0);
		if(r < 0) {
                  fprintf(stderr, "%s\n", strerror(-r));
                  abort();
            }

		__atomic_fetch_add( &count, 1, __ATOMIC_SEQ_CST );
	}

      return NULL;
}

int main(int argc, char * argv[]) {
	unsigned flags = 0;
	unsigned sublen = 16;

      setlocale(LC_ALL, "");

	for(;;) {
		static struct option options[] = {
			BENCH_OPT_LONG
			{"polled-io",    required_argument, 0, 'i'},
			{"bufsize",      required_argument, 0, 'b'},
			{0, 0, 0, 0}
		};

		int idx = 0;
		int opt = getopt_long(argc, argv, BENCH_OPT_SHORT "ib:", options, &idx);

		const char * arg = optarg ? optarg : "";
		char * end;
		switch(opt) {
			// Exit Case
			case -1:
				goto arg_loop;
			BENCH_OPT_CASE
			case 'i':
				flags |= O_DIRECT;
				break;
			case 'b':
				buflen = strtoul(arg, &end, 10);
				if(*end != '\0' && buflen < 10) {
					fprintf(stderr, "Buffer size must be at least 10, was %s\n", arg);
					goto usage;
				}
				break;
			default: /* ? */
				fprintf(stderr, "%d\n", opt);
			usage:
				bench_usage( argv );
				fprintf( stderr, "  -i, --polled-io          If set opens the file with O_DIRECT\n" );
				fprintf( stderr, "  -b, --buflen=SIZE        Number of bytes to read per request\n" );
				exit(EXIT_FAILURE);
		}
	}
      arg_loop:

	fd = open(__FILE__, flags);
	if(fd < 0) {
		fprintf(stderr, "Could not open source file\n");
		exit(EXIT_FAILURE);
	}

	printf("Running %d threads, reading %lu bytes each, over %d processors for %f seconds\n", nthreads, buflen, nprocs, duration);

	{
		uint64_t start, end;
		{
			pthread_barrier_t barrier;
                  pthread_barrier_init(&barrier, NULL, nthreads + 1);
			{
				pthread_t threads[nthreads];
                        for(int i = 0; i < nthreads; i++) {
                        	pthread_attr_t attr;
                              pthread_attr_init( &attr );
                              pthread_create( &threads[i], &attr, reader_main, &barrier );
                        }

				printf("Starting\n");
				bool is_tty = isatty(STDOUT_FILENO);
				start = timeHiRes();
				run = true;

				pthread_barrier_wait( &barrier );
				wait_duration(duration, start, end, is_tty);

				run = false;
				end = timeHiRes();
				printf("\nDone\n");

                        for(int i = 0; i < nthreads; i++) {
                              void * ret;
                              pthread_join( threads[i], &ret );
                        }
			}
                  pthread_barrier_destroy(&barrier);
		}
		printf("Took %'ld ms\n", to_miliseconds(end - start));
		printf("Total reads      : %'15zu\n", count);
		printf("Reads per second : %'18.2lf\n", ((double)count) / to_fseconds(end - start));
		printf("Total read size  : %'15zu\n", buflen * count);
		printf("Bytes per second : %'18.2lf\n", ((double)count * buflen) / to_fseconds(end - start));
	}

	close(fd);
}
