#include <iostream>
#include <cassert>
#include <string>
#include <memory>
#include <vector>
#include <fstream>
#include <sstream>

class FormatVisitor : public BaseVisitor {
public:
    void Visit(const BaseNode* node) override {
        node->Visit(this);
    }

    void Visit(const ClassDeclarationNode* node) override {
        std::ostringstream line;
        line << getCurrentIdent() << "class " << node->ClassName() << " {";
        cachedCode.emplace_back(line.str());
        ident_++;
        if (!node->PublicFields().empty()) {
            cachedCode.emplace_back(getCurrentIdent() + "public:");
            ident_++;
            for (auto field : node->PublicFields())
                Visit(field);
            ident_--;
        }
        if (!node->ProtectedFields().empty()) {
            runThroughBlock("protected:", node->ProtectedFields());
            cachedCode.emplace_back(getCurrentIdent() + "protected:");
            ident_++;
            for (auto field : node->ProtectedFields())
                Visit(field);
            ident_--;
        }
        if (!node->PrivateFields().empty()) {
            cachedCode.emplace_back("");
            cachedCode.emplace_back(getCurrentIdent() + "private:");
            ident_++;
            for (auto field : node->PrivateFields())
                Visit(field);
            ident_--;
        }
        ident_--;
        cachedCode.emplace_back(getCurrentIdent() + "};");
    };
    void Visit(const VarDeclarationNode* node) override {
        cachedCode.emplace_back(getCurrentIdent() + node->TypeName()
            + ' ' + node->VarName() + ';');
    };
    void Visit(const MethodDeclarationNode* node) override {
        std::ostringstream line;
        line << getCurrentIdent() << node->ReturnTypeName() << ' '
            << node->MethodName() << '(';
        for (uint i = 0; i < node->Arguments().size(); ++i) {
            if (i != 0) line << ", ";
            auto arg = reinterpret_cast<VarDeclarationNode*>(node->Arguments()[i]);
            line << arg->TypeName() << ' ' << arg->VarName();
        }
        line << ");";
        cachedCode.emplace_back(line.str());
    };

    const std::vector<std::string>& GetFormattedCode() const {
        return cachedCode;
    }

private:
    int ident_ = 0;
    std::string getCurrentIdent() const {
        std::ostringstream res;
        for (int i = 0; i < ident_; ++i)
            res << "  ";
        return res.str();
    }
    std::vector<std::string> cachedCode = std::vector<std::string>();
};