#include "GSAT/GSAT.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <mpi.h>
#include "Main.hpp"

using namespace std;

int world_size;
int world_rank;

void help(char* prog) {
    fprintf(stderr, "usage: mpirun -n int ./%s -i file.cnf [-t int]\n\t-n Number of process to use\n\t-i CNF file to solve\n\t-t Number of threads to use\n", prog);
    exit(1);
}

int main(int argc, char* argv[]) {
    extern char * optarg; 
    int nbThread = DEFAULT_NB_THREAD;
    int opt;
    bool optI = false;

    char** gsatArg = (char**) malloc(sizeof(char*) * 3);
    gsatArg[0] = (char*) malloc(sizeof(char) * strlen(argv[0]) + 1);
    memset(gsatArg[0], 0, strlen(argv[0]) + 1);
    strncpy(gsatArg[0], argv[0], strlen(argv[0]));

    while((opt = getopt(argc, argv, "i:t:")) != -1) {
        switch(opt) {
            case 'i':
                optI = true;
                gsatArg[1] = (char*) malloc(sizeof(char) * 3);
                memset(gsatArg[1], 0, 3);
                strncpy(gsatArg[1], "-i", 2);
                gsatArg[2] = (char*) malloc(sizeof(char) * strlen(optarg) + 1);
                memset(gsatArg[2], 0, strlen(optarg) + 1);
                strncpy(gsatArg[2], optarg, strlen(optarg));
                break;
            case 't':
                nbThread = atoi(optarg);
                break;
            case '?':
                help(argv[0]); 
        }
    }

    if(!optI) {
        help(argv[0]);
    }

    //Init MPI
    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
  
    GSATThreadMPI* gsat = new GSATThreadMPI(nbThread, 3, gsatArg);
    if(world_rank == 0) {
        printf("c nbProcessors: %d\n", world_size);
        printf("c nbThreads: %d\n", nbThread);
        printf("c nbVariables: %d\n", gsat->getNbVariables());
        printf("c nbClauses: %d\n", gsat->getNbClauses());
    }
    gsat->solve();
    
    MPI_Finalize();

}

void mpiWait(GSATThreadMPI* gsat) {
    int buff;
    MPI_Recv(&buff, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    if(buff != world_rank) {
        gsat->isEnd();
    }
}

void mpiNotify(int rank) {
    for(int i = 0; i < world_size; i++) {
        MPI_Send(&rank, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
    }
}