/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
 * Copyright (c) 2018      Triad National Security, LLC.  All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

#if !defined(OMPI_INSTANCE_H)
#define OMPI_INSTANCE_H

#include "opal/class/opal_object.h"
#include "opal/class/opal_hash_table.h"
#include "opal/util/info_subscriber.h"
#include "ompi/errhandler/errhandler.h"
#include "opal/mca/threads/mutex.h"
#include "ompi/communicator/comm_request.h"

#include "mpi.h"
#include "ompi/mca/coll/coll.h"
#include "ompi/info/info.h"
#include "ompi/proc/proc.h"

struct ompi_group_t;

struct ompi_instance_t {
    opal_infosubscriber_t  super;
    opal_mutex_t           s_lock;
    int                    i_thread_level;
    char                   i_name[MPI_MAX_OBJECT_NAME];
    uint32_t               i_flags;

    /* Attributes */
    opal_hash_table_t     *i_keyhash;

    /* index in Fortran <-> C translation array (for when I get around
     * to implementing fortran support-- UGH) */
    int                    i_f_to_c_index;

    ompi_errhandler_t     *error_handler;
    ompi_errhandler_type_t errhandler_type;
};

typedef struct ompi_instance_t ompi_instance_t;

OBJ_CLASS_DECLARATION(ompi_instance_t);

/* Define for the preallocated size of the predefined handle.
 * Note that we are using a pointer type as the base memory chunk
 * size so when the bitness changes the size of the handle changes.
 * This is done so we don't end up needing a structure that is
 * incredibly larger than necessary because of the bitness.
 *
 * This padding mechanism works as a (likely) compile time check for when the
 * size of the ompi_communicator_t exceeds the predetermined size of the
 * ompi_predefined_communicator_t. It also allows us to change the size of
 * the ompi_communicator_t without impacting the size of the
 * ompi_predefined_communicator_t structure for some number of additions.
 *
 * Note: we used to define the PAD as a multiple of sizeof(void*).
 * However, this makes a different size PAD, depending on
 * sizeof(void*).  In some cases
 * (https://github.com/open-mpi/ompi/issues/3610), 32 bit builds can
 * run out of space when 64 bit builds are still ok.  So we changed to
 * use just a naked byte size.  As a rule of thumb, however, the size
 * should probably still be a multiple of 8 so that it has the
 * possibility of being nicely aligned.
 *
 * As an example:
 * If the size of ompi_communicator_t is less than the size of the _PAD then
 * the _PAD ensures that the size of the ompi_predefined_communicator_t is
 * whatever size is defined below in the _PAD macro.
 * However, if the size of the ompi_communicator_t grows larger than the _PAD
 * (say by adding a few more function pointers to the structure) then the
 * 'padding' variable will be initialized to a large number often triggering
 * a 'array is too large' compile time error. This signals two things:
 * 1) That the _PAD should be increased.
 * 2) That users need to be made aware of the size change for the
 *    ompi_predefined_communicator_t structure.
 *
 * Q: So you just made a change to communicator structure, do you need to adjust
 * the PREDEFINED_COMMUNICATOR_PAD macro?
 * A: Most likely not, but it would be good to check.
 */
#define PREDEFINED_INSTANCE_PAD 512

struct ompi_predefined_instance_t {
    ompi_instance_t instance;
    char padding[PREDEFINED_INSTANCE_PAD - sizeof(ompi_instance_t)];
};
typedef struct ompi_predefined_instance_t ompi_predefined_instance_t;

/**
 * @brief NULL instance
 */
OMPI_DECLSPEC extern ompi_predefined_instance_t ompi_mpi_instance_null;

OMPI_DECLSPEC extern opal_pointer_array_t ompi_instance_f_to_c_table;

extern ompi_instance_t *ompi_mpi_instance_default;

/**
 * @brief Bring up the bare minimum infrastructure to support pre-session_init functions.
 *
 * List of subsystems initialized:
 *  - OPAL (including class system)
 *  - Error handlers
 *  - MPI Info
 */
int ompi_mpi_instance_retain (void);

/**
 * @brief Release (and possibly teardown) pre-session_init infrastructure.
 */
void ompi_mpi_instance_release (void);

/**
 * @brief Create a new MPI instance
 *
 * @param[in]    ts_level  thread support level (see mpi.h)
 * @param[in]    info      info object
 * @param[in]    errhander errhandler to set on the instance
 */
OMPI_DECLSPEC int ompi_mpi_instance_init (int ts_level, opal_info_t *info, ompi_errhandler_t *errhandler,
                                          ompi_instance_t **instance, int argc, char **argv);

/**
 * @brief Destroy an MPI instance and set it to MPI_SESSION_NULL
 */
OMPI_DECLSPEC int ompi_mpi_instance_finalize (ompi_instance_t **instance);


/**
 * @brief Add a function to the finalize chain. Note this function will be called
 *        when the last instance has been destroyed.
 */
#define ompi_mpi_instance_append_finalize opal_finalize_register_cleanup

/**
 * @brief Get an MPI group object for a named process set.
 *
 * @param[in] instance   MPI instance (session)
 * @param[in] pset_name  Name of process set (includes mpi://world, mpi://self)
 * @param[out group_out  New MPI group
 */
OMPI_DECLSPEC int ompi_group_from_pset (ompi_instance_t *instance, const char *pset_name, struct ompi_group_t **group_out);

OMPI_DECLSPEC int ompi_instance_get_num_psets (ompi_instance_t *instance, int *npset_names);
OMPI_DECLSPEC int ompi_instance_get_nth_pset (ompi_instance_t *instance, int n, int *len, char *pset_name);
OMPI_DECLSPEC int ompi_instance_get_pset_info (ompi_instance_t *instance, const char *pset_name, opal_info_t **info_used);

/**
 * @brief current number of active instances
 */
extern opal_atomic_int32_t ompi_instance_count;

static inline int ompi_instance_invalid (const ompi_instance_t* instance)
{
    if ((NULL == instance) || (MPI_SESSION_NULL == instance))
        return true;
    else
        return false;
}

#endif /* !defined(OMPI_INSTANCE_H) */
