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

#include "global.h"
#include "options.h"
#include "timer.h"
#include "since.h"

/* This version has a single thread that reads several streams
 * synchronously.
 */

struct streamdata {	/* data structure for each stream */
	int pos;		/* The position in the file */
#ifdef __SOLARIS__
	off64_t offset;		/* The file's offset */
#else
	off_t offset;	
#endif /* __SOLARIS__ */
	int done;               /* True when this stream is done. */
	int size;		/* the stream's size */
} streamdata[MAX_STREAMS];


int how_big;

/* The device file descriptor. */
int dev_fd;

char *mapMeg(int fn);

int main(int argc, char* argv[]) {
	int i = 0, j = 0;
	char *buf;
	int ctr = 0, errctr = 0;

	/* see options.c */
	extern int block_size, num_streams, bit_rate, num_blocks;

	/* see options.c */
        parseArgs(argc, argv);
        if(validateParams() != VALIDATION_SUCCESS) {
                printUsage();
                exit(1);
        }

        how_big = num_blocks;

        /* Seed the random number generator */
        srandom(1);

        /* Open the device */
#ifdef __SOLARIS__
        dev_fd = open64(RAWDEV, O_RDONLY | O_LARGEFILE, NULL);
#else
        dev_fd = open(RAWDEV, O_RDONLY, NULL);
#endif /* __SOLARIS__ */
        if(dev_fd < 1) {
                printf("open error");
                exit(1);
        }

	/* Initialize the data structures which contain per stream info
	 * including opening files */
	for (i = 0; i < num_streams; i++) {
		streamdata[i].done = 0;
		streamdata[i].pos = random() % how_big;
		streamdata[i].offset = ( (unsigned long long)i * 85 / 63  ) * NUMBYTES * (unsigned long long)how_big;
		
#ifdef DEBUG
		printf("Initialized stream: %d\n",i);
#endif
        }

	/* See timer.c */
	init_timer(block_size, bit_rate);

        /* See since.c */
        init_since();

	for (i = 0; i < how_big; i++) {
                now_since();
		for(j = 0; j < num_streams; j++) {
#ifdef DEBUG
                	printf("Reading chunk %d in stream %d.\n",i, j);
#endif
                	if((buf = mapMeg(j)) <= 0) {
                       		fprintf(stderr, "Exiting because of stream %d.\n", j);
                        	exit(1);
                	}
#ifdef DEBUG
                	fprintf (stderr, "Stream %d block %d.\n", j, i);
#endif
		}
		++ctr;
                since();
		if(sleep_end_quantum_increment()) {
			++errctr;
		}
		printf("Period %d, %d errors\n",ctr,errctr);
        }
        end_since();
        printf("Total errors: %d\n", errctr);
	exit(0);

}

char *mapMeg(int fn) {
	char cksum = 0;
	int rv,i;
	char *buf;
#ifdef __SOLARIS__
        off64_t offset;
#else
        off_t offset;
#endif /* __SOLARIS__ */


	extern int block_size;

        offset = streamdata[fn].offset + ONE_MEGA * streamdata[fn].pos;
        
#ifdef DEBUG
        fprintf(stderr, "Entering mapMeg: stream %d: offset 0x%llX, pos %d\n",
                fn, offset, streamdata[fn].pos);
#endif
	
	buf = mmap(NULL, block_size, PROT_READ, MAP_SHARED,
                   dev_fd, offset);
        
	if (buf == MAP_FAILED) {
		perror("Mmap failed!\n");
		exit(4);
	}
#ifdef DEBUG
	printf("Copying content of file %d at position %d to %d...\n", fn, streamdata[fn].pos, (int)buf);
#endif
	for (i=0; i< block_size; i+=512) {
		cksum ^= buf[i];
        }
#ifdef DEBUG
        fprintf(stderr, "Got this far.\n");
#endif
	if(++streamdata[fn].pos >= 100) {
		streamdata[fn].pos = 0;
	}

        rv = munmap(buf, block_size);
	if(rv !=0) {
		perror("Munmap error!");
		exit(6);
	}
	return buf;
}
