inline auto llvm_type_gen(std::string type_id, llvm::LLVMContext& llvm_context)
    -> llvm::Type *
{
    if (type_id == "double") {
        return llvm::Type::getDoubleTy(llvm_context);
    }
    else {
        printf("Type %s is not a valid . Stopping.", type_id.c_str());
        exit(4);
    }

    return nullptr;
}

inline auto convert_args_to_types(std::vector<VarInitAST> arguments,
                                  llvm::LLVMContext& llvm_context)
    -> std::vector<llvm::Type*>
{
    std::vector<llvm::Type*> arg_types;

    for (const auto& arg : arguments) {
        auto value = arg.type.value;
        auto* type = llvm_type_gen(value, llvm_context);
        if (!type) {
            arg_types.clear();
            break;
        }
        arg_types.push_back(type);
    }

    return arg_types;
}

inline auto make_function_type(llvm::LLVMContext& context,
                               std::string return_id,
                               std::vector<VarInitAST> arguments,
                               bool has_vargs) -> llvm::FunctionType *
{
    // Convert the string type ids into real llvm::Types.
    auto* return_type = llvm_type_gen(return_id, context);
    auto arg_types = convert_args_to_types(arguments, context);

    if (arg_types.size() != arguments.size()) {
        printf("Size mis-match, type conversion failed.\n");
        return nullptr;
    }

    auto* function_type
        = llvm::FunctionType::get(return_type, arg_types, has_vargs);

    return function_type;
}

inline auto make_function(llvm::LLVMContext& context, llvm::Module* module,
                          std::string function_name, std::string return_id,
                          std::vector<VarInitAST> arguments, bool has_vargs)
    -> llvm::Function *
{
    auto* function_type
        = make_function_type(context, return_id, arguments, has_vargs);

    if (!function_type) {
        printf("Failed to create function type.\n");
        return nullptr;
    }

    auto* function = llvm::Function::Create(
        function_type, llvm::Function::ExternalLinkage, function_name, module);

    unsigned Idx = 0;
    for (auto &arg : function->args()) {
        auto name = arguments[Idx++].name.value;
        arg.setName(name);
    }

    return function;
}

