#include <string>
#include <vector>

class FormatVisitor: public BaseVisitor {
 public:
  void Visit(const BaseNode* node) override {
    node->Visit(this);
  }
  void Visit(const ClassDeclarationNode* node) override {
    PushLine("class " + node->ClassName() + " {");
    AddTab();
    if (!node->PublicFields().empty()) {
      PushLine("public:");
      AddTab();
      for (BaseNode *next_node : node->PublicFields()) {
        next_node->Visit(this);
      }
      DelTab();
    }
    if (!node->ProtectedFields().empty()) {
      this->data_.emplace_back("");
      PushLine("protected:");
      AddTab();
      for (BaseNode *next_node : node->ProtectedFields()) {
        next_node->Visit(this);
      }
      DelTab();
    }
    if (!node->PrivateFields().empty()) {
      this->data_.emplace_back("");
      PushLine("private:");
      AddTab();
      for (BaseNode *next_node : node->PrivateFields()) {
        next_node->Visit(this);
      }
      DelTab();
    }
    DelTab();
    PushLine("};");
  }
  void Visit(const VarDeclarationNode* node) override {
    std::string line = node->TypeName() + ' ' + node->VarName();
    if (this->parse_args_) {
      AppendLastLine(line);
    } else {
      PushLine(line + ";");
    }
  }
  void Visit(const MethodDeclarationNode* node) override {
    std::string line = node->ReturnTypeName() + " ";
    line += node->MethodName() + "(";
    PushLine(line);
    this->parse_args_ = true;
    const auto &nodes = node->Arguments();
    for (size_t i = 0; i < nodes.size(); ++i) {
      nodes[i]->Visit(this);
      if (i != nodes.size() - 1) {
        AppendLastLine(", ");
      }
    }
    this->parse_args_ = false;
    AppendLastLine(");");
  }

  [[nodiscard]] const std::vector<std::string>& GetFormattedCode() const {
    return this->data_;
  }

 private:
  std::vector<std::string> data_;
  size_t space_on_line_ = 0;
  bool parse_args_ = false;
  const size_t TAB_SIZE = 2;
  void AddTab() {
    this->space_on_line_ += TAB_SIZE;
  }
  void DelTab() {
    this->space_on_line_ -= TAB_SIZE;
  }
  void PushLine(const std::string &line) {
    this->data_.push_back(std::string(this->space_on_line_, ' ') + line);
  }
  void AppendLastLine(const std::string &str) {
    this->data_.back() += str;
  }
};