#pragma once
#include <string>
#include <vector>
#include <cmath>

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

    void Visit(const ClassDeclarationNode* node) override {
        my_expected_code.push_back(spaceit + "class "
            + node->ClassName() + " {");

        if (!node->PublicFields().empty()) {
            my_expected_code.push_back(spaceit + "  public:");
            spaceit += "    ";
            for (int i = 0; abs(i) < abs(node->PublicFields().size()); i++) {
                node->PublicFields()[i]->Visit(this);
                my_expected_code[my_expected_code.size() - 1] += ";";
            }
            spaceit = spaceit.substr(0, spaceit.size() - 4);
        }

        if (!node->ProtectedFields().empty()) {
            my_expected_code.push_back("");
            my_expected_code.push_back(spaceit + "  protected:");
            spaceit += "    ";
            for (int i = 0; abs(i) < abs(node->ProtectedFields().size()); i++) {
                node->ProtectedFields()[i]->Visit(this);
                my_expected_code[my_expected_code.size() - 1] += ";";
            }
            spaceit = spaceit.substr(0, spaceit.size() - 4);
        }

        if (!node->PrivateFields().empty()) {
            my_expected_code.push_back("");
            my_expected_code.push_back(spaceit + "  private:");
            spaceit += "    ";
            for (int i = 0; abs(i) < abs(node->PrivateFields().size()); i++) {
                node->PrivateFields()[i]->Visit(this);
                my_expected_code[my_expected_code.size() - 1] += ";";
            }
            spaceit = spaceit.substr(0, spaceit.size() - 4);
        }
        my_expected_code.push_back(spaceit + "}");
        if (spaceit == "") {
            my_expected_code[my_expected_code.size() - 1] += ";";
        }
    }
    void Visit(const VarDeclarationNode* node) override {
        my_expected_code.push_back(spaceit + node->TypeName()
            + " " + node->VarName());
    }
    void Visit(const MethodDeclarationNode* node) override {
        my_expected_code.push_back(spaceit + node->ReturnTypeName()
            + " " + node->MethodName() + "(");
        for (int i = 0; i < static_cast<int>(node->Arguments().size()); i++) {
            Visit(node->Arguments()[i]);
            my_expected_code[my_expected_code.size() - 2] +=
                my_expected_code[my_expected_code.size() - 1].
                substr(spaceit.size(),
                    my_expected_code[my_expected_code.size() - 1].size()
                    - spaceit.size());
            if (abs(i) < abs(node->Arguments().size() - 1)) {
                my_expected_code[my_expected_code.size() - 2] += ", ";
            }
            my_expected_code.pop_back();
        }
        my_expected_code[my_expected_code.size() - 1] += ")";
    }



    const std::vector<std::string>& GetFormattedCode() const {
         std::cout << "";
        return my_expected_code;
    }

 private:
    std::vector<std::string> my_expected_code;
    std::string spaceit = "";
};