// SPDX-License-Identifier: 0BSD
/*!
 * @file
 * @license{
 * BSD Zero Clause License
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 * }
 */
/*==============================================================================
    INCLUDE
==============================================================================*/
// Own header
#include "fmpi_futhark.h"
// C Standard Library
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// External
// Internal
#include "fmpi_ctx.h"
#include "fmpi_mpi.h"
#include "internal/fmpi_fut.h"
#include "internal/fmpi_generic.h"
/*==============================================================================
    STRUCT
==============================================================================*/
/*==============================================================================
    PUBLIC FUNCTION
==============================================================================*/
/*------------------------------------------------------------------------------
    fmpi_futhark_init()
------------------------------------------------------------------------------*/
int fmpi_futhark_init(struct fmpi_ctx * const ctx)
{
    assert(ctx != NULL);
    assert(ctx->futhark == NULL);
    assert(ctx->futhark_cfg == NULL);

    ctx->futhark_cfg = futhark_context_config_new();
    if(ctx->futhark_cfg == NULL) {
        fprintf(stderr, "[FUTHARK ERROR rank=%d] futhark_context_config_new() failed!\n", ctx->mpi->rank);
        return ctx->mpi->futhark_err_class;
    }
    ctx->futhark = futhark_context_new(ctx->futhark_cfg);
    char * err_ptr = futhark_context_get_error(ctx->futhark);
    if(ctx->futhark == NULL || err_ptr != NULL) {
        const char * err_str = err_ptr != NULL ? err_ptr : "";
        fprintf(stderr, "[FUTHARK ERROR rank=%d] futhark_context_new() failed! %s\n", ctx->mpi->rank, err_str);
        futhark_context_free(ctx->futhark);
        futhark_context_config_free(ctx->futhark_cfg);
        free(ctx->futhark);
        return ctx->mpi->futhark_err_class;
    }
    return MPI_SUCCESS;
}
/*------------------------------------------------------------------------------
    fmpi_futhark_exit()
------------------------------------------------------------------------------*/
void fmpi_futhark_exit(struct fmpi_ctx * const ctx)
{
    assert(ctx != NULL);
    assert(ctx->futhark_cfg != NULL);
    assert(ctx->futhark != NULL);

    fmpi_futhark_sync(ctx);
    futhark_context_free(ctx->futhark);
    ctx->futhark = NULL;
    futhark_context_config_free(ctx->futhark_cfg);
    ctx->futhark_cfg = NULL;
}
/*------------------------------------------------------------------------------
    fmpi_futhark_sync()
------------------------------------------------------------------------------*/
void fmpi_futhark_sync(struct fmpi_ctx * const ctx)
{
    assert(ctx != NULL);
    futhark_context_sync(ctx->futhark);
    fmpi_futhark_abort_on_error(ctx, "futhark_context_sync()");
}
/*------------------------------------------------------------------------------
    fmpi_futhark_has_error()
------------------------------------------------------------------------------*/
_Bool fmpi_futhark_has_error(struct fmpi_ctx * const ctx, const char * const func_name)
{
    assert(ctx != NULL);

    char * err_str = futhark_context_get_error(ctx->futhark);
    if(err_str != NULL) {
        const char * func_str = func_name != NULL ? func_name : "NO_NAME";
        fprintf(stderr, "[FUTHARK ERROR rank=%d] %s failed! %s\n", ctx->mpi->rank, func_str, err_str);
        free(err_str); err_str = NULL;
        return 1;
    }
    return 0;
}
/*------------------------------------------------------------------------------
    fmpi_futhark_abort_on_error()
------------------------------------------------------------------------------*/
void fmpi_futhark_abort_on_error(struct fmpi_ctx * ctx, const char * const func_name)
{
    assert(ctx != NULL);
    if(fmpi_futhark_has_error(ctx, func_name)) {
        fmpi_abort(ctx);
    }
}
/*------------------------------------------------------------------------------
    fmpi_new_1d()
------------------------------------------------------------------------------*/
#define FMPI_DEFINE_NEW_1D(T)                                 \
FMPI_PROTO_NEW_1D(T)                                          \
{                                                                              \
    assert(ctx != NULL);                                                       \
    assert(array != NULL);                                                     \
    struct futhark_##T##_1d * xs = futhark_new_##T##_1d(ctx->futhark, array, length); \
    fmpi_futhark_abort_on_error(ctx, CPL_STRINGIFY(futhark_new_##T##_1d())); \
    fmpi_futhark_sync(ctx);\
    return xs;\
}
/*------------------------------------------------------------------------------
    fmpi_free_1d()
------------------------------------------------------------------------------*/
#define FMPI_DEFINE_FREE_1D(T)                                 \
FMPI_PROTO_FREE_1D(T)                                          \
{                                                                              \
    assert(ctx != NULL);                                                       \
    assert(array != NULL);                                                     \
    fmpi_futhark_sync(ctx);\
    futhark_free_##T##_1d(ctx->futhark, array);                                   \
    fmpi_futhark_abort_on_error(ctx, CPL_STRINGIFY(futhark_free_##T##_1d())); \
}

FMPI_DEFINE_FUNC(FMPI_DEFINE_NEW_1D, FMPI_FUTHARK_TYPES)
FMPI_DEFINE_FUNC(FMPI_DEFINE_FREE_1D, FMPI_FUTHARK_TYPES)