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

#define DISPLAY_RANK 0

using namespace std;

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

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

    if(argc < 3) {
        help(argv[0]);
    }

    //Init MPI
    int world_size;
    int world_rank;
    MPI_Init(NULL, NULL);
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);

    GSAT* gsat = new GSAT();
    gsat->setParameters(argc,argv);
    gsat->initialize();
    if(world_rank == 0) {
        printf("c nbProcess: %d\n", world_size);
        printf("c nbVariables: %d\n",gsat->getNbVariables());
        printf("c nbClauses: %d\n",gsat->getNbClauses());
    }
    double startTime = gsat->realTime();
    srand(getpid());

    //Attente d'un message de fin
    int syncBuff;
    MPI_Request sync, finish;
    MPI_Irecv(&syncBuff, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, &sync);

    //Calcul  
    bool end = false;
    bool stop = false;
    while(!end && !stop){
        //Pour eviter que les processeurs effectue tous les meme calculs
        int fill = rand() % 4 + 2;
        int h = rand() % 7;
        end = gsat->start(fill, h);
        //Affiche calcul
        if(end) {
            printf(CYAN);
        }
        printf("c [pid:%6d][process:%2d][iteration:%4d][fill:%d][heuristic:%d]Satisfied clauses (begin: %d)(end:%d)\n",
            getpid(),
            world_rank,
            gsat->getNbIterations(),
            gsat->getHeuristicFill(),
            gsat->getHeuristicSolve(),
            gsat->getNbSatisfiedClausesFill(),
            gsat->getNbSatisfiedClausesSolve());
        if(end) {
            printf(RESET);
        }
        //Regarde si quelqu'un a trouver
        int flag;
        MPI_Test(&sync, &flag, MPI_STATUS_IGNORE);
        if(flag) {
            stop = true;
        }
    }
    fflush(stdout);

    //Si on est le processus qui à trouvé
    if(end) {
        //Reverifie si quelqu'un d'autre a trouver avant
        int flag;
        MPI_Test(&sync, &flag, MPI_STATUS_IGNORE);
        if(flag) {
            stop = true;
        }
        if(!stop) {
            //Avertit les autres
            for(int i = 0; i < world_size; i++) {
                MPI_Isend(&world_rank, 1, MPI_INT, i, 0, MPI_COMM_WORLD, &finish);
            }
            //Envoi les infos de resolution au processus de rank 0
            int pid;
            double time;
            unsigned int nbIte, heuriFill, heuriSolve, satisfFill, satisfSolve;
            pid = getpid();
            MPI_Isend(&pid, 1, MPI_INT, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
            MPI_Isend(&world_rank, 1, MPI_INT, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
            time = gsat->realTime() - startTime;
            MPI_Isend(&time, 1, MPI_DOUBLE, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
            nbIte = gsat->getNbIterations();
            MPI_Isend(&nbIte, 1, MPI_UNSIGNED, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
            heuriFill = gsat->getHeuristicFill();
            MPI_Isend(&heuriFill, 1, MPI_UNSIGNED, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
            heuriSolve = gsat->getHeuristicSolve();
            MPI_Isend(&heuriSolve, 1, MPI_UNSIGNED, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
            satisfFill = gsat->getNbSatisfiedClausesFill();
            MPI_Isend(&satisfFill, 1, MPI_UNSIGNED, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
            satisfSolve = gsat->getNbSatisfiedClausesSolve();
            MPI_Isend(&satisfSolve, 1, MPI_UNSIGNED, DISPLAY_RANK, 0, MPI_COMM_WORLD, &finish);
        }
    }

    //L'affichage est fait par le processus un seul processus
    if(world_rank == DISPLAY_RANK) {
        //Attend las infos venants de processus qui à trouvé
        int pid, rank;
        double time;
        unsigned int nbIte, heuriFill, heuriSolve, satisfFill, satisfSolve;
        MPI_Recv(&pid, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(&rank, 1, MPI_INT, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(&time, 1, MPI_DOUBLE, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(&nbIte, 1, MPI_UNSIGNED, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(&heuriFill, 1, MPI_UNSIGNED, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(&heuriSolve, 1, MPI_UNSIGNED, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(&satisfFill, 1, MPI_UNSIGNED, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        MPI_Recv(&satisfSolve, 1, MPI_UNSIGNED, MPI_ANY_SOURCE, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        //Attend pour que tous les autres porcessus se termine
        sleep(1);
        //Affiche le resultat
        printf("------------------------------------------------------------------------------------------------------\n");
        printf(GREEN);
        printf("s SATISFIABLE\n");
        printf(YELLOW);
        printf("c real time : %.4f seconds\n", time);
        printf("c [pid:%6d][process:%2d][iteration:%4d][fill:%d][heuristic:%d]Satisfied clauses (begin: %d)(end:%d)\n",
            pid,
            rank,
            nbIte,
            heuriFill,
            heuriSolve,
            satisfFill,
            satisfSolve);
        printf(RESET); 
    }

    MPI_Finalize();    

}