/*
 * Copyright (c) 2008      The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2009      Cisco Systems, Inc.  All rights reserved.
 * Copyright (c) 2011-2013 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2011-2013 Inria.  All rights reserved.
 * Copyright (c) 2011-2013 Université Bordeaux 1
 * Copyright (c) 2014-2015 Research Organization for Information Science
 *                         and Technology (RIST). All rights reserved.
 * Copyright (c) 2017      IBM Corp.  All rights reserved.
 */

#include "ompi_config.h"

#include "ompi/communicator/communicator.h"
#include "ompi/info/info.h"
#include "ompi/mca/topo/base/base.h"


int mca_topo_base_dist_graph_create_adjacent(mca_topo_base_module_t* module,
                                             ompi_communicator_t *comm_old,
                                             int indegree, const int sources[],
                                             const int sourceweights[],
                                             int outdegree,
                                             const int destinations[],
                                             const int destweights[],
                                             opal_info_t *info, int reorder,
                                             ompi_communicator_t **newcomm)
{
    mca_topo_base_comm_dist_graph_2_2_0_t *topo = NULL;
    int err;

    if( OMPI_SUCCESS != (err = ompi_comm_create(comm_old,
                                                comm_old->c_local_group,
                                                newcomm)) ) {
        return err;
    }
    // But if there is an info object, the above call didn't make use
    // of it, so we'll do a dup-with-info to get the final comm and
    // free the above intermediate newcomm:
    if (info && info != &(MPI_INFO_NULL->super)) {
        ompi_communicator_t *intermediate_comm = *newcomm;
        ompi_comm_dup_with_info (intermediate_comm, info, newcomm);
        ompi_comm_free(&intermediate_comm);
    }

    err = OMPI_ERR_OUT_OF_RESOURCE;  /* suppose by default something bad will happens */

    assert( NULL == (*newcomm)->c_topo );

    topo = OBJ_NEW(mca_topo_base_comm_dist_graph_2_2_0_t);
    if( NULL == topo ) {
        goto bail_out;
    }
    topo->in = topo->inw = NULL;
    topo->out = topo->outw = NULL;
    topo->indegree = indegree;
    topo->outdegree = outdegree;
    topo->weighted = !((MPI_UNWEIGHTED == sourceweights) && (MPI_UNWEIGHTED == destweights));

    if (topo->indegree > 0) {
        topo->in = (int*)malloc(sizeof(int) * topo->indegree);
        if (NULL == topo->in) {
            goto bail_out;
        }
        memcpy(topo->in, sources, sizeof(int) * topo->indegree);
        if (MPI_UNWEIGHTED != sourceweights) {
            topo->inw = (int*)malloc(sizeof(int) * topo->indegree);
            if( NULL == topo->inw ) {
                goto bail_out;
            }
            memcpy( topo->inw, sourceweights, sizeof(int) * topo->indegree );
        }
    }

    if (topo->outdegree > 0) {
        topo->out = (int*)malloc(sizeof(int) * topo->outdegree);
        if (NULL == topo->out) {
            goto bail_out;
        }
        memcpy(topo->out, destinations, sizeof(int) * topo->outdegree);
        topo->outw = NULL;
        if (MPI_UNWEIGHTED != destweights) {
            if (topo->outdegree > 0) {
                topo->outw = (int*)malloc(sizeof(int) * topo->outdegree);
                if (NULL == topo->outw) {
                    goto bail_out;
                }
                memcpy(topo->outw, destweights, sizeof(int) * topo->outdegree);
            }
        }
    }

    (*newcomm)->c_topo                 = module;
    (*newcomm)->c_topo->mtc.dist_graph = topo;
    (*newcomm)->c_topo->reorder        = reorder;
    (*newcomm)->c_flags               |= OMPI_COMM_DIST_GRAPH;

    return OMPI_SUCCESS;

 bail_out:
    if (NULL != topo) {
        if( NULL != topo->in ) free(topo->in);
        if( MPI_UNWEIGHTED != sourceweights ) {
            if( NULL != topo->inw ) free(topo->inw);
        }
        if( NULL != topo->out ) free(topo->out);
        if( MPI_UNWEIGHTED != destweights ) {
            if( NULL != topo->outw ) free(topo->outw);
        }
        OBJ_RELEASE(topo);
    }
    ompi_comm_free(newcomm);
    return err;
}
