void generate_atom_reflector(ast::Tree& /*ast*/, ast::Node* atom, Paste_Func* paste_namespace, const char* name_function, const char* tuple_struct_postfix, const char* tuple_id_prefix, Formatter& output)
{
    output.writeln("template <typename V>");
    output.writeln("struct Generated_Type_Reflector<%p::%i_%s, V>", paste_namespace, atom->s_expr->token, tuple_struct_postfix);
    {
        Class_Scope s(output);

        output.writeln("void operator()(const %p::%i_%s& tuple, V& visitor)", paste_namespace, atom->s_expr->token, tuple_struct_postfix);
        {
            Scope s(output, false);

            int param_count = 0;

            for (ast::Node* child = atom->first_child; child != 0; child = child->next_sibling)
            {
                param_count++;
            }

            output.writeln("PLNNR_GENCODE_VISIT_TUPLE_BEGIN(visitor, %p, %s, %s_%i, %d);", paste_namespace, name_function, tuple_id_prefix, atom->s_expr->token, param_count);

            for (int i = 0; i < param_count; ++i)
            {
                output.writeln("PLNNR_GENCODE_VISIT_TUPLE_ELEMENT(visitor, tuple, %d);", i);
            }

            output.writeln("PLNNR_GENCODE_VISIT_TUPLE_END(visitor, %p, %s, %s_%i, %d);", paste_namespace, name_function, tuple_id_prefix, atom->s_expr->token, param_count);
        }
    }
}
void generate_worldstate_reflectors(ast::Tree& ast, ast::Node* worldstate, Formatter& output)
{
    ast::Node* worldstate_namespace = worldstate->first_child;
    plnnrc_assert(worldstate_namespace && ast::is_namespace(worldstate_namespace));

    Paste_Fully_Qualified_Namespace paste_world_namespace(worldstate_namespace);

    output.writeln("template <typename V>");
    output.writeln("struct Generated_Type_Reflector<%p::Worldstate, V>", &paste_world_namespace);
    {
        Class_Scope s(output);

        output.writeln("void operator()(const %p::Worldstate& world, V& visitor)", &paste_world_namespace);
        {
            Scope s(output, false);

            for (ast::Node* atom = worldstate_namespace->next_sibling; atom != 0; atom = atom->next_sibling)
            {
                if (!ast::is_atom(atom))
                {
                    continue;
                }

                output.writeln("PLNNR_GENCODE_VISIT_ATOM_LIST(%p, atom_%i, %i_tuple, visitor);", &paste_world_namespace, atom->s_expr->token, atom->s_expr->token);
            }
        }
    }

    for (ast::Node* atom = worldstate_namespace->next_sibling; atom != 0; atom = atom->next_sibling)
    {
        if (!ast::is_atom(atom))
        {
            continue;
        }

        generate_atom_reflector(ast, atom, &paste_world_namespace, "atom_name", "tuple", "atom", output);
    }
}
void generate_task_type_dispatcher(ast::Tree& ast, ast::Node* domain, Formatter& output)
{
    ast::Node* domain_namespace = domain->first_child;
    plnnrc_assert(domain_namespace && ast::is_namespace(domain_namespace));

    Paste_Fully_Qualified_Namespace paste_namespace(domain_namespace);

    output.writeln("template <typename V>");
    output.writeln("struct Task_Type_Dispatcher<%p::Task_Type, V>", &paste_namespace);
    {
        Class_Scope s(output);
        output.writeln("void operator()(const %p::Task_Type& task_type, void* args, V& visitor)", &paste_namespace);
        {
            Scope s(output, false);
            output.writeln("switch (task_type)");
            {
                Scope s(output, false);

                for (ast::Node* method = domain->first_child; method != 0; method = method->next_sibling)
                {
                    if (!ast::is_method(method))
                    {
                        continue;
                    }

                    ast::Node* method_atom = method->first_child;
                    plnnrc_assert(method_atom);

                    output.writeln("case %p::task_%i:", &paste_namespace, method_atom->s_expr->token);
                    {
                        Indented s(output);

                        const char* atom_id = method_atom->s_expr->token;

                        if (method_atom->first_child)
                        {
                            output.writeln("PLNNR_GENCODE_VISIT_TASK_WITH_ARGS(visitor, %p, task_%i, %i_args);", &paste_namespace, atom_id, atom_id);
                        }
                        else
                        {
                            output.writeln("PLNNR_GENCODE_VISIT_TASK_NO_ARGS(visitor, %p, task_%i);", &paste_namespace, atom_id);
                        }

                        output.writeln("break;");
                    }
                }

                for (Id_Table_Values operators = ast.operators.values(); !operators.empty(); operators.pop())
                {
                    ast::Node* operatr = operators.value();
                    ast::Node* operator_atom = operatr->first_child;

                    output.writeln("case %p::task_%i:", &paste_namespace, operator_atom->s_expr->token);
                    {
                        Indented s(output);

                        const char* atom_id = operator_atom->s_expr->token;

                        if (operator_atom->first_child)
                        {
                            output.writeln("PLNNR_GENCODE_VISIT_TASK_WITH_ARGS(visitor, %p, task_%i, %i_args);", &paste_namespace, atom_id, atom_id);
                        }
                        else
                        {
                            output.writeln("PLNNR_GENCODE_VISIT_TASK_NO_ARGS(visitor, %p, task_%i);", &paste_namespace, atom_id);
                        }

                        output.writeln("break;");
                    }
                }

                output.writeln("default:");
                {
                    Indented s(output);
                    output.writeln("plnnr_assert(false);");
                    output.writeln("break;");
                }
            }
        }
    }
}