#include <ASTSymbolTableBuilder.hpp>
#include <SymbolTable.hpp>

#include <PEGGrammar.hpp>

void
ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable>& symbol_table)
{
  if (n.is<language::bloc>() or (n.is<language::for_statement>())) {
    if (!n.children.empty()) {
      std::shared_ptr bloc_symbol_table = std::make_shared<SymbolTable>(symbol_table);
      n.m_symbol_table                  = bloc_symbol_table;
      for (auto& child : n.children) {
        this->buildSymbolTable(*child, bloc_symbol_table);
      }
    }
  } else {
    n.m_symbol_table = symbol_table;
    if (n.has_content()) {
      if (n.is<language::declaration>()) {
        const std::string& symbol = n.children[1]->string();
        auto [i_symbol, success]  = symbol_table->add(symbol);
        if (not success) {
          std::ostringstream error_message;
          error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << '\'' << " was already defined!";
          throw parse_error(error_message.str(), std::vector{n.begin()});
        }
      } else if (n.is<language::name>()) {
        auto [i_symbol, found] = symbol_table->find(n.string());
        if (not found) {
          std::ostringstream error_message;
          error_message << "undefined symbol '" << rang::fg::red << n.string() << rang::fg::reset << '\'';
          throw parse_error(error_message.str(), std::vector{n.begin()});
        }
      }
    }

    for (auto& child : n.children) {
      this->buildSymbolTable(*child, symbol_table);
    }
  }
}

ASTSymbolTableBuilder::ASTSymbolTableBuilder(ASTNode& node)
{
  Assert(node.is_root());
  std::shared_ptr symbol_table = std::make_shared<SymbolTable>();

  node.m_symbol_table = symbol_table;

  this->buildSymbolTable(node, symbol_table);
  std::cout << " - checked symbols declaration\n";
}