#ifndef PROTOTYPE1_SRC_L1_5_CODEGEN_RB_TREE_SET_MAP_TEMPLATE_INST_H
#define PROTOTYPE1_SRC_L1_5_CODEGEN_RB_TREE_SET_MAP_TEMPLATE_INST_H

#include "all_set_map_templ_util_inst.h"

/* Assuming A nd B are passed as intended */

NODISCARD VecU8 codegen_buf_rbtree_map__exp_passing_cur_key(map_instantiation_op op){
    return VecU8_fmt("%s" "self->el.buf[cur - 1]" "%s",
        op.k_integer ? cstr("") : cstr("&"), op.V.len > 0 ? cstr(".key") : cstr(""));
}


/* Yes, both sets and maps use this function to instantiate themselves. No, user does not need to use it
 * set is either a set name or a map name. If we are instantiating set, TT is op.T from set options, if we are
 * instantiating a map, TT is KVP{op.K}To{op.V} from map options
 * */
void codegen_append_buff_rbtree_map__structure_and_simplest_methods(
    VecU8* res, map_instantiation_op op, SpanU8 set, SpanU8 TT
    ){
    VecU8_append_vec(res, VecU8_fmt(
        "typedef struct {\n"
        SPACE "VecBufRBTreeNode tree;\n"
        SPACE "U64 root;\n"
        SPACE "Vec%s el;\n"
        "%v"
        "} %s;\n\n",
    TT, op.guest_data_T.len > 0 ? VecU8_fmt(SPACE "%s guest;\n", op.guest_data_T) : vcstr(""), set));

    if (op.guest_data_T.len > 0) {
        VecU8_append_vec(res, VecU8_fmt(
           "NODISCARD %s %s_new(%s guest) {\n"                                         /* set, set, op.guest_data_T */
           SPACE "return (%s){.tree = VecBufRBTreeNode_new_zeroinit(1), .root = 0, .el = Vec%s_new(), .guest = guest};\n" /* set, TT */
           "}\n\n",
           set, set, op.guest_data_T,
           set, TT));
        VecU8_append_vec(res, VecU8_fmt(
           "NODISCARD %s %s_new_reserved(%s guest, size_t size) {\n"                     /* set, set, op.guest_data_T */
           SPACE "return (%s){.tree = (VecBufRBTreeNode){\n"                                 /* set */
           SPACE SPACE ".buf = (BufRBTreeNode*)safe_calloc(size + 1, sizeof(BufRBTreeNode)), .len = 1, .capacity = size + 1},\n"
           SPACE SPACE ".root = 0, .el = Vec%s_new_reserved(size), .guest = guest};\n"                        /* TT */
           "}\n\n",
           set, set, op.guest_data_T,
           set, TT));
    } else {
        VecU8_append_vec(res, VecU8_fmt(
           "NODISCARD %s %s_new() {\n"                                   /* set, set */
           SPACE "return (%s){.tree = VecBufRBTreeNode_new_zeroinit(1), .root = 0, .el = Vec%s_new()};\n" /* set, TT */
           "}\n\n",
           set, set,
           set, TT));
        VecU8_append_vec(res, VecU8_fmt(
           "NODISCARD %s %s_new_reserved(size_t size) {\n"                       /* set, set */
           SPACE "return (%s){.tree = (VecBufRBTreeNode){\n"
           SPACE SPACE ".buf = (BufRBTreeNode*)safe_calloc(size + 1, sizeof(BufRBTreeNode)), .len = 1, .capacity = size + 1},\n"
           SPACE SPACE ".root = 0, .el = Vec%s_new_reserved(size)};\n"                          /* set, TT */
           "}\n\n",
           set, set,
           set, TT));
    }

    VecU8_append_vec(res, VecU8_fmt(
        "void %s_drop(%s self) {\n"              /* set, set */
        SPACE "VecBufRBTreeNode_drop(self.tree);\n"
        SPACE "Vec%s_drop(self.el);\n"           /* TT */
        "}\n\n", set, set, TT));

    VecU8_append_vec(res, VecU8_fmt(
        "void %s_sink(%s* self) {\n"              /* set, set */
        SPACE "self->tree.len = 1;\n"
        SPACE "self->tree.buf[0] = (BufRBTreeNode){0};\n"
        SPACE "Vec%s_sink(&self->el, 0);\n"       /* TT */
        "}\n\n", set, set, TT));

    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find(const %s* self, %v key) {\n"               /* set, set, taking_ref_k_argument */
        SPACE "U64 cur = self->root;\n"
        SPACE "while (cur != 0) {\n"
        SPACE SPACE "if (%v)\n"    /* key < cur key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE "else if (%v)\n"  /* cur key < key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE "else \n"
        SPACE SPACE SPACE "return cur;\n"
        SPACE "}\n"
        SPACE "return 0;\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_ref_k_argument(op),
        codegen_rbtree_map__less(op, vcstr("key"), codegen_buf_rbtree_map__exp_passing_cur_key(op)),
        codegen_rbtree_map__less(op, codegen_buf_rbtree_map__exp_passing_cur_key(op), vcstr("key"))));

    if (op.k_clonable && op.v_clonable) {
        VecU8_append_vec(res, VecU8_fmt(
           "NODISCARD %s %s_clone(const %s* self){\n"                                            /* set, set, set */
           SPACE "return (%s){.tree = VecBufRBTreeNode_clone(&self->tree), .root = self->root,\n"   /* set */
           SPACE SPACE ".el = Vec%s_clone(&self->el)%s};\n"                  /* TT, whether to clone guest or no  */
           "}\n\n",
           set, set, set,
           set,
           TT, op.guest_data_T.len > 0 ? cstr(", .guest = self->guest") : cstr("")));
    }

    // todo: move to common code
    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_next(const %s* self, U64 x){\n"
        SPACE "assert(x != 0 && x < self->tree.len);\n"
        SPACE "if (self->tree.buf[x].right != 0)\n"
        SPACE SPACE "return BufRBTree_minimum_in_subtree(self->tree.buf, self->tree.buf[x].right);\n"
        SPACE "while (true) {\n"
        SPACE SPACE "U64 p = self->tree.buf[x].parent;\n"
        SPACE SPACE "if (p == 0)\n"
        SPACE SPACE SPACE "return 0;\n"
        SPACE SPACE "if (self->tree.buf[p].left == x)\n"
        SPACE SPACE SPACE "return p;\n"
        SPACE SPACE "x = p;\n"
        SPACE "}\n"
        "}\n\n", set, set));

    // todo: move to comon code (core/buff_rb_tree_node.h)
    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_prev(const %s* self, U64 x){\n"
        SPACE "assert(x != 0 && x < self->tree.len);\n"
        SPACE "if (self->tree.buf[x].left != 0)\n"
        SPACE SPACE "return BufRBTree_maximum_in_subtree(self->tree.buf, self->tree.buf[x].left);\n"
        SPACE "while (true) {\n"
        SPACE SPACE "U64 p = self->tree.buf[x].parent;\n"
        SPACE SPACE "if (p == 0)\n"
        SPACE SPACE SPACE "return 0;\n"
        SPACE SPACE "if (self->tree.buf[p].right == x)\n"
        SPACE SPACE SPACE "return p;\n"
        SPACE SPACE "x = p;\n"
        SPACE "}\n"
        "}\n\n", set, set));

    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_min(const %s* self) {\n"
        SPACE "return self->root != 0 ? BufRBTree_minimum_in_subtree(self->tree.buf, self->root) : 0;\n"
        "}\n\n", set, set));

    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_max(const %s* self) {\n"
        SPACE "return self->root != 0 ? BufRBTree_maximum_in_subtree(self->tree.buf, self->root) : 0;\n"
        "}\n\n", set, set));

    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_max_less(const %s* self, %v key) {\n" /* set, set, taking_ref_t_argument */
        SPACE "U64 last_less = 0;\n"
        SPACE "U64 cur = self->root;\n"
        SPACE "while (cur != 0) {\n"
        SPACE SPACE "if (%v) {"   /* key < cur key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE "} else if (%v) {\n"   /* cur key < key */
        SPACE SPACE SPACE "last_less = cur;\n"
        SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE "} else {\n"
        SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE SPACE "if (cur == 0)\n"
        SPACE SPACE SPACE SPACE "return last_less;\n"
        SPACE SPACE SPACE "while (self->tree.buf[cur].right != 0)\n"
        SPACE SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE SPACE "return cur;\n"
        SPACE SPACE "}\n"
        SPACE "}\n"
        SPACE "return last_less;\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_ref_k_argument(op),
        codegen_rbtree_map__less(op, vcstr("key"), codegen_buf_rbtree_map__exp_passing_cur_key(op)),
        codegen_rbtree_map__less(op, codegen_buf_rbtree_map__exp_passing_cur_key(op), vcstr("key"))));

    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_max_less_or_eq(const %s* self, %v key) {\n" /* set, set, taking_ref_t_argument */
        SPACE "U64 last_less = 0;\n"
        SPACE "U64 cur = self->root;\n"
        SPACE "while (cur != 0) {\n"
        SPACE SPACE "if (%v) {"   /* key < cur key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE "} else if (%v) {\n"   /* cur key < key */
        SPACE SPACE SPACE "last_less = cur;\n"
        SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE "} else {\n"
        SPACE SPACE SPACE "return cur;\n"
        SPACE SPACE "}\n"
        SPACE "}\n"
        SPACE "return last_less;\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_ref_k_argument(op),
        codegen_rbtree_map__less(op, vcstr("key"), codegen_buf_rbtree_map__exp_passing_cur_key(op)),
        codegen_rbtree_map__less(op, codegen_buf_rbtree_map__exp_passing_cur_key(op), vcstr("key"))));


    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_min_grtr(const %s* self, %v key) {\n" /* set, set, taking_ref_t_argument */
        SPACE "U64 last_grtr = 0;\n"
        SPACE "U64 cur = self->root;\n"
        SPACE "while (cur != 0) {\n"
        SPACE SPACE "if (%v) {\n"  /* key < cur key*/
        SPACE SPACE SPACE "last_grtr = cur;\n"
        SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE "} else if (%v) {\n"  /* cur key < key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE "} else {\n"
        SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE SPACE "if (cur == 0)\n"
        SPACE SPACE SPACE SPACE "return last_grtr;\n"
        SPACE SPACE SPACE "while (self->tree.buf[cur].left != 0)\n"
        SPACE SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE SPACE "return cur;\n"
        SPACE SPACE "}\n"
        SPACE "}\n"
        SPACE "return last_grtr;\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_ref_k_argument(op),
        codegen_rbtree_map__less(op, vcstr("key"), codegen_buf_rbtree_map__exp_passing_cur_key(op)),
        codegen_rbtree_map__less(op, codegen_buf_rbtree_map__exp_passing_cur_key(op), vcstr("key"))));


    VecU8_append_vec(res, VecU8_fmt(
        "U64 %s_find_min_grtr_or_eq(const %s* self, %v key) {\n" /* set, set, taking_ref_t_argument */
        SPACE "U64 last_grtr = 0;\n"
        SPACE "U64 cur = self->root;\n"
        SPACE "while (cur != 0) {\n"
        SPACE SPACE "if (%v) {\n"  /* key < cur key*/
        SPACE SPACE SPACE "last_grtr = cur;\n"
        SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE "} else if (%v) {\n"  /* cur key < key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE "} else {\n"
        SPACE SPACE SPACE "return cur;\n"
        SPACE SPACE "}\n"
        SPACE "}\n"
        SPACE "return last_grtr;\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_ref_k_argument(op),
        codegen_rbtree_map__less(op, vcstr("key"), codegen_buf_rbtree_map__exp_passing_cur_key(op)),
        codegen_rbtree_map__less(op, codegen_buf_rbtree_map__exp_passing_cur_key(op), vcstr("key"))));

    VecU8 line_that_appends_new_el_to_el_vec = op.V.len > 0 ?
        VecU8_fmt("VecKVP%sTo%s_append(&self->el, (KVP%sTo%s){.key = key, .value = value});", op.K, op.V, op.K, op.V) :
        VecU8_fmt("Vec%s_append(&self->el, key);", op.K);

    VecU8_append_vec(res, VecU8_fmt(
        /* This method is unsafe. Arguments key, value will be taken if 0 is returned,
         * or left on their place if not-0 is returned */
        "/* UNSAFE */\n"
        "NODISCARD U64 %s_try_insert(%s* self, %v) {\n"  /* set, set, taking_t_argument */
        SPACE "if (self->root == 0) {\n"
        SPACE SPACE "assert(self->tree.len == 1);\n"
        SPACE SPACE "VecBufRBTreeNode_append(&self->tree, (BufRBTreeNode){.color = RBTree_black});\n"
        SPACE SPACE "self->root = 1;\n"
        SPACE SPACE "%s\n"  /* line_that_appends_new_el_to_el_vec */
        SPACE SPACE "return 0;\n"
        SPACE "}\n"
        SPACE "U64 cur = self->root;\n"
        SPACE "while (true) {\n"
        SPACE SPACE "if (%v) {\n"  /* key < el[cur] */
        SPACE SPACE SPACE "if (self->tree.buf[cur].left != 0) {\n"
        SPACE SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE SPACE "} else { \n"
        SPACE SPACE SPACE SPACE "U64 n = self->tree.len;\n"
        SPACE SPACE SPACE SPACE "VecBufRBTreeNode_append(&self->tree, (BufRBTreeNode){.parent = cur, .color = RBTree_red});\n"
        SPACE SPACE SPACE SPACE "self->tree.buf[cur].left = n;\n"
        SPACE SPACE SPACE SPACE "BufRBTree_fix_after_insert(self->tree.buf, &self->root, n);\n"
        SPACE SPACE SPACE SPACE "%s\n"  /* line_that_appends_new_el_to_el_vec */
        SPACE SPACE SPACE SPACE "return 0;\n"
        SPACE SPACE SPACE "}\n"
        SPACE SPACE "} else if (%v) {\n"  /* el[cur] < key */
        SPACE SPACE SPACE "if (self->tree.buf[cur].right != 0) {\n"
        SPACE SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE SPACE "} else {\n"
        SPACE SPACE SPACE SPACE "U64 n = self->tree.len;\n"
        SPACE SPACE SPACE SPACE "VecBufRBTreeNode_append(&self->tree, (BufRBTreeNode){.parent = cur, .color = RBTree_red});\n"
        SPACE SPACE SPACE SPACE "self->tree.buf[cur].right = n;\n"
        SPACE SPACE SPACE SPACE "BufRBTree_fix_after_insert(self->tree.buf, &self->root, n);\n"
        SPACE SPACE SPACE SPACE "%s\n"  /* line_that_appends_new_el_to_el_vec */
        SPACE SPACE SPACE SPACE "return 0;\n"
        SPACE SPACE SPACE "}\n"
        SPACE SPACE "} else {\n"
        SPACE SPACE SPACE "return cur;\n"
        SPACE SPACE "}\n"
        SPACE "}\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_t_argument(op),
        VecU8_to_span(&line_that_appends_new_el_to_el_vec),
        codegen_rbtree_map__less(op, codegen_rbtree_map__exp_passing_key_val(op), codegen_buf_rbtree_map__exp_passing_cur_key(op)),
        VecU8_to_span(&line_that_appends_new_el_to_el_vec),
        codegen_rbtree_map__less(op, codegen_buf_rbtree_map__exp_passing_cur_key(op), codegen_rbtree_map__exp_passing_key_val(op)),
        VecU8_to_span(&line_that_appends_new_el_to_el_vec)));
        VecU8_drop(line_that_appends_new_el_to_el_vec);

    // VecU8_append_vec(res, VecU8_fmt(
    //     "/* UNSAFE. Use when you dropped the symbol that is about to be deleted */\n"
    //     "void %s_empty_index_erase(%s* self, U64 z) {\n"                          /* set, set */
    //     SPACE "assert(z != 0 && z < self->tree.len);\n"
    //     SPACE "U64 y = (self->tree.buf[z].left == 0 || self->tree.buf[z].right == 0) ? z : BufRBTree_minimum_in_subtree(self->tree.buf, self->tree.buf[z].right);\n"
    //     SPACE "U64 x = self->tree.buf[y].left != 0 ? self->tree.buf[y].left : self->tree.buf[y].right;\n"
    //     SPACE "assert(x != y && x != z);\n"
    //     SPACE "U64 x_adopter = self->tree.buf[y].parent;\n"
    //     SPACE "self->tree.buf[x].parent = x_adopter;\n"
    //     SPACE "if (x_adopter == 0)\n"
    //     SPACE SPACE "self->root = x;\n"
    //     SPACE "else if (self->tree.buf[x_adopter].left == y)\n"
    //     SPACE SPACE "self->tree.buf[x_adopter].left = x;\n"
    //     SPACE "else\n"
    //     SPACE SPACE "self->tree.buf[x_adopter].right = x;\n"
    //     SPACE "RBTreeColor y_org_clr = self->tree.buf[y].color;\n"
    //     SPACE "if (z != y) {\n"
    //     SPACE SPACE "BufRBTree_steal_neighbours(self->tree.buf, &self->root, z, y);\n"
    //     SPACE SPACE "if (x_adopter == z)\n"
    //     SPACE SPACE SPACE "x_adopter = y;\n"
    //     SPACE "}\n"
    //     SPACE "U64 L = self->el.len;\n"
    //     SPACE "if (L != z) {\n"
    //     SPACE SPACE "BufRBTree_steal_neighbours(self->tree.buf, &self->root, L, z);\n"
    //     SPACE SPACE "self->el.buf[z-1] = self->el.buf[L-1];\n"
    //     SPACE SPACE "if (L == x)\n"
    //     SPACE SPACE SPACE "x = z;\n"
    //     SPACE SPACE "else if (L == x_adopter) \n"
    //     SPACE SPACE SPACE "x_adopter = z;\n"
    //     SPACE "}\n"
    //     SPACE "self->tree.buf[x].parent = x_adopter;\n"
    //     SPACE "self->tree.len--;\n"
    //     SPACE "self->el.len--;\n"
    //     SPACE "if (y_org_clr == RBTree_black)\n"
    //     SPACE SPACE "BufRBTree_fix_after_delete(self->tree.buf, &self->root, x);\n"
    //     "}\n\n",
    //     set, set));

    VecU8_append_vec(res, VecU8_fmt(
        "bool %s_insert(%s* self, %v) {\n"    /* set, set, taking_t_argument */
        SPACE "U64 col = %s_try_insert(self, key" "%s" ");\n"  /* set, "" /, value */
        SPACE "if (col == 0)\n"
        SPACE SPACE "return true;\n"
        "%v" "%v"                     /* "" / dropping key, "" / dropping value */
        SPACE "return false;\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_t_argument(op),
        set, op.V.len > 0 ? cstr(", value") : cstr(""),
        op.k_primitive ? vcstr("") : VecU8_fmt(SPACE "%s_drop(key);\n", op.K),
        op.v_primitive ? vcstr("") : VecU8_fmt(SPACE "%s_drop(value);\n", op.V)));

    // todo: write _erase_by_iter method

    // todo: rewrite _erase using _erase_by_it
    VecU8_append_vec(res, VecU8_fmt(
        "bool %s_erase(%s* self, %v key) {\n"                 /* set, set, taking_ref_k_argument */
        SPACE "U64 v = %s_find(self, key);\n"                 /* set */
        SPACE "if (v == 0)\n"
        SPACE SPACE "return false;\n"
        "%v"                                                /* "" / op.K_drop(v->key) */
        "%v"                                                /* "" / op.V_drop(v->value) */
        SPACE "BufRBTree_empty_index_erase(&self->tree, &self->root, v);\n"
        SPACE "self->el.buf[v - 1] = self->el.buf[self->el.len - 1];\n"
        SPACE "self->el.len--;\n"
        SPACE "return true;\n"
        "}\n\n",
        set, set, codegen_rbtree_map__taking_ref_k_argument(op), set,
        op.k_primitive ? vcstr("") : VecU8_fmt(
            SPACE "%s_drop(self->el.buf[v - 1]%s);\n", op.K, op.V.len > 0 ? cstr(".key") : cstr("")),
        op.v_primitive ? vcstr("") : VecU8_fmt(SPACE "%s_drop(self->el.buf[v - 1].value);\n", op.V)));
}

NODISCARD VecU8 codegen_buf_rbtree_map__option_returned_ref_v(map_instantiation_op op, bool mut){
    assert(op.V.len > 0);
    if (op.v_integer)
        return VecU8_fmt("Option%s", op.V);
    return mut ? VecU8_fmt("OptionRefMut%s", op.V) : VecU8_fmt("OptionRef%s", op.V);
}

NODISCARD VecU8 codegen_buf_rbtree_map__some_ref_v(map_instantiation_op op, bool mut){
    assert(op.V.len > 0);
    if (op.v_integer)
        return VecU8_fmt("Some_%s(self->el.buf[cur - 1].value)", op.V);
    if (mut)
        return VecU8_fmt("Some_RefMut%s(&self->el.buf[cur - 1].value)", op.V);
    return VecU8_fmt("Some_Ref%s(&self->el.buf[cur - 1].value)", op.V);
}

NODISCARD VecU8 codegen_buf_rbtree_map__none_ref_v(map_instantiation_op op, bool mut){
    assert(op.V.len > 0);
    if (op.v_integer)
        return VecU8_fmt("None_%s()", op.V);
    return mut ? VecU8_fmt("None_RefMut%s()", op.V) : VecU8_fmt("None_Ref%s()", op.V) ;
}

void codegen_append_buf_rbtree_map__method_at(VecU8* res, map_instantiation_op op, SpanU8 set, bool mut){
    VecU8_append_vec(res, VecU8_fmt(
        "%v %s_%s(%s%s* self, %v key) {\n" /* option_returned_ref_t, set, mat/at, e/const, set, taking_ref_t_argument */
        SPACE "U64 cur = self->root;\n"
        SPACE "while (cur != 0) {\n"
        SPACE SPACE "if (%v) {\n"                    /* key < cur key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].left;\n"
        SPACE SPACE "} else if (%v) {\n"             /* cur key < key */
        SPACE SPACE SPACE "cur = self->tree.buf[cur].right;\n"
        SPACE SPACE "} else {\n"
        SPACE SPACE SPACE "return %v;\n"             /* some_ref_t */
        SPACE SPACE "}\n"
        SPACE "}\n"
        SPACE "return %v;\n"                         /* none_ref_t */
        "}\n\n",
        codegen_buf_rbtree_map__option_returned_ref_v(op, mut), set, mut ? cstr("mat") : cstr("at"),
        mut ? cstr("") : cstr("const "), set, codegen_rbtree_map__taking_ref_k_argument(op),

        codegen_rbtree_map__less(op, vcstr("key"), codegen_buf_rbtree_map__exp_passing_cur_key(op)),
        codegen_rbtree_map__less(op, codegen_buf_rbtree_map__exp_passing_cur_key(op), vcstr("key")),
        codegen_buf_rbtree_map__some_ref_v(op, mut),
        codegen_buf_rbtree_map__none_ref_v(op, mut)
        ));
}

NODISCARD VecU8 get_name_of_buf_rbtree_set_structure(set_instantiation_op op){
    if (op.alternative_comp_set_name_embed.len)
        return VecU8_fmt("BufRBTreeBy%s_Set%s", op.alternative_comp_set_name_embed, op.T);
    return VecU8_fmt("BufRBTree_Set%s", op.T);
}

/* src/l1_5/core/buff_rb_tree_node.h is a dependency of all instantiations of BufRBTree_Set template
 * Don't forget to include them
 * */
NODISCARD VecU8 generate_buf_rbtree_Set_template_instantiation(set_instantiation_op op){
    set_instantiation_op_fix(&op);
    VecU8 res = VecU8_new();
    VecU8 g_set = get_name_of_buf_rbtree_set_structure(op);
    SpanU8 set = VecU8_to_span(&g_set);

    map_instantiation_op map_op = {.K = op.T,
        .k_integer = op.t_integer, .k_primitive = op.t_primitive, .k_clonable = op.t_clonable,
        .V = cstr(""), .v_primitive = true, .v_clonable = true,
        .alternative_less = op.alternative_less,
        .alternative_comp_map_name_embed = op.alternative_comp_set_name_embed, .guest_data_T = op.guest_data_T,
    };

    codegen_append_buff_rbtree_map__structure_and_simplest_methods(&res, map_op, set, op.T);

    VecU8_append_vec(&res, VecU8_fmt(
        "const %s* %s_at_iter(const %s* self, U64 it) {\n"      /* op.T, set, set */
        SPACE "assert(0 < it && it < self->tree.len);\n"
        SPACE "return &self->el.buf[it - 1];\n"
        "}\n\n",
        op.T, set, set));

    VecU8_drop(g_set);
    return res;
}


void generate_buf_rbtree_Set_templ_inst_eve_header(SpanU8 layer, SpanU8 bonus_ns, set_instantiation_op op) {
    generate_SOME_templ_inst_eve_header(layer, bonus_ns,
        generate_buf_rbtree_Set_template_instantiation(op), get_name_of_buf_rbtree_set_structure(op));
}

void generate_buf_rbtree_Set_templ_inst_guarded_header(
    SpanU8 layer, SpanU8 bonus_ns, SpanU8 dependencies, set_instantiation_op op
    ){
    VecU8 all_dependencies = VecU8_fmt("%v%s\n",
        codegen_include_relative_to_root(bonus_ns, cstr("src/l1_5/core/buff_rb_tree_node.h")), dependencies);
    generate_SOME_templ_inst_guarded_header(layer, bonus_ns, all_dependencies,
        generate_buf_rbtree_Set_template_instantiation(op), get_name_of_buf_rbtree_set_structure(op));
}

/* ========= Now we add Map<K, V> into the picture ======== */

void codegen_append_buff_rbtree_map__method_at_iter(VecU8* res, map_instantiation_op op, SpanU8 set, bool mut){
    assert(op.V.len > 0);
    VecU8_append_vec(res, VecU8_fmt(
        "void %s_%s(%s%s* self, U64 it, %v* ret_key, %v* ret_value) {\n"  /* set, method name, self access modifier, set, key ret ptr, value ret ptr */
        SPACE "assert(0 < it && it < self->tree.len);\n"
        SPACE "*ret_key = %s" "self->el.buf[it - 1].key;\n"               /* epsilon / ampersand */
        SPACE "*ret_value = %s" "self->el.buf[it - 1].value;\n"           /* epsilon / ampersand */
        "}\n\n",
        set, mut ? cstr("mat_iter") : cstr("at_iter"), mut ? cstr("") : cstr("const "), set,
        op.k_integer ? VecU8_from_span(op.K) : VecU8_fmt("const %s*", op.K),
        mut ? VecU8_fmt("%s*", op.V) : (op.v_integer ? VecU8_from_span(op.V) : VecU8_fmt("const %s*", op.V)),

        op.k_integer ? cstr("") : cstr("&"), (op.v_integer && !mut) ? cstr("") : cstr("&")));
}

NODISCARD VecU8 get_name_of_buf_rbtree_map_structure(map_instantiation_op op){
    if (op.alternative_comp_map_name_embed.len)
        return VecU8_fmt("BufRBTreeBy%s_Map%sTo%s", op.alternative_comp_map_name_embed, op.K, op.V);
    return VecU8_fmt("BufRBTree_Map%sTo%s", op.K, op.V);
}

NODISCARD VecU8 generate_buf_rbtree_Map_template_instantiation(map_instantiation_op op){
    assert(op.V.len > 0);
    map_instantiation_op_fix(&op);
    VecU8 res = VecU8_new();
    VecU8 map_g = get_name_of_buf_rbtree_map_structure(op);
    SpanU8 map = VecU8_to_span(&map_g);

    VecU8 kvp_g = VecU8_fmt("KVP%sTo%s", op.K, op.V);
    codegen_append_buff_rbtree_map__structure_and_simplest_methods(&res, op, map, VecU8_to_span(&kvp_g));
    VecU8_drop(kvp_g);

    // todo: write erase_substitute (only if v is not primitive) using try_insert

    // todo: write pop_substitute using try_insert
    if (!op.v_primitive) {
        VecU8_append_vec(&res, VecU8_fmt(
            "bool %s_erase_substitute(%s* self, %s key, %s value) {\n"                /* map, map, op.K, op.V */
            SPACE "U64 col = %s_try_insert(self, key, value);\n"                      /* map */
            SPACE "if (col == 0)\n"
            SPACE SPACE "return true;\n"
            "%v" "%v"                     /* "" / drop col->key, "" / drop col->value */
            SPACE "self->el.buf[col - 1].key = key;\n"
            SPACE "self->el.buf[col - 1].value = value;\n"
            SPACE "return false;\n"
            "}\n\n",
            map, map, op.K, op.V, map,
            op.k_primitive ? vcstr("") : VecU8_fmt(SPACE "%s_drop(self->el.buf[col - 1].key);\n", op.K),
            op.v_primitive ? vcstr("") : VecU8_fmt(SPACE "%s_drop(self->el.buf[col - 1].value);\n", op.V)));
    }

    if (op.pop_substitute) {
        VecU8_append_vec(&res, VecU8_fmt(
            "Option%s %s_pop_substitute(%s* self, %s key, %s value) {\n"    /* op.V, map, map, op.K, op.V */
            SPACE "U64 col = %s_try_insert(self, key, value);\n"            /* map */
            SPACE "if (col == 0)\n"
            SPACE SPACE "return None_%s();\n"                               /* op.V */
            "%s saved = self->el.buf[col - 1].value;\n"                     /* op.V */
            "%v"                                                            /* "" / drop col->key */
            SPACE "self->el.buf[col - 1].key = key;\n"
            SPACE "self->el.buf[col - 1].value = value;\n"
            SPACE "return Some_%s(saved);\n"                                /* op.V */
            "}\n\n",
            op.V, map, map, op.K, op.V,
            map, op.V,
            op.V,
            op.k_primitive ? vcstr("") : VecU8_fmt(SPACE "%s_drop(self->el.buf[col - 1].key);\n", op.K),
            op.V));
    }

    // todo: write _pop_by_iter method

    // todo: write _pop method

    if (op.at)
        codegen_append_buf_rbtree_map__method_at(&res, op, map, false);
    if (op.mat)
        codegen_append_buf_rbtree_map__method_at(&res, op, map, true);

    /* These functions break my design and return answer through pointers given in arguments. For greater good ofk */
    codegen_append_buff_rbtree_map__method_at_iter(&res, op, map, false);
    codegen_append_buff_rbtree_map__method_at_iter(&res, op, map, true);

    return res;
}

void generate_buf_rbtree_Map_templ_inst_eve_header(SpanU8 layer, SpanU8 bonus_ns, map_instantiation_op op) {
    generate_SOME_templ_inst_eve_header(layer, bonus_ns,
        generate_buf_rbtree_Map_template_instantiation(op), get_name_of_buf_rbtree_map_structure(op));
}

void generate_buf_rbtree_Map_templ_inst_guarded_header(
    SpanU8 layer, SpanU8 bonus_ns, SpanU8 dependencies, map_instantiation_op op
    ){
    VecU8 all_dependencies = VecU8_fmt("%v%s\n",
        codegen_include_relative_to_root(bonus_ns, cstr("src/l1_5/core/buff_rb_tree_node.h")), dependencies);
    generate_SOME_templ_inst_guarded_header(layer, bonus_ns, all_dependencies,
        generate_buf_rbtree_Map_template_instantiation(op), get_name_of_buf_rbtree_map_structure(op));
}

#endif
