#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/wait.h>

FILE *fin;
FILE *fout;
pthread_mutex_t read_file_lock;
pthread_mutex_t write_file_lock;
const int SIZE_OF_OPTIONS_STRING = 64;
char *python_file = "./test_harness.py";
char *delim = ",";

void *runner(void *input) {
    int flag = 1;
    char options[SIZE_OF_OPTIONS_STRING];
    char options_copy[SIZE_OF_OPTIONS_STRING];
    char *saveptr = NULL;
    char *exec_vector[9];
    pid_t child;

    exec_vector[0] = python_file;
    exec_vector[8] = NULL;

    while (flag) {
        pthread_mutex_lock(&read_file_lock);
        if(fgets(options, SIZE_OF_OPTIONS_STRING, fin) == NULL) {
            pthread_mutex_unlock(&read_file_lock);

            flag = 0;
        } else {
            pthread_mutex_unlock(&read_file_lock);
            strncpy(options_copy, options, SIZE_OF_OPTIONS_STRING);

            exec_vector[1] = strtok_r(options_copy, delim, &saveptr);
            exec_vector[2] = strtok_r(NULL, delim, &saveptr);
            exec_vector[3] = strtok_r(NULL, delim, &saveptr);
            exec_vector[4] = strtok_r(NULL, delim, &saveptr);
            exec_vector[5] = strtok_r(NULL, delim, &saveptr);
            exec_vector[6] = strtok_r(NULL, delim, &saveptr);
            exec_vector[7] = strtok_r(NULL, delim, &saveptr);
            exec_vector[7][2] = 0;
            
            if ((child = fork())) {
                waitpid(child, NULL, 0);

                pthread_mutex_lock(&write_file_lock);
                fputs(options, fout);
                fflush(fout);
                pthread_mutex_unlock(&write_file_lock);

                printf("Ending simulation: %s, %s, %s, %s, %s, %s\n", exec_vector[1], exec_vector[2], exec_vector[3], exec_vector[4], exec_vector[7], exec_vector[6]);
            } else {
                printf("Running simulation: %s, %s, %s, %s, %s, %s\n", exec_vector[1], exec_vector[2], exec_vector[3], exec_vector[4], exec_vector[7], exec_vector[6]);

                execve(python_file, exec_vector, NULL);
            }
        }    
    }
    
    return NULL;
}

int main(int argc, char *argv[]) {
    int num_processes = 4;
    pthread_t *other_threads;
    
    if (argc > 1) {
        num_processes = atoi(argv[1]);
    }

    printf("Removing redundant runs.\n");
    system("echo 'comm -23 <(sort -u test_options) <(sort -u completed_options) > sorted_options' | bash");
    system("sort -R sorted_options > curr_options");

    fin = fopen("curr_options", "r");
    fout = fopen("completed_options", "a");
    pthread_mutex_init(&read_file_lock, NULL);
    pthread_mutex_init(&write_file_lock, NULL);
    other_threads = malloc(sizeof(*other_threads) * num_processes);

    printf("Creating threads.\n");
    for (int i = 0; i < num_processes; i++) {
        pthread_create(other_threads + i, NULL, runner, NULL);
    }

    for (int i = 0; i < num_processes; i++) {
        pthread_join(other_threads[i], NULL);
    }
    printf("All threads complete.\n");

    free(other_threads);
    pthread_mutex_destroy(&write_file_lock);
    pthread_mutex_destroy(&read_file_lock);
    fclose(fout);
    fclose(fin);

    return 0;
}
