/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
 * Copyright (c) 2012-2015 Los Alamos National Security, LLC. All rights
 *                         reserved.
 * Copyright (c) 2015      Cisco Systems, Inc.  All rights reserved.
 * Copyright (c) 2016-2020 Intel, Inc.  All rights reserved.
 * Copyright (c) 2021-2022 Nanook Consulting.  All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

#include "src/include/pmix_config.h"

#include "pmix_common.h"
#include "src/util/pmix_output.h"

#include "pmix_mca_base_framework.h"
#include "pmix_mca_base_var.h"
#include "src/mca/base/pmix_base.h"

bool pmix_mca_base_framework_is_registered(struct pmix_mca_base_framework_t *framework)
{
    return !!(framework->framework_flags & PMIX_MCA_BASE_FRAMEWORK_FLAG_REGISTERED);
}

bool pmix_mca_base_framework_is_open(struct pmix_mca_base_framework_t *framework)
{
    return !!(framework->framework_flags & PMIX_MCA_BASE_FRAMEWORK_FLAG_OPEN);
}

static void framework_open_output(struct pmix_mca_base_framework_t *framework)
{
    if (0 < framework->framework_verbose) {
        if (-1 == framework->framework_output) {
            framework->framework_output = pmix_output_open(NULL);
        }
        pmix_output_set_verbosity(framework->framework_output, framework->framework_verbose);
    } else if (-1 != framework->framework_output) {
        pmix_output_close(framework->framework_output);
        framework->framework_output = -1;
    }
}

static void framework_close_output(struct pmix_mca_base_framework_t *framework)
{
    if (-1 != framework->framework_output) {
        pmix_output_close(framework->framework_output);
        framework->framework_output = -1;
    }
}

int pmix_mca_base_framework_register(struct pmix_mca_base_framework_t *framework,
                                     pmix_mca_base_register_flag_t flags)
{
    char *desc;
    int ret;

    assert(NULL != framework);

    framework->framework_refcnt++;

    if (pmix_mca_base_framework_is_registered(framework)) {
        return PMIX_SUCCESS;
    }

    PMIX_CONSTRUCT(&framework->framework_components, pmix_list_t);
    PMIX_CONSTRUCT(&framework->framework_failed_components, pmix_list_t);

    if (framework->framework_flags & PMIX_MCA_BASE_FRAMEWORK_FLAG_NO_DSO) {
        flags |= PMIX_MCA_BASE_REGISTER_STATIC_ONLY;
    }

    if (!(PMIX_MCA_BASE_FRAMEWORK_FLAG_NOREGISTER & framework->framework_flags)) {
        /* register this framework with the MCA variable system */
        ret = pmix_mca_base_var_group_register(framework->framework_project,
                                               framework->framework_name, NULL,
                                               framework->framework_description);
        if (0 > ret) {
            return ret;
        }

        ret = asprintf(&desc,
                       "Default selection set of components for the %s framework (<none>"
                       " means use all components that can be found)",
                       framework->framework_name);
        if (0 > ret) {
            return PMIX_ERR_OUT_OF_RESOURCE;
        }

        ret = pmix_mca_base_var_register(framework->framework_project, framework->framework_name,
                                         NULL, NULL, desc, PMIX_MCA_BASE_VAR_TYPE_STRING,
                                         &framework->framework_selection);
        free(desc);
        if (0 > ret) {
            return ret;
        }

        /* register a verbosity variable for this framework */
        ret = asprintf(&desc, "Verbosity level for the %s framework (default: 0)",
                       framework->framework_name);
        if (0 > ret) {
            return PMIX_ERR_OUT_OF_RESOURCE;
        }

        framework->framework_verbose = PMIX_MCA_BASE_VERBOSE_ERROR;
        ret = pmix_mca_base_framework_var_register(framework, "verbose", desc,
                                                   PMIX_MCA_BASE_VAR_TYPE_INT,
                                                   &framework->framework_verbose);
        free(desc);
        if (0 > ret) {
            return ret;
        }

        /* check the initial verbosity and open the output if necessary. we
           will recheck this on open */
        framework_open_output(framework);

        /* register framework variables */
        if (NULL != framework->framework_register) {
            ret = framework->framework_register(flags);
            if (PMIX_SUCCESS != ret) {
                return ret;
            }
        }

        /* register components variables */
        ret = pmix_mca_base_framework_components_register(framework, flags);
        if (PMIX_SUCCESS != ret) {
            return ret;
        }
    }

    framework->framework_flags |= PMIX_MCA_BASE_FRAMEWORK_FLAG_REGISTERED;

    /* framework did not provide a register function */
    return PMIX_SUCCESS;
}

int pmix_mca_base_framework_open(struct pmix_mca_base_framework_t *framework,
                                 pmix_mca_base_open_flag_t flags)
{
    int ret;

    assert(NULL != framework);

    /* register this framework before opening it */
    ret = pmix_mca_base_framework_register(framework, PMIX_MCA_BASE_REGISTER_DEFAULT);
    if (PMIX_SUCCESS != ret) {
        return ret;
    }

    /* check if this framework is already open */
    if (pmix_mca_base_framework_is_open(framework)) {
        return PMIX_SUCCESS;
    }

    if (PMIX_MCA_BASE_FRAMEWORK_FLAG_NOREGISTER & framework->framework_flags) {
        flags |= PMIX_MCA_BASE_OPEN_FIND_COMPONENTS;

        if (PMIX_MCA_BASE_FRAMEWORK_FLAG_NO_DSO & framework->framework_flags) {
            flags |= PMIX_MCA_BASE_OPEN_STATIC_ONLY;
        }
    }

    /* check the verbosity level and open (or close) the output */
    framework_open_output(framework);

    if (NULL != framework->framework_open) {
        ret = framework->framework_open(flags);
    } else {
        ret = pmix_mca_base_framework_components_open(framework, flags);
    }

    if (PMIX_SUCCESS != ret) {
        framework->framework_refcnt--;
    } else {
        framework->framework_flags |= PMIX_MCA_BASE_FRAMEWORK_FLAG_OPEN;
    }

    return ret;
}

int pmix_mca_base_framework_close(struct pmix_mca_base_framework_t *framework)
{
    bool is_open = pmix_mca_base_framework_is_open(framework);
    bool is_registered = pmix_mca_base_framework_is_registered(framework);
    int ret, group_id;

    assert(NULL != framework);

    if (!(is_open || is_registered)) {
        return PMIX_SUCCESS;
    }

    assert(framework->framework_refcnt);
    if (--framework->framework_refcnt) {
        return PMIX_SUCCESS;
    }

    /* find and deregister all component groups and variables */
    group_id = pmix_mca_base_var_group_find(framework->framework_project, framework->framework_name,
                                            NULL);
    if (0 <= group_id) {
        (void) pmix_mca_base_var_group_deregister(group_id);
    }

    /* close the framework and all of its components */
    if (is_open) {
        if (NULL != framework->framework_close) {
            ret = framework->framework_close();
        } else {
            ret = pmix_mca_base_framework_components_close(framework, NULL);
        }

        if (PMIX_SUCCESS != ret) {
            return ret;
        }
    } else {
        pmix_list_item_t *item;
        while (NULL != (item = pmix_list_remove_first(&framework->framework_components))) {
            pmix_mca_base_component_list_item_t *cli;
            cli = (pmix_mca_base_component_list_item_t *) item;
            pmix_mca_base_component_unload(cli->cli_component, framework->framework_output);
            PMIX_RELEASE(item);
        }
        ret = PMIX_SUCCESS;
    }

    framework->framework_flags &= ~(PMIX_MCA_BASE_FRAMEWORK_FLAG_REGISTERED
                                    | PMIX_MCA_BASE_FRAMEWORK_FLAG_OPEN);

    PMIX_DESTRUCT(&framework->framework_components);
    PMIX_LIST_DESTRUCT(&framework->framework_failed_components);

    framework_close_output(framework);

    return ret;
}
