#include <vector>
#include <string>
#include <utility>


using std::vector;
using std::string;

class FormatVisitor
:
public BaseVisitor{
 private:
    vector < string > code_{};
    uint32_t cur_pad{};
    uint32_t cur_str{};
    int const classSpace = 0;
    int const classPadding = 4;
    string startDeclaration(const string& type, const string& name) {
      string
      str(type + " " + name);
      return str;
    }
    string stringWithPadd(const string& sourceStr, uint32_t param1,
    const string& param2, char param3) {
      string str = sourceStr;
      str.insert(param1, param2, param3);
      return str;
    }
 public:
    void Visit(const BaseNode* node) override {
      node->Visit(this);
    }
    void Visit(const ClassDeclarationNode* node) override {
      auto tmp = startDeclaration("class", node->ClassName()) + " {";
      code_[cur_str] += tmp;

      tmp = stringWithPadd(" public:", 0, cur_pad, ' ');
      code_.push_back(std::move(tmp));

      cur_str += classSpace;
      cur_pad += classPadding;

      for (auto field : node->PublicFields()) {
        cur_str++;
        auto ss = stringWithPadd(string(""), 0, cur_pad, ' ');
        code_.push_back(std::move(ss));
        Visit(field);
        code_[cur_str].push_back(';');
      }

      if (!node->ProtectedFields().empty()) {
        code_.push_back("");
        code_.push_back(" protected:");
        cur_str += 2;

        for (auto field : node->ProtectedFields()) {
          cur_str++;
          auto ss = stringWithPadd(string(""), 0, cur_pad, ' ');

          code_.push_back(std::move(ss));
          Visit(field);
          code_[cur_str].push_back(';');
        }
      }
      if (node->PrivateFields().size() != 0) {
        code_.push_back("");
        code_.push_back(" private:");
        cur_str++;
        cur_str++;
        for (auto field : node->PrivateFields()) {
          cur_str++;
          auto ss = stringWithPadd(string(""), 0, cur_pad, ' ');
          code_.push_back(std::move(ss));
          Visit(field);
          code_[cur_str].push_back(';');
        }
      }
      cur_pad -= classPadding;
      auto ss = stringWithPadd(string("}"), 0, cur_pad, ' ');

      code_.push_back(std::move(ss))
      cur_str++;
      if (0 == cur_pad)
        code_[cur_str].push_back(';');
    };
    void Visit(const VarDeclarationNode* node) override {
      code_[cur_str] += startDeclaration(node->TypeName(), node->VarName());
    };
    void Visit(const MethodDeclarationNode* node) override {
      string tmp = startDeclaration(node->ReturnTypeName(), node->MethodName());
      tmp += "(";
      code_[cur_str] += tmp;
      for (auto*args : node->Arguments()) {
        Visit(args);
        code_[cur_str] += ", ";
      }

      if (!node->Arguments().empty()) {
        code_[cur_str].pop_back();
        code_[cur_str].pop_back();
      }
      code_[cur_str].push_back(')');
    };
    const std::vector<std::string>& GetFormattedCode() const {
      return code_;
    }
};