#pragma once

#include <vector>
#include <string>

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

    void Visit(const ClassDeclarationNode* node) override {
        if (!formatted_code.size()) {
            formatted_code.push_back("class " + node->ClassName() + " {");
        } else {
            std::string spaces;
            bool flag = true;
            std::string word;
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            for (const auto& letter
            : formatted_code[formatted_code.size() - 2]) {
                if (letter != ' ') {
                    flag = false;
                    word += letter;
                } else if (flag) {
                    spaces+=' ';
                } else {
                    break;
                }
            }
            if (word == "private:" ||
                word == "public:" ||
                word == "protected:") {
                formatted_code.back() += spaces + "  ";
            } else {
                formatted_code.back() += spaces;
            }
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            formatted_code.back() += "class " + node->ClassName() + " {";
        }
        if (node->PublicFields().size()) {
            formatted_code.push_back("public:");
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            formatted_code.back() += findWord() + "  ";
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            for (const auto& fields : node->PublicFields()) {
                formatted_code.push_back("");
                Visit(fields);
                if (formatted_code.back().back() != ';')
                    formatted_code.back() += ";";
                std::string spaces;
                bool flag = true;
                std::string word;
                std::reverse(formatted_code.back().begin(),
                formatted_code.back().end());
                for (const auto& letter
                : formatted_code[formatted_code.size() - 2]) {
                    if (letter != ' ') {
                        flag = false;
                        word += letter;
                    } else if (flag) {
                        spaces += ' ';
                    } else {
                        break;
                    }
                }
                if (word == "private:" ||
                    word == "public:" ||
                    word == "protected:") {
                    formatted_code.back() += spaces + "  ";
                } else {
                    formatted_code.back() += spaces;
                }
                reverse(formatted_code.back().begin(),
                formatted_code.back().end());
            }
            if (node->ProtectedFields().size() || node->PrivateFields().size())
                formatted_code.push_back("");
        }
        if (node->ProtectedFields().size()) {
            formatted_code.push_back("protected:");
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            formatted_code.back() += findWord() + "  ";
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            for (const auto& fields : node->ProtectedFields()) {
                formatted_code.push_back("");
                Visit(fields);
                if (formatted_code.back().back() != ';')
                    formatted_code.back() += ";";
                std::string spaces;
                bool flag = true;
                std::string word;
                reverse(formatted_code.back().begin(),
                formatted_code.back().end());
                for (const auto& letter
                : formatted_code[formatted_code.size() - 2]) {
                    if (letter != ' ') {
                        flag = false;
                        word += letter;
                    } else if (flag) {
                        spaces += ' ';
                    } else {
                        break;
                    }
                }
                if (word == "private:" ||
                    word == "public:" ||
                    word == "protected:") {
                    formatted_code.back() += spaces + "  ";
                } else {
                    formatted_code.back() += spaces;
                }
                std::reverse(formatted_code.back().begin(),
                formatted_code.back().end());
            }
            if (node->PrivateFields().size())
                formatted_code.push_back("");
        }
        if (node->PrivateFields().size()) {
            formatted_code.push_back("private:");
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            formatted_code.back() += findWord() + "  ";
            reverse(formatted_code.back().begin(),
            formatted_code.back().end());
            for (const auto& fields : node->PrivateFields()) {
                formatted_code.push_back("");
                Visit(fields);
                if (formatted_code.back().back() != ';')
                    formatted_code.back() += ";";
                reverse(formatted_code.back().begin(),
                formatted_code.back().end());
                if (formatted_code.back()[1] != '}') {
                    reverse(formatted_code.back().begin(),
                    formatted_code.back().end());
                    std::string spaces;
                    bool flag = true;
                    std::string word;
                    reverse(formatted_code.back().begin(),
                    formatted_code.back().end());
                    for (const auto& letter
                    : formatted_code[formatted_code.size() - 2]) {
                        if (letter != ' ') {
                            flag = false;
                            word += letter;
                        } else if (flag) {
                            spaces += ' ';
                        } else {
                            break;
                        }
                    }
                    if (word == "private:" ||
                        word == "public:" ||
                        word == "protected") {
                        formatted_code.back() += spaces + "  ";
                    } else {
                        formatted_code.back() += spaces;
                    }
                }
                reverse(formatted_code.back().begin(),
                formatted_code.back().end());
            }
        }
        formatted_code.push_back("");
        formatted_code.back() += (findWordAnother());
        formatted_code.back() += "};";
        counter++;
    }
    void Visit(const VarDeclarationNode* node) override {
        formatted_code.back() += node->TypeName() + " " + node->VarName();
    }
    void Visit(const MethodDeclarationNode* node) override {
        formatted_code.back() += node->ReturnTypeName()
            + " "
            + node->MethodName()
            + "(";
        for (std::uint32_t i = 0; i < node->Arguments().size()-1
        && node->Arguments().size(); ++i) {
            Visit(node->Arguments()[i]);
            formatted_code.back() += ", ";
        }
        if (node->Arguments().size()) {
            Visit(node->Arguments().back());
        }
        formatted_code.back() += ")";
    }

    std::string findWord() {
        std::string word;
        reverse(formatted_code.begin(), formatted_code.end());
        for (const auto& word_ : formatted_code) {
            std::string true_word, word = "";
            std::uint16_t j = 0;
            while (word_[j] == ' ') {
                j++;
                word += " ";
            }
            if (word_.size() > j + 5) {
                for (std::uint32_t i = j; i < j + 5; ++i) {
                    true_word += word_[i];
                }
                if (true_word == "class") {
                    std::reverse(formatted_code.begin(), formatted_code.end());
                    return word;
                }
            }
        }
        reverse(formatted_code.begin(), formatted_code.end());
        return word + "  ";
    }

    std::string findWordAnother() {
        std::string word;
        std::uint32_t counter_copy = 0;
        reverse(formatted_code.begin(), formatted_code.end());
        for (const auto& word_ : formatted_code) {
            std::string true_word, word = "";
            std::uint16_t j = 0;
            while (word_[j] == ' ') {
                j++;
                word += " ";
            }
            if (word_.size() > j + 5) {
                for (std::uint32_t i = j; i < j + 5; ++i) {
                    true_word += word_[i];
                }
                if (true_word == "class") {
                    if (counter_copy == counter) {
                        reverse(formatted_code.begin(),
                        formatted_code.end());
                        return word;
                    }
                    counter_copy++;
                }
            }
        }
        reverse(formatted_code.begin(), formatted_code.end());
        return word;
    }

    const std::vector<std::string>& GetFormattedCode() const {
        return formatted_code;
    }

 private:
     std::vector<std::string> formatted_code;
     std::uint32_t counter = 0;
};