#ifndef prototype1_src_l1_codegen_list_template_inst_h
#define prototype1_src_l1_codegen_list_template_inst_h

#include "codegen.h"

typedef struct {
    SpanU8 T;
    bool t_primitive;
    bool t_clonable;
} list_instantiation_op;

NODISCARD VecU8 generate_List_template_instantiation(list_instantiation_op op, bool gen_node_declaration){
    if (op.t_primitive)
        op.t_clonable = true;
    assert(op.T.len > 0);

    VecU8 res = VecU8_new();
    if (gen_node_declaration) {
        VecU8_append_vec(&res, VecU8_fmt("typedef struct ListNode%s ListNode%s;\n", op.T, op.T));
    }
    VecU8_append_vec(&res, VecU8_fmt(
        "struct ListNode%s {\n"                       /* op.T */
        SPACE "ListNode%s* prev;\n"                    /* op.T */
        SPACE "ListNode%s* next;\n"                   /* op.T */
        SPACE "%s el;\n"                              /* op.T */
        "};\n\n", op.T, op.T, op.T, op.T));

    VecU8_append_vec(&res, VecU8_fmt(
        "typedef struct {\n"
        SPACE "ListNode%s* first;\n"                 /* op.T */
        "} List%s;\n\n",                             /* op.T */
        op.T, op.T));
    VecU8_append_vec(&res, VecU8_fmt(
        "#define List%s_new() {0}\n\n"                           /* op.T */
        "void List%s_drop(List%s self) {\n"                    /* op.T, op.T */
        SPACE "ListNode%s* cur = self.first;\n"                  /* op.T */
        SPACE "while (cur){\n"
        SPACE SPACE "ListNode%s* next = cur->next;\n"          /* op.T */
        "%v"                                             /* "" / %s_drop(cur->el) */
        SPACE SPACE "free(cur);\n"
        SPACE SPACE "cur = next;\n"
        SPACE "}\n"
        "}\n\n",
        op.T, op.T, op.T, op.T, op.T,
        op.t_primitive ? vcstr("") : VecU8_fmt(SPACE SPACE "%s_drop(cur->el);\n", op.T)));
    VecU8_append_vec(&res, VecU8_fmt(
        "ListNode%s* List%s_insert(List%s* self, %s el) {\n"          /* op.T, op.T, op.T, op.T */
        SPACE "ListNode%s* new_node = safe_malloc(sizeof(ListNode%s));\n"       /* op.T, op.T */
        SPACE "new_node->prev = NULL;\n"
        SPACE "new_node->next = self->first;\n"
        SPACE "new_node->el = el;\n"
        SPACE "if (self->first)\n"
        SPACE SPACE "self->first->prev = new_node;\n"
        SPACE "self->first = new_node;\n"
        SPACE "return new_node;\n"
        "}\n\n", op.T, op.T, op.T, op.T, op.T, op.T));
    VecU8_append_vec(&res, VecU8_fmt(
        "void List%s_insert_node(List%s* self, ListNode%s* new_node) {\n"          /* op.T, op.T, op.T */
        SPACE "new_node->prev = NULL;\n"
        SPACE "new_node->next = self->first;\n"
        SPACE "if (self->first)\n"
        SPACE SPACE "self->first->prev = new_node;\n"
        SPACE "self->first = new_node;\n"
        "}\n\n", op.T, op.T, op.T));
    VecU8_append_vec(&res, VecU8_fmt(
        "void List%s_erase_by_it(List%s* self, ListNode%s* it) {\n"    /* op.T, op.T, op.T */
        SPACE "if (it->prev)\n"
        SPACE SPACE "it->prev->next = it->next;\n"
        SPACE "else\n"
        SPACE SPACE "self->first = it->next;\n"
        SPACE "if (it->next)\n"
        SPACE SPACE "it->next->prev = it->prev;\n"
        "%v"                                                           /* "" / %s_drop(it->el) */
        SPACE "free(it);\n"
        "}\n\n",
        op.T, op.T, op.T,
        op.t_primitive ? vcstr("") : VecU8_fmt(SPACE "%s_drop(it->el);\n", op.T)));
    VecU8_append_vec(&res, VecU8_fmt(
        "void List%s_sink(List%s* self) {\n"                    /* op.T, op.T */
        SPACE "ListNode%s* cur = self->first;\n"                  /* op.T */
        SPACE "while (cur){\n"
        SPACE SPACE "ListNode%s* next = cur->next;\n"          /* op.T */
        "%v"                                                /* "" / %s_drop(cur->el) */
        SPACE SPACE "free(cur);\n"
        SPACE SPACE "cur = next;\n"
        SPACE "}\n"
        SPACE "self->first = NULL;\n"
        "}\n\n",
        op.T, op.T, op.T, op.T,
        op.t_primitive ? vcstr("") : VecU8_fmt(SPACE SPACE "%s_drop(cur->el);\n", op.T)));
    return res;
}


void generate_List_templ_inst_eve_header(
    SpanU8 layer, SpanU8 bonus_ns, list_instantiation_op op, bool gen_node_declaration) {
    generate_SOME_templ_inst_eve_header(layer, bonus_ns,
        generate_List_template_instantiation(op, gen_node_declaration), VecU8_fmt("List%s", op.T));
}

void generate_List_templ_inst_guarded_header(
    SpanU8 layer, SpanU8 bonus_ns, SpanU8 dependencies, list_instantiation_op op
    ){
    VecU8 all_dependencies = VecU8_fmt("%v%s\n",
        codegen_include_relative_to_root(bonus_ns, cstr("src/l1/core/utils.h")), dependencies);
    generate_SOME_templ_inst_guarded_header(layer, bonus_ns, all_dependencies,
        generate_List_template_instantiation(op, true), VecU8_fmt("List%s", op.T));
}


#endif