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

#include "global.h"
#include "options.h"
#include "time.h"

struct streamdata {             /* data structure for each stream / thread */
        pthread_t t_tid;        /* the thread id */
        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 thread is done. */
        char buf[NUMBYTES+1];   /* The thread's buffer. */
} threaddata[MAX_STREAMS];

pthread_cond_t sched_go;

pthread_mutex_t sched_mutex ;


int currthread = 0;
int scheduling = 1;

pthread_t sched_tid;

void *readStream(void *fnp);
/* void *scheduler(void *idp); not needed */

struct timeval start_tv;	
pthread_attr_t attr1;
pthread_attr_t attr2;

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

	int all_done = 0;
	int size = 0;
	char buf[MAX_BUF_SIZE];
	char *filename;
	int fd;
	int ctr = 0, errctr = 0;

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

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

	init_timer(block_size, bit_rate);

	rv = pthread_attr_init(&attr1);
	if(rv) {
		perror("Can't init pthread attributes.");
		exit(1);
	}

	rv = pthread_attr_setscope(&attr1, PTHREAD_SCOPE_SYSTEM);
	if(rv) {
		perror("Can't set thread scope to system-wide scope.");
		exit(1);
	}
		
	pthread_cond_init(&sched_go, NULL);
	pthread_mutex_init(&sched_mutex, NULL);
        
	for (i = 0; i < MAX_STREAMS; i++) {
		threaddata[i].fileno = i;
		threaddata[i].done = 0;
		printf("Creating thread: %d\n",i);
		rv = pthread_create(&(threaddata[i].t_tid), &attr1, readStream, 
				(void *)&(threaddata[i].fileno));
		if(rv) {
			perror("Can't create thread");
			exit(1);
		}
	}

	/*rv = pthread_attr_setscope(&attr2, PTHREAD_SCOPE_SYSTEM);
	  rv = pthread_create(&sched_tid, &attr2, NULL, NULL);*/

	for(i = 0; i < MAX_STREAMS; i++) {
		printf("Waiting for thread: %d\n",i);
		pthread_join(threaddata[i].t_tid, NULL);
	}
    
	/*pthread_join(sched_tid, NULL);*/

}

void *readStream(void *fnp) {
        int fd, i, rv;
        char *filename;
        char *buf;

        int fn = *(int *)fnp;

        buf = threaddata[fn].buf;

        printf("Reading stream: %d\n", fn);

        memset(buf, '\0', NUMBYTES);

        filename = malloc(256);
        if(filename == NULL) {
                fprintf(stderr, "Can't malloc for filename.\n");
                exit(1);
        }

        rv = snprintf(filename, 255, "bf/%2.2d",fn);
        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);
        }

        for (i = 0; i < HOWBIG; i++) {
                pthread_mutex_lock(&sched_mutex);
                while(scheduling || currthread != fn) {
                        pthread_cond_signal(&sched_go);
                        pthread_cond_wait(&sched_go,&sched_mutex);
                }
                printf("Reading chunk %d in stream %d.\n",i, fn);
                if(readMeg(buf, fd) < 0) {
                        fprintf(stderr, "Exiting with i = %d.\n", i);
                        exit(1);
                }
                fprintf (stderr, "Stream %d block %d.\n", fn, i);
                scheduling = 1;
                pthread_mutex_unlock(&sched_mutex);
        }
        pthread_mutex_lock(&sched_mutex);
        pthread_cond_signal(&sched_go);
        threaddata[fn].done = 1;
        pthread_mutex_unlock(&sched_mutex);
		
}

/* Since we're using system threads, a scheduler is not need. The kernel scheduler is used instead. 
 *
  void *scheduler(void *idp) {
        // int *my_id = idp;
        int all_done = 0;
        int i;
        
        gettimeofday(&start_tv, NULL);
        while(!all_done) {
                all_done = 1;
                pthread_mutex_lock(&sched_mutex);
                for(i = 0; i < MAX_STREAMS; i++) {
                        printf("Scheduler unblocking thread %d\n",i);
                        
						Unblock threads in order
                        if(!threaddata[i].done) {
                                all_done = 0;
                                currthread = i;
                                scheduling = 0;
                                pthread_cond_signal(&sched_go);

                                while(!scheduling) {
                                        pthread_cond_wait(&sched_go,&sched_mutex);
                                }
                        }
                }
                pthread_mutex_unlock(&sched_mutex);
        }
}

**/
               

int readMeg(char *buf, int fd) {
        int curByte = 0;
        int rv;
        while(1) {
                rv = read(fd, buf, NUMBYTES - curByte);
                curByte += rv;
                if(rv > 0) {
                        /* fprintf(stderr, "Read %d bytes.\n",rv); */
                        if (curByte < NUMBYTES) {
                                continue;
                        } else {
                                return curByte;
                        }
                } else if (rv == 0) {
                        fprintf(stderr, "Read Zero bytes.\n");
                        return rv;
                } else {
                        fprintf(stderr, "Read returned negative: %d.\n", rv);
                        return rv;
                }
        }
        return rv;
}
