/*
 * Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2011 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2010      Oracle and/or its affiliates.  All rights reserved.
 * Copyright (c) 2014-2016 Research Organization for Information Science
 *                         and Technology (RIST). 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 <stdio.h>
#include <string.h>

#include "pmix_common.h"

#include "src/include/pmix_globals.h"
#include "src/threads/pmix_tsd.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_name_fns.h"
#include "src/util/pmix_printf.h"

#define PMIX_PRINT_NAME_ARGS_MAX_SIZE 300
#define PMIX_PRINT_NAME_ARG_NUM_BUFS  16

#define PMIX_SCHEMA_DELIMITER_CHAR  '.'
#define PMIX_SCHEMA_WILDCARD_CHAR   '*'
#define PMIX_SCHEMA_WILDCARD_STRING "*"
#define PMIX_SCHEMA_INVALID_CHAR    '$'
#define PMIX_SCHEMA_INVALID_STRING  "$"

static bool fns_init = false;

static pmix_tsd_key_t print_args_tsd_key;
char *pmix_print_args_null = "NULL";
typedef struct {
    char *buffers[PMIX_PRINT_NAME_ARG_NUM_BUFS];
    int cntr;
} pmix_print_args_buffers_t;

static void buffer_cleanup(void *value)
{
    int i;
    pmix_print_args_buffers_t *ptr;

    if (NULL != value) {
        ptr = (pmix_print_args_buffers_t *) value;
        for (i = 0; i < PMIX_PRINT_NAME_ARG_NUM_BUFS; i++) {
            free(ptr->buffers[i]);
        }
        free(ptr);
    }
}

static pmix_print_args_buffers_t *get_print_name_buffer(void)
{
    pmix_print_args_buffers_t *ptr;
    int ret, i;

    if (!fns_init) {
        /* setup the print_args function */
        if (PMIX_SUCCESS != (ret = pmix_tsd_key_create(&print_args_tsd_key, buffer_cleanup))) {
            PMIX_ERROR_LOG(ret);
            return NULL;
        }
        fns_init = true;
    }

    ret = pmix_tsd_getspecific(print_args_tsd_key, (void **) &ptr);
    if (PMIX_SUCCESS != ret)
        return NULL;

    if (NULL == ptr) {
        ptr = (pmix_print_args_buffers_t *) malloc(sizeof(pmix_print_args_buffers_t));
        for (i = 0; i < PMIX_PRINT_NAME_ARG_NUM_BUFS; i++) {
            ptr->buffers[i] = (char *) malloc((PMIX_PRINT_NAME_ARGS_MAX_SIZE + 1) * sizeof(char));
        }
        ptr->cntr = 0;
        ret = pmix_tsd_setspecific(print_args_tsd_key, (void *) ptr);
    }

    return (pmix_print_args_buffers_t *) ptr;
}

static char *print_args(char *ns, pmix_rank_t rnk)
{
    pmix_print_args_buffers_t *ptr;
    char *rank;
    int index;

    /* get the next buffer */
    ptr = get_print_name_buffer();
    if (NULL == ptr) {
        PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
        return pmix_print_args_null;
    }

    /* protect against NULL nspace */
    if (NULL == ns) {
        index = ptr->cntr;
        pmix_snprintf(ptr->buffers[index], PMIX_PRINT_NAME_ARGS_MAX_SIZE, "[NO-NAME]");
        ptr->cntr++;
        if (PMIX_PRINT_NAME_ARG_NUM_BUFS == ptr->cntr) {
            ptr->cntr = 0;
        }
        return ptr->buffers[index];
    }

    rank = pmix_util_print_rank(rnk);

    index = ptr->cntr;
    pmix_snprintf(ptr->buffers[index], PMIX_PRINT_NAME_ARGS_MAX_SIZE, "[%s,%s]", ns, rank);
    ptr->cntr++;
    if (PMIX_PRINT_NAME_ARG_NUM_BUFS == ptr->cntr) {
        ptr->cntr = 0;
    }

    return ptr->buffers[index];
}

char *pmix_util_print_name_args(const pmix_proc_t *name)
{
    if (NULL == name) {
        return print_args(NULL, PMIX_RANK_UNDEF);
    }

    return print_args((char *) name->nspace, name->rank);
}

char *pmix_util_print_pname_args(const pmix_name_t *name)
{
    if (NULL == name) {
        return print_args(NULL, PMIX_RANK_UNDEF);
    }

    return print_args((char *) name->nspace, name->rank);
}

char *pmix_util_print_rank(const pmix_rank_t vpid)
{
    pmix_print_args_buffers_t *ptr;
    int index;

    ptr = get_print_name_buffer();

    if (NULL == ptr) {
        PMIX_ERROR_LOG(PMIX_ERR_OUT_OF_RESOURCE);
        return pmix_print_args_null;
    }

    index = ptr->cntr;
    if (PMIX_RANK_UNDEF == vpid) {
        pmix_snprintf(ptr->buffers[index], PMIX_PRINT_NAME_ARGS_MAX_SIZE, "UNDEF");
    } else if (PMIX_RANK_WILDCARD == vpid) {
        pmix_snprintf(ptr->buffers[index], PMIX_PRINT_NAME_ARGS_MAX_SIZE, "WILDCARD");
    } else {
        pmix_snprintf(ptr->buffers[index], PMIX_PRINT_NAME_ARGS_MAX_SIZE, "%ld", (long) vpid);
    }
    ptr->cntr++;
    if (PMIX_PRINT_NAME_ARG_NUM_BUFS == ptr->cntr) {
        ptr->cntr = 0;
    }
    return ptr->buffers[index];
}

int pmix_util_compare_proc(const void *a, const void *b)
{
    const pmix_proc_t *proc_a = (pmix_proc_t *)a;
    const pmix_proc_t *proc_b = (pmix_proc_t *)b;

    int nspace_dif = strncmp(proc_a->nspace, proc_b->nspace, PMIX_MAX_NSLEN);
    if (nspace_dif != 0)
        return nspace_dif;

    return proc_a->rank - proc_b->rank;
}
