#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 fileno;		/* The filenumber for this stream */
	int pos;		/* The position in the file */
	int fd;			/* The file's file descriptor */
	int done;               /* True when this stream is done. */
	int size;		/* the stream's size */
} streamdata[MAX_STREAMS];


int how_big;

char *mapMeg(int fn);

int main(int argc, char* argv[])
{

	int i = 0, j = 0;
	int rv;
        char *buf;
        char *filename;

	int fd;
	int ctr = 0, errctr = 0;
        struct stat sbuf;

	/* 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);
        }


	/* Not necessary */
	// memset(buf,'\0', NUMBYTES);

	/* Initialize the data structures which contain per stream info
	 * including opening files */
	for (i = 0; i < num_streams; i++) {
		streamdata[i].fileno = i;
		streamdata[i].done = 0;
		streamdata[i].pos = 0;
		filename = malloc(256);
	        if(filename == NULL) {
       			fprintf(stderr, "Can't malloc for filename.\n");
                	exit(1);
        	}
        
        	rv = snprintf(filename, 255, "bf/%2.2d",i);
        	if(rv != 5) {
                	fprintf(stderr, "Can't snprintf for filename.\n");
		}
        
		fd = open(filename, O_RDONLY, NULL);
        	if(fd < 1) {
                	printf("error: fd %d on %s\n",fd, filename);
        	}

		streamdata[i].fd = fd;
		
	
                rv = fstat(fd, &sbuf);
                if(rv != 0) {
                        printf("fstat error: %d\n",rv);
                        exit(1);
                }
                streamdata[i].size = sbuf.st_size;
#ifdef DEBUG
		printf("Initialized stream: %d\n",i);
#endif
        }

        how_big = num_blocks;
        if(how_big > streamdata[0].size / block_size) {
                fprintf(stderr, "Can't read %d blocks from file!!\n", how_big);
                exit(1);
        }
        
	/* 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;

	extern int block_size;
        
#ifdef DEBUG
        fprintf(stderr, "Entering mapMeg: stream %d: fd %d, pos %d\n",
                fn, streamdata[fn].fd, streamdata[fn].pos);
#endif
	
	buf = mmap(NULL, block_size, PROT_READ, MAP_SHARED,
                   streamdata[fn].fd,
                   (off_t) (streamdata[fn].pos * block_size));
        
	if (buf == MAP_FAILED) {
		perror("Mmap failed!\n");
		exit(4);
	}
#ifdef DEBUG
	printf("Copying content of file %d at position %d to 0x%X...\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

        rv = munmap(buf, block_size);
	if(rv !=0) {
		perror("Munmap error!");
		exit(6);
	}
	streamdata[fn].pos++;
	return buf;
}
