#include <mpi.h>
#include "main.h"
#include "master.h"

int master_init(void) {
   // Find out how many processes there are in the default communicator
    int ntasks;
    MPI_Comm_size(MPI_COMM_WORLD, &ntasks);
    return ntasks;
}

void master_run(int ntasks, int items_per_task) {
    int rank;
    unit_work_t *work;
    unit_result_t result;
    MPI_Status status;

    // Seed the slaves; send one unit of work to each slave.
    for(rank = 1; rank < ntasks; ++rank) {
        // Get the next items of work to do
        work = get_next_work_item();

        // Send it to each rank
        MPI_Send(work,             // message buffer
                items_per_task,    // size of data array
                MPI_DOUBLE,        // data item is an integer
                rank,              // destination process rank
                WORK_TAG,          // user chosen message tag
                MPI_COMM_WORLD);   // default communicator
    }

    // Loop over getting new work requests until there is no work to be done.
    while((work = get_next_work_item()) != NULL) {
        // Receive results from a slave
        MPI_Recv(&result,          // message buffer
                1,                 // one data item
                MPI_DOUBLE,        // of type double real
                MPI_ANY_SOURCE,    // receive from any sender
                MPI_ANY_TAG,       // any type of message
                MPI_COMM_WORLD,    // default communicator
                &status);          // info about the received message

        // Send the slave a new work unit
        MPI_Send(work,             // message buffer
                items_per_task,    // size of data array
                MPI_DOUBLE,        // data item is an integer
                status.MPI_SOURCE, // to who we just received from
                WORK_TAG,          // user chosen message tag
                MPI_COMM_WORLD);   // default communicator
    }

    // There's no more work to be done, so receive all the outstanding results
    // from the slaves.
    for (rank = 1; rank < ntasks; ++rank) {
        MPI_Recv(&result, 1, MPI_DOUBLE, MPI_ANY_SOURCE, MPI_ANY_TAG,
                MPI_COMM_WORLD, &status);
    }
}

void master_destroy(int ntasks) {
    // Tell all slaves to exit by sending an empty message with the DIE_TAG.
    for (int rank = 1; rank < ntasks; ++rank) {
        MPI_Send(0, 0, MPI_DOUBLE, rank, DIE_TAG, MPI_COMM_WORLD);
    }
}
