/**
 Zadanie:
 program ma 1 parameter:
 ./a.out -n<N> <directory>

 - pouzivejte getopt()

 Ukol:
 V adresari vzit regularni soubory
 - nad kazdym souborem spustit vlakno - (predate mu nejak jmeno souboru)
 - vlakno zjisti velikost souboru a updatuje citac velikosti reg.souboru toho adresare.(f)

 cislo N - v jednu chvili muze bezet max. N vlaken, jakmile jedno skonci, spustim dalsi.
 - spravna synch. citac, max. N vlaken
 - osetrovat chyby (nad etc)
 13:45- 16:15
 - vystup - cislo + njekay debugging vhodny
 */

// for fdopendir
#ifndef __USE_GNU
# define __USE_GNU
#endif

#ifndef _ATFILE_SOURCE
# define _ATFILE_SOURCE
#endif


#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <error.h>
#include <err.h>
#include <errno.h>
#include <pthread.h> // threads
#include <dirent.h> // readdir...
#include <sys/types.h>
#include <sys/stat.h> // stat, fstatat, ...
#include <fcntl.h>


void usage(char* argv0)
{
	fprintf(stderr, "usage: %s -n<N> <directory>\n", argv0);
	exit(1);
}

int ACT_THRS = 0;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

off_t ALLSIZE = 0;

void *
adderthr(void *val)
{
	struct stat* fileinfo = (struct stat*) val;

	// zamkneme
	pthread_mutex_lock(&mutex);

	printf("AKTUALNY POCET VLAKIEN: %d sucet: %d = %d + %d\n", ACT_THRS, ALLSIZE, ALLSIZE, fileinfo->st_size);

	// pricitame velkost suboru
	ALLSIZE += fileinfo->st_size;

	--ACT_THRS;

	// vlakno vykonalo pricitanie, signalizacia pre vypustenie dalsieho vlakna
	pthread_cond_signal(&cond);

	// odomkneme
	pthread_mutex_unlock(&mutex);

//	poll(NULL, 0, 95);

	return val;
}

void secti(char* dirname, int N)
{
	DIR *d;
	int dirfd;
	struct dirent *de;
	struct stat* fileinfo;
	void * thr_retval;
	int i;

	int threadnr = 0;
	int threadsize = 10;

	// cisla vsetkych threadov pre joinovanie
	pthread_t* threads;
	if ((threads = malloc(threadsize * sizeof(pthread_t))) == NULL)
		errx(1, "chyba naalokovania tabulky threadov");

    if ((dirfd = open(dirname, O_RDONLY)) < 0)
    	errx(1, "Chyba otvorenia zlozky dirdf - %s", dirname);
//        perror(dirname);

	if ((d = fdopendir(dirfd)) == NULL)
		errx(1, "Chyba otvorenia zlozky fdopendir - %s", dirname);

	// Rozlisenie medzi koncom zlozky a chybou
	errno = 0;
	while ((de = readdir(d))) {
		// ked je to regularny subor

		if ((fileinfo = malloc(sizeof(struct stat))) == NULL)
			perror("malloc");

		// nenasleduje symlinky
		if (fstatat(dirfd, de->d_name, fileinfo, AT_SYMLINK_NOFOLLOW) == -1)
			perror("fstatat");

		if (de->d_type == DT_REG) {
//		if (S_ISREG(fileinfo->st_mode)) {

			pthread_mutex_lock(&mutex);

			printf("spracovanie suboru %s\n", de->d_name);
			// uspi a caka kym skoncia vlakna
            while (ACT_THRS == N) {
            	pthread_cond_wait(&cond, &mutex);
            }

            if (threadnr == threadsize) {
            	// realloc
            	threadsize = threadsize * 2;
            	if ((threads = realloc(threads, threadsize * sizeof(pthread_t))) == NULL)
            		perror("realloc");
            }

            ++ACT_THRS;
            pthread_create(&threads[threadnr++], NULL, adderthr, (void *) fileinfo);

			// zobudi vlakna
//			if (ACT_THRS < N)
//				pthread_cond_signal(&cond);

			pthread_mutex_unlock(&mutex);
		} else {
			printf("neregularny subor: %s\n", de->d_name);
			free(fileinfo);
		}
	}

	// join all threads
	for (i = 0; i != threadnr; ++i) {
		pthread_join(threads[i], &thr_retval);

		// uvolnenie zdrojov
		fileinfo = (struct stat*) thr_retval;
		free(fileinfo);
	}

	free(threads);

	if (errno != 0)
		warn("%s", dirname);

	printf("HOTOVO: vysledok: %ld", (long int)ALLSIZE);

	// directory closed by close
	// closedir(d);

	if (close(dirfd) == -1)
		perror("close");
}

int main(int argc, char* argv[])
{
	int N = 10; // default number of threads
	char *dirname;

	int opt;
	char *argv0 = argv[0];

	//  opterr = 0;

	if (argc < 2)
		usage(argv0);

	printf("first param (command): %s\n", argv[1]);

	argv++;
	argc--;
	while ((opt = getopt(argc, argv, "n:")) != -1)
	{
		switch (opt)
		{
		case 'n':
			printf("option -c set to '%s'\n", optarg);
			N = atoi(optarg);
			break;
		case '?':
			usage(argv0);
			break;
		}
	}

	printf("option arguments done\n");

	/* optind is the 1st non-option argument */
	argv = argv + optind;

	if (argv[0] != NULL)
	{
		printf("dirname:");
		dirname = argv[0];
		printf(" %s\n", dirname);
	} else {
		printf("no filenames entered\n");
		usage(argv0);
	}

	secti(dirname, N);

	return (EXIT_SUCCESS);
}