#ifndef PROTOTYPE1_SRC_L1_5_CODEGEN_ALL_SET_MAP_TEMPL_UTIL_INST_H
#define PROTOTYPE1_SRC_L1_5_CODEGEN_ALL_SET_MAP_TEMPL_UTIL_INST_H

#include "../../l1/codegen/util_template_inst.h"

/* We assume that T is trivially movable */
typedef struct {
    SpanU8 T;
    bool t_integer;
    bool t_primitive;
    bool t_clonable;
    SpanU8 alternative_less;
    SpanU8 alternative_comp_set_name_embed;

    /* GT. You probably want it to be a pointer or an integer parameter.
     * Leave empty if you don't need guest data (GT = void)
     * GT must be primitive, or, even better, be integer */
    SpanU8 guest_data_T;
} set_instantiation_op;

void set_instantiation_op_fix(set_instantiation_op* self){
    if (self->t_integer)
        self->t_primitive = true;
    if (self->t_primitive)
        self->t_clonable = true;
    assert(self->T.len > 0);
    assert(self->alternative_less.len > 0 == self->alternative_comp_set_name_embed.len > 0);
    assert(self->guest_data_T.len == 0 || self->alternative_less.len > 0);
}

/* We assume K and V are trivially movable */
typedef struct {
    SpanU8 K;
    bool k_integer;
    bool k_primitive;
    bool k_clonable;
    SpanU8 V;
    bool v_integer;
    bool v_primitive;
    bool v_clonable;

    SpanU8 alternative_less;
    SpanU8 alternative_comp_map_name_embed;

    SpanU8 guest_data_T;

    bool at, mat;
    bool pop, pop_substitute;
} map_instantiation_op;

void map_instantiation_op_fix(map_instantiation_op* self){
    assert(self->K.len > 0);
    if (self->k_integer)
        self->k_primitive = true;
    if (self->k_primitive)
        self->k_clonable = true;

    if (self->V.len == 0)
        self->v_primitive = true;
    if (self->v_integer)
        self->v_primitive = true;
    if (self->v_primitive)
        self->v_clonable = true;
    assert(self->alternative_less.len > 0 == self->alternative_comp_map_name_embed.len > 0);
    assert(self->guest_data_T.len == 0 || self->alternative_less.len > 0);
}


/* --- Sharing is caring --- */
/* Assuming A nd B are passed as intended */
NODISCARD VecU8 codegen_rbtree_map__less(map_instantiation_op op, VecU8 A, VecU8 B){
    if (op.guest_data_T.len > 0) {
        assert(op.alternative_less.len > 0);
        return VecU8_fmt("%s(%v, %v, self->guest)", op.alternative_less, A, B);
    }
    if (op.alternative_less.len > 0)
        return VecU8_fmt("%s(%v, %v)", op.alternative_less, A, B);
    if (op.k_integer)
        return VecU8_fmt("%v < %v", A, B);
    return VecU8_fmt("%s_less_%s(%v %v)", op.K, op.K, A, B);
}

NODISCARD VecU8 codegen_rbtree_map__exp_passing_key_val(map_instantiation_op op){
    return op.k_integer ? vcstr("key") : vcstr("&key");
}

/* Suppose some method (like _erase() or _pop(), or _find(), or _at(),  takes constant reference to key T
 * This function tells how to write type of this argument. Basically it is needed to take into account that
 * integer is better than pointer to integer. (Though, notice that _pop family of methods don't exist for
 * sets of integers
 */
NODISCARD VecU8 codegen_rbtree_map__taking_ref_k_argument(map_instantiation_op op){
    return op.k_integer ? VecU8_from_span(op.K) : VecU8_fmt("const %s*", op.K);
}

NODISCARD VecU8 codegen_rbtree_map__taking_t_argument(map_instantiation_op op){
    return op.V.len > 0 ? VecU8_fmt("%s key, %s value", op.K, op.V) : VecU8_fmt("%s key", op.K);
}


#endif
