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())
            runThroughBlock("public:", node->PublicFields());
        if (!node->ProtectedFields().empty()) {
            cachedCode.emplace_back("");
            runThroughBlock("protected:", node->ProtectedFields());
        }
        if (!node->PrivateFields().empty()) {
            cachedCode.emplace_back("");
            runThroughBlock("private:", node->PrivateFields());
        }
        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 = static_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;
    void runThroughBlock(const std::string& blockName,
        const std::vector<BaseNode*>& fields) {
        cachedCode.emplace_back(getCurrentIdent() + blockName);
        ident_++;
        for (auto field : fields)
            Visit(field);
        ident_--;
    }
    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>();
};