/**
 * Compile
 *  mpic++ MPI_Send_Recv.cpp -Wall -pedantic -Wpedantic -o MPI_Send_Recv
 * Usage
 *  mpirun -n 4 ./MPI_Send_Recv
 * 
 * Note: An easy way to finde the location of the `mpi.h` file is
 *  mpic++ --showme:compile
 * which might be usefull to configure the intellicense.
 */

#include <iostream>
#include <sstream>
#include <mpi.h>

/** min of two ints (fine for an example) */
int min(int a, int b) { return a < b ? a : b; }

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

    // Initialize MPI (always required)
    MPI_Init(nullptr, nullptr);

    // Allocate MPI Settings
    int mpi_size;       /*< Number of processes */
    int mpi_rank;       /*< This process rank (a.k.a. the MPI process ID) */
    // Set/Get MPI Settings
    MPI_Comm_size(MPI_COMM_WORLD, &mpi_size);
    MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);

    // Write MPI info to stdout
    std::cout << "Hello World, i am rank " << mpi_rank << " of " << mpi_size
        << " processes." << std::endl;

    // setting this to small truncates the message (Try it, works fine.)
    constexpr int max_size = 128;   /*< Maximum message length */
    // Distinguish between rank 0 and the rest
    if (mpi_rank == 0) {
        // Send a different message to all other ranks
        for (int rank = 1; rank < mpi_size; ++rank) {
            // Build message
            std::ostringstream message;
            message << "Hello rank nr. " << rank;
            std::string sendbuf = message.str();

            // Send message
            MPI_Send(sendbuf.c_str(), min(max_size, sendbuf.size()),
                     MPI_CHAR, rank, sendbuf.size(), MPI_COMM_WORLD);
            // Note: the tag beeing set to the string length (hacky, but :-})
        }

        // Report your done
        std::cout << "Done: rank 0 send all messages." << std::endl;
    } else {
        char recvbuf[max_size]; /*< MPI_Recv buffer, e.g. memory to write to */
        MPI_Status status;      /*< MPI Status object, e.g. communication info */

        // Each rank listens for a single message from rank 0
        MPI_Recv(recvbuf, max_size,
                 MPI_CHAR, 0, MPI_ANY_TAG, MPI_COMM_WORLD, &status);

        if (status.MPI_ERROR) {
            std::cerr << "Error: Recv reported an Error?!" << std::endl;
        } else {
            // Ensure C-style string is terminated and of propper size as
            // the tag encodes the string length.
            // Should be null terminated, but better save than sorry.
            recvbuf[min(max_size - 1, status.MPI_TAG)] = '\0';
            // Print receved message to console
            std::cout << "Recv: " << recvbuf << std::endl;
        }
    }

    // Shutdown MPI (always required)
    MPI_Finalize();

    return 0;
}
