/*
 * Copyright (C) by Argonne National Laboratory
 *     See COPYRIGHT in top-level directory
 */

#include "hydra.h"
#include "bsci.h"

HYD_status HYDU_env_to_str(struct HYD_env *env, char **str)
{
    int i;
    char *tmp[10];
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    i = 0;
    tmp[i++] = MPL_strdup("'");
    tmp[i++] = MPL_strdup(env->env_name);
    tmp[i++] = MPL_strdup("=");
    tmp[i++] = env->env_value ? MPL_strdup(env->env_value) : MPL_strdup("");
    tmp[i++] = MPL_strdup("'");
    tmp[i++] = NULL;

    status = HYDU_str_alloc_and_join(tmp, str);
    HYDU_ERR_POP(status, "unable to join strings\n");

    for (i = 0; tmp[i]; i++)
        MPL_free(tmp[i]);

  fn_exit:
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}


HYD_status HYDU_list_inherited_env(struct HYD_env **env_list)
{
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    *env_list = NULL;
    for (int i = 0; environ[i]; i++) {
        char *env_str, *env_name;
        int should_inherit;

        env_str = MPL_strdup(environ[i]);
        env_name = strtok(env_str, "=");

        status = HYDT_bsci_query_env_inherit(env_name, &should_inherit);
        MPL_free(env_str);
        HYDU_ERR_POP(status, "error querying environment propagation\n");

        if (should_inherit) {
            status = HYDU_append_env_str_to_list(environ[i], env_list);
            HYDU_ERR_POP(status, "unable to add env to list\n");
        }
    }

  fn_exit:
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}


struct HYD_env *HYDU_env_list_dup(struct HYD_env *env)
{
    struct HYD_env *tenv, *run;
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    run = env;
    tenv = NULL;
    while (run) {
        status = HYDU_append_env_to_list(run->env_name, run->env_value, &tenv);
        HYDU_ERR_POP(status, "unable to add env to list\n");
        run = run->next;
    }

  fn_exit:
    HYDU_FUNC_EXIT();
    return tenv;

  fn_fail:
    tenv = NULL;
    goto fn_exit;
}


HYD_status HYDU_env_create(struct HYD_env **env, const char *env_name, const char *env_value)
{
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    HYDU_MALLOC_OR_JUMP(*env, struct HYD_env *, sizeof(struct HYD_env), status);
    (*env)->env_name = MPL_strdup(env_name);
    (*env)->env_value = env_value ? MPL_strdup(env_value) : NULL;
    (*env)->next = NULL;

  fn_exit:
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}


HYD_status HYDU_env_free(struct HYD_env *env)
{
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    MPL_free(env->env_name);
    MPL_free(env->env_value);
    MPL_free(env);

    HYDU_FUNC_EXIT();
    return status;
}


HYD_status HYDU_env_free_list(struct HYD_env * env)
{
    struct HYD_env *run, *tmp;
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    run = env;
    while (run) {
        tmp = run->next;
        HYDU_env_free(run);
        run = tmp;
    }

    HYDU_FUNC_EXIT();
    return status;
}


struct HYD_env *HYDU_env_lookup(char *env_name, struct HYD_env *env_list)
{
    struct HYD_env *run;

    HYDU_FUNC_ENTER();

    run = env_list;
    while (run->next) {
        if (!strcmp(run->env_name, env_name))
            goto fn_exit;
        run = run->next;
    }
    run = NULL;

  fn_exit:
    HYDU_FUNC_EXIT();
    return run;
}

HYD_status HYDU_append_env_to_list(const char *env_name, const char *env_value,
                                   struct HYD_env ** env_list)
{
    struct HYD_env *run, *tenv;
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    status = HYDU_env_create(&tenv, env_name, env_value);
    HYDU_ERR_POP(status, "unable to create env structure\n");

    tenv->next = NULL;

    /* Add the structure to the end of the list */
    if (*env_list == NULL) {
        *env_list = tenv;
    } else {
        run = *env_list;

        while (1) {
            if (!strcmp(run->env_name, env_name)) {
                /* If we found an entry for this environment variable, just update it */
                MPL_free(run->env_value);
                if (tenv->env_value != NULL) {
                    run->env_value = MPL_strdup(tenv->env_value);
                } else {
                    run->env_value = NULL;
                }

                MPL_free(tenv->env_name);
                MPL_free(tenv->env_value);
                MPL_free(tenv);

                break;
            } else if (run->next == NULL) {
                run->next = tenv;
                break;
            }
            run = run->next;
        }
    }

  fn_exit:
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}

HYD_status HYDU_append_env_str_to_list(const char *str, struct HYD_env **env_list)
{
    char *my_str = NULL;
    char *env_name, *env_value;
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    my_str = env_value = MPL_strdup(str);
    /* don't use strtok, it will mangle env values that contain '=' */
    env_name = MPL_strsep(&env_value, "=");
    HYDU_ASSERT(env_name != NULL, status);

    status = HYDU_append_env_to_list(env_name, env_value, env_list);
    HYDU_ERR_POP(status, "unable to append env to list\n");

  fn_exit:
    MPL_free(my_str);
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}

HYD_status HYDU_putenv(struct HYD_env *env, HYD_env_overwrite_t overwrite)
{
    char *tmp[10], *str;
    int i;
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    /* If the overwrite flag is false, just exit */
    if (MPL_env2str(env->env_name, (const char **) &str) && overwrite == HYD_ENV_OVERWRITE_FALSE)
        goto fn_exit;

    i = 0;
    tmp[i++] = MPL_strdup(env->env_name);
    tmp[i++] = MPL_strdup("=");
    tmp[i++] = env->env_value ? MPL_strdup(env->env_value) : MPL_strdup("");
    tmp[i++] = NULL;
    status = HYDU_str_alloc_and_join(tmp, &str);
    HYDU_ERR_POP(status, "unable to join strings\n");

    MPL_putenv(str);

    for (i = 0; tmp[i]; i++)
        MPL_free(tmp[i]);

  fn_exit:
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}


HYD_status HYDU_putenv_list(struct HYD_env *env_list, HYD_env_overwrite_t overwrite)
{
    struct HYD_env *env;
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    for (env = env_list; env; env = env->next) {
        status = HYDU_putenv(env, overwrite);
        HYDU_ERR_POP(status, "putenv failed\n");
    }

  fn_exit:
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}


HYD_status HYDU_comma_list_to_env_list(char *str, struct HYD_env **env_list)
{
    char *env_name;
    HYD_status status = HYD_SUCCESS;

    HYDU_FUNC_ENTER();

    env_name = strtok(str, ",");
    do {
        status = HYDU_append_env_to_list(env_name, NULL, env_list);
        HYDU_ERR_POP(status, "unable to add env to list\n");
    } while ((env_name = strtok(NULL, ",")));

  fn_exit:
    HYDU_FUNC_EXIT();
    return status;

  fn_fail:
    goto fn_exit;
}
