#include <PugsParser.hpp>

#include <PugsAssert.hpp>

#include <fstream>
#include <iostream>
#include <unordered_map>
#include <variant>

#include <rang.hpp>

#include <pegtl/analyze.hpp>
#include <pegtl/contrib/parse_tree.hpp>
#include <pegtl/contrib/parse_tree_to_dot.hpp>

#include <ASTNode.hpp>

#include <ASTBuilder.hpp>
#include <PEGGrammar.hpp>
#include <SymbolTable.hpp>

#include <ASTModulesImporter.hpp>

#include <ASTNodeDataTypeBuilder.hpp>
#include <ASTNodeDataTypeChecker.hpp>

#include <ASTNodeJumpPlacementChecker.hpp>

#include <ASTNodeExpressionBuilder.hpp>

#include <ASTSymbolInitializationChecker.hpp>
#include <ASTSymbolTableBuilder.hpp>

#include <ASTNodeEmptyBlockCleaner.hpp>

#include <ASTNodeDeclarationToAffectationConverter.hpp>
#include <ASTNodeTypeCleaner.hpp>

#include <ASTDotPrinter.hpp>
#include <ASTPrinter.hpp>

void
parser(const std::string& filename)
{
  const size_t grammar_issues = analyze<language::grammar>();

  std::cout << rang::fgB::yellow << "grammar_issues=" << rang::fg::reset << grammar_issues << '\n';

  std::cout << rang::style::bold << "Parsing file " << rang::style::reset << rang::style::underline << filename
            << rang::style::reset << " ...\n";

  std::unique_ptr<ASTNode> root_node;
  read_input input(filename);
  try {
    root_node = ASTBuilder::build(input);

    ASTModulesImporter{*root_node};
    ASTNodeTypeCleaner<language::import_instruction>{*root_node};

    ASTSymbolTableBuilder{*root_node};

    ASTSymbolInitializationChecker{*root_node};

    ASTNodeDataTypeBuilder{*root_node};

    {
      std::string dot_filename{"parse_tree.dot"};
      std::ofstream fout(dot_filename);
      ASTDotPrinter dot_printer{*root_node};
      fout << dot_printer;
      std::cout << "   AST dot file: " << dot_filename << '\n';
    }

    ASTNodeDataTypeChecker{*root_node};

    ASTNodeJumpPlacementChecker{*root_node};

    // optimizations
    ASTNodeDeclarationToAffectationConverter{*root_node};

    ASTNodeTypeCleaner<language::declaration>{*root_node};
    ASTNodeTypeCleaner<language::let_declaration>{*root_node};

    ASTNodeEmptyBlockCleaner{*root_node};

    ASTNodeExpressionBuilder{*root_node};

    std::cout << ASTPrinter{*root_node} << '\n';

    ExecutionPolicy exec_all;
    root_node->execute(exec_all);
    std::cout << *(root_node->m_symbol_table) << '\n';
  }
  catch (const parse_error& e) {
    const auto p = e.positions.front();
    std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.byte_in_line << ": " << rang::style::reset
              << rang::fgB::red << "error: " << rang::fg::reset << rang::style::bold << e.what() << rang::style::reset
              << '\n'
              << input.line_at(p) << '\n'
              << std::string(p.byte_in_line, ' ') << rang::fgB::yellow << '^' << rang::fg::reset << std::endl;
    std::exit(1);
  }

  std::cout << "Parsed: " << filename << '\n';
}