// IWYU pragma: private; include "rlang.h"

#ifndef RLANG_EVAL_H
#define RLANG_EVAL_H

#include "rlang-types.h"
#include "c-utils.h"
#include "call.h"

static inline r_obj* r_eval(r_obj* expr, r_obj* env) {
    return Rf_eval(expr, env);
}

r_obj* r_eval_with_x(r_obj* call, r_obj* x, r_obj* parent);
r_obj* r_eval_with_xy(r_obj* call, r_obj* x, r_obj* y, r_obj* parent);
r_obj* r_eval_with_xyz(
    r_obj* call,
    r_obj* x,
    r_obj* y,
    r_obj* z,
    r_obj* parent
);
r_obj* r_eval_with_wxyz(
    r_obj* call,
    r_obj* w,
    r_obj* x,
    r_obj* y,
    r_obj* z,
    r_obj* parent
);

r_obj* r_exec_mask_n(
    r_obj* fn_sym,
    r_obj* fn,
    const struct r_pair* args,
    int n,
    r_obj* parent
);

r_obj* r_exec_n(
    r_obj* fn_sym,
    r_obj* fn,
    const struct r_pair* args,
    int n,
    r_obj* env
);

r_obj* r_exec_mask_n_call_poke(
    r_obj* fn_sym,
    r_obj* fn,
    const struct r_pair* args,
    int n,
    r_obj* env
);

static inline r_obj* r_exec_mask1(
    r_obj* fn_sym,
    r_obj* fn,
    r_obj* x_sym,
    r_obj* x,
    r_obj* env
) {
    struct r_pair args[] = {{x_sym, x}};
    return r_exec_mask_n(fn_sym, fn, args, R_ARR_SIZEOF(args), env);
}

static inline r_obj* r_exec_mask2(
    r_obj* fn_sym,
    r_obj* fn,
    r_obj* x1_sym,
    r_obj* x1,
    r_obj* x2_sym,
    r_obj* x2,
    r_obj* env
) {
    struct r_pair args[] = {{x1_sym, x1}, {x2_sym, x2}};
    return r_exec_mask_n(fn_sym, fn, args, R_ARR_SIZEOF(args), env);
}

static inline r_obj* r_exec_mask3(
    r_obj* fn_sym,
    r_obj* fn,
    r_obj* x1_sym,
    r_obj* x1,
    r_obj* x2_sym,
    r_obj* x2,
    r_obj* x3_sym,
    r_obj* x3,
    r_obj* env
) {
    struct r_pair args[] = {{x1_sym, x1}, {x2_sym, x2}, {x3_sym, x3}};
    return r_exec_mask_n(fn_sym, fn, args, R_ARR_SIZEOF(args), env);
}

static inline r_obj* r_exec_mask4(
    r_obj* fn_sym,
    r_obj* fn,
    r_obj* x1_sym,
    r_obj* x1,
    r_obj* x2_sym,
    r_obj* x2,
    r_obj* x3_sym,
    r_obj* x3,
    r_obj* x4_sym,
    r_obj* x4,
    r_obj* env
) {
    struct r_pair args[] =
        {{x1_sym, x1}, {x2_sym, x2}, {x3_sym, x3}, {x4_sym, x4}};
    return r_exec_mask_n(fn_sym, fn, args, R_ARR_SIZEOF(args), env);
}

static inline r_obj* r_exec_mask5(
    r_obj* fn_sym,
    r_obj* fn,
    r_obj* x1_sym,
    r_obj* x1,
    r_obj* x2_sym,
    r_obj* x2,
    r_obj* x3_sym,
    r_obj* x3,
    r_obj* x4_sym,
    r_obj* x4,
    r_obj* x5_sym,
    r_obj* x5,
    r_obj* env
) {
    struct r_pair args[] =
        {{x1_sym, x1}, {x2_sym, x2}, {x3_sym, x3}, {x4_sym, x4}, {x5_sym, x5}};
    return r_exec_mask_n(fn_sym, fn, args, R_ARR_SIZEOF(args), env);
}

static inline r_obj* r_exec_mask6(
    r_obj* fn_sym,
    r_obj* fn,
    r_obj* x1_sym,
    r_obj* x1,
    r_obj* x2_sym,
    r_obj* x2,
    r_obj* x3_sym,
    r_obj* x3,
    r_obj* x4_sym,
    r_obj* x4,
    r_obj* x5_sym,
    r_obj* x5,
    r_obj* x6_sym,
    r_obj* x6,
    r_obj* env
) {
    struct r_pair args[] = {
        {x1_sym, x1},
        {x2_sym, x2},
        {x3_sym, x3},
        {x4_sym, x4},
        {x5_sym, x5},
        {x6_sym, x6}
    };
    return r_exec_mask_n(fn_sym, fn, args, R_ARR_SIZEOF(args), env);
}

static inline r_obj* r_exec_mask7(
    r_obj* fn_sym,
    r_obj* fn,
    r_obj* x1_sym,
    r_obj* x1,
    r_obj* x2_sym,
    r_obj* x2,
    r_obj* x3_sym,
    r_obj* x3,
    r_obj* x4_sym,
    r_obj* x4,
    r_obj* x5_sym,
    r_obj* x5,
    r_obj* x6_sym,
    r_obj* x6,
    r_obj* x7_sym,
    r_obj* x7,
    r_obj* env
) {
    struct r_pair args[] = {
        {x1_sym, x1},
        {x2_sym, x2},
        {x3_sym, x3},
        {x4_sym, x4},
        {x5_sym, x5},
        {x6_sym, x6},
        {x7_sym, x7}
    };
    return r_exec_mask_n(fn_sym, fn, args, R_ARR_SIZEOF(args), env);
}

static inline r_obj* r_lazy_eval(struct r_lazy lazy) {
    if (!lazy.env) {
        // Unitialised lazy variable
        return r_null;
    } else if (lazy.env == r_null) {
        // Forced lazy variable
        return lazy.x;
    } else {
        return r_eval(lazy.x, lazy.env);
    }
}

extern struct r_lazy r_lazy_null;

extern struct r_lazy r_lazy_missing_arg;

static inline r_obj* r_lazy_eval_protect(struct r_lazy lazy) {
    r_obj* out = KEEP(r_lazy_eval(lazy));
    out = r_expr_protect(out);

    FREE(1);
    return out;
}

static inline bool r_lazy_is_null(struct r_lazy call) {
    return !call.x && !call.env;
}

#endif
