#pragma once
#include <memory>
#include <string>
#include <vector>
#include <llvm/IR/Function.h>
#include "instruction.hpp"

struct ast;
using ast_ptr = std::unique_ptr<ast>;

struct global_function {
    std::string name;
    std::vector<std::string> params;
    ast_ptr body;

    std::vector<instruction_ptr> instructions;
    llvm::Function* generated_function;

    global_function(std::string n, std::vector<std::string> ps, ast_ptr b)
        : name(std::move(n)), params(std::move(ps)), body(std::move(b)) {}

    void compile();
    void declare_llvm(llvm_context& ctx);
    void generate_llvm(llvm_context& ctx);
};

using global_function_ptr = std::unique_ptr<global_function>;

struct global_constructor {
    std::string name;
    int8_t tag;
    size_t arity;

    global_constructor(std::string n, int8_t t, size_t a)
        : name(std::move(n)), tag(t), arity(a) {}

    void generate_llvm(llvm_context& ctx);
};

using global_constructor_ptr = std::unique_ptr<global_constructor>;

struct global_scope {
    std::map<std::string, int> occurence_count;
    std::vector<global_function_ptr> functions;
    std::vector<global_constructor_ptr> constructors;

    global_function& add_function(std::string n, std::vector<std::string> ps, ast_ptr b);
    global_constructor& add_constructor(std::string n, int8_t t, size_t a);

    void compile();
    void generate_llvm(llvm_context& ctx);

    private:
    std::string mangle_name(const std::string& n);
};
