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 = 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;
  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>();
};