#include "Evalvisitor.h"

#include <any>
#include <iostream>

#include "Python3ParserBaseVisitor.h"
#include "int2048/int2048.h"
#include "names.h"
#include "utils.h"

VariableContainer Variables;
FucntionContainer Functions;

std::any EvalVisitor::visitFile_input(Python3Parser::File_inputContext *ctx) {
  return visitChildren(ctx);
}

std::any EvalVisitor::visitFuncdef(Python3Parser::FuncdefContext *ctx) {
  std::vector<ParaArguItemType> paraVec =
      std::any_cast<std::vector<ParaArguItemType>>(
          visitParameters(ctx->parameters()));
  std::string name = ctx->NAME()->getText();
  Functions.AddFunction(name, FunctionItem(ctx->suite(), paraVec));
  return NoneType();
}

std::any EvalVisitor::visitParameters(Python3Parser::ParametersContext *ctx) {
  if (!ctx->typedargslist())
    return std::vector<ParaArguItemType>();
  else
    return visitTypedargslist(ctx->typedargslist());
}

std::any EvalVisitor::visitTypedargslist(
    Python3Parser::TypedargslistContext *ctx) {
  auto tfpdef_list = ctx->tfpdef();
  auto default_value_list = ctx->test();
  const int lenght_delta = tfpdef_list.size() - default_value_list.size();
  std::vector<ParaArguItemType> res;
  res.reserve(tfpdef_list.size());
  for (int i = 0; i < lenght_delta; i++)
    res.emplace_back(tfpdef_list[i]->NAME()->getText(), nullptr);
  for (int i = lenght_delta; i < tfpdef_list.size(); i++)
    res.emplace_back(tfpdef_list[i]->NAME()->getText(),
                     DeQuate(visitTest(default_value_list[i - lenght_delta]),
                             Variables));
  return res;
}

std::any EvalVisitor::visitTfpdef(Python3Parser::TfpdefContext *ctx) {
  return ctx->NAME()->getText();
}

std::any EvalVisitor::visitStmt(Python3Parser::StmtContext *ctx) {
  if (ctx->compound_stmt()) return visitCompound_stmt(ctx->compound_stmt());
  return visitSimple_stmt(ctx->simple_stmt());
}

std::any EvalVisitor::visitSimple_stmt(Python3Parser::Simple_stmtContext *ctx) {
  return visitSmall_stmt(ctx->small_stmt());
}

std::any EvalVisitor::visitSmall_stmt(Python3Parser::Small_stmtContext *ctx) {
  if (ctx->expr_stmt())
    return visitExpr_stmt(ctx->expr_stmt());
  else
    return visitFlow_stmt(ctx->flow_stmt());
}

std::any EvalVisitor::visitExpr_stmt(Python3Parser::Expr_stmtContext *ctx) {
  if (!ctx->augassign() && !ctx->ASSIGN(0))
    return visitTestlist(ctx->testlist(0));
  if (ctx->augassign()) {
    auto left_value = visitTestlist(ctx->testlist(0));
    auto right_value = visitTestlist(ctx->testlist(1));
    right_value = DeQuate(right_value, Variables);
    RawVarible *ptr_var = std::any_cast<RawVarible>(&left_value);
    std::vector<std::any> *ptr_vec =
        std::any_cast<std::vector<std::any>>(&left_value);
    if (!ptr_var && !ptr_vec)
      throw InterpretException(
          "visitExpr_stmt: Expect left value or tuple at the left of "
          "augassign");
    if (ptr_var) {
      std::any old_val = DeQuate(*ptr_var, Variables);
      if (ctx->augassign()->ADD_ASSIGN())
        SelfAdd(old_val, right_value);
      else if (ctx->augassign()->SUB_ASSIGN())
        SelfSub(old_val, right_value);
      else if (ctx->augassign()->MULT_ASSIGN())
        SelfMul(old_val, right_value);
      else if (ctx->augassign()->DIV_ASSIGN())
        SelfDiv(old_val, right_value);
      else if (ctx->augassign()->IDIV_ASSIGN())
        SelfDivv(old_val, right_value);
      else if (ctx->augassign()->MOD_ASSIGN())
        SelfMod(old_val, right_value);
      else
        throw FatalError("visitExpr_stmt: Unknown operator at augassign");
      Variables.WriteVariable(ptr_var->name, old_val);
    } else {
      if (ptr_vec->size() !=
          std::any_cast<std::vector<std::any>>(right_value).size())
        throw InterpretException("visitExpr_stmt: Tuple size not match");
      for (int i = 0; i < ptr_vec->size(); i++) {
        ptr_var = std::any_cast<RawVarible>(&(*ptr_vec)[i]);
        if (!ptr_var)
          throw InterpretException(
              "visitExpr_stmt: Expect left value at the left of augassign");
        std::any old_val = DeQuate(*ptr_var, Variables);
        if (ctx->augassign()->ADD_ASSIGN())
          SelfAdd(old_val,
                  std::any_cast<std::vector<std::any>>(right_value)[i]);
        else if (ctx->augassign()->SUB_ASSIGN())
          SelfSub(old_val,
                  std::any_cast<std::vector<std::any>>(right_value)[i]);
        else if (ctx->augassign()->MULT_ASSIGN())
          SelfMul(old_val,
                  std::any_cast<std::vector<std::any>>(right_value)[i]);
        else if (ctx->augassign()->DIV_ASSIGN())
          SelfDiv(old_val,
                  std::any_cast<std::vector<std::any>>(right_value)[i]);
        else if (ctx->augassign()->IDIV_ASSIGN())
          SelfDivv(old_val,
                   std::any_cast<std::vector<std::any>>(right_value)[i]);
        else if (ctx->augassign()->MOD_ASSIGN())
          SelfMod(old_val,
                  std::any_cast<std::vector<std::any>>(right_value)[i]);
        else
          throw FatalError("visitExpr_stmt: Unknown operator at augassign");
        Variables.WriteVariable(ptr_var->name, old_val);
      }
    }
    return nullptr;
  }
  auto testlist_list = ctx->testlist();
  auto val = visitTestlist(testlist_list[testlist_list.size() - 1]);
  val = DeQuate(val, Variables);
  for (int i = testlist_list.size() - 2; i >= 0; i--) {
    auto dest = visitTestlist(testlist_list[i]);
    RawVarible *ptr_dest_single = std::any_cast<RawVarible>(&dest);
    std::vector<std::any> *ptr_dest_tuple =
        std::any_cast<std::vector<std::any>>(&dest);
    if (ptr_dest_single)
      Variables.WriteVariable(ptr_dest_single->name, val);
    else if (ptr_dest_tuple) {
      if (ptr_dest_tuple->size() !=
          std::any_cast<std::vector<std::any>>(val).size())
        throw InterpretException("visitExpr_stmt: Tuple size not match");
      for (int j = 0; j < ptr_dest_tuple->size(); j++) {
        ptr_dest_single = std::any_cast<RawVarible>(&(*ptr_dest_tuple)[j]);
        if (!ptr_dest_single)
          throw InterpretException(
              "visitExpr_stmt: Expect left value at the left of assign");
        Variables.WriteVariable(ptr_dest_single->name,
                                std::any_cast<std::vector<std::any>>(val)[j]);
      }
    } else
      throw InterpretException(
          "visitExpr_stmt: Expect left value or tuple at the left of assign");
  }
  return nullptr;
}

std::any EvalVisitor::visitAugassign(Python3Parser::AugassignContext *ctx) {
  throw FatalError("Function visitAugassign Shouldn't be called");
}

std::any EvalVisitor::visitFlow_stmt(Python3Parser::Flow_stmtContext *ctx) {
  if (ctx->break_stmt()) return visitBreak_stmt(ctx->break_stmt());
  if (ctx->continue_stmt()) return visitContinue_stmt(ctx->continue_stmt());
  if (ctx->return_stmt()) return visitReturn_stmt(ctx->return_stmt());
}

std::any EvalVisitor::visitBreak_stmt(Python3Parser::Break_stmtContext *ctx) {
  return FlowType(BREAK);
}

std::any EvalVisitor::visitContinue_stmt(
    Python3Parser::Continue_stmtContext *ctx) {
  return FlowType(CONTINUE);
}

std::any EvalVisitor::visitReturn_stmt(Python3Parser::Return_stmtContext *ctx) {
  if (!ctx->testlist()) return FlowType(RETURN, std::vector<std::any>());
  std::any val = visitTestlist(ctx->testlist());
  val = DeQuate(val, Variables);
  std::vector<std::any> *val_ptr = std::any_cast<std::vector<std::any>>(&val);
  if (val_ptr) return FlowType(RETURN, *val_ptr);
  std::vector<std::any> tmp;
  tmp.push_back(val);
  // std::cerr << "ready to send return value" << Any2String(val) << std::endl;
  return FlowType(RETURN, tmp);
}

std::any EvalVisitor::visitCompound_stmt(
    Python3Parser::Compound_stmtContext *ctx) {
  if (ctx->if_stmt()) return visitIf_stmt(ctx->if_stmt());
  if (ctx->while_stmt()) return visitWhile_stmt(ctx->while_stmt());
  if (ctx->funcdef()) return visitFuncdef(ctx->funcdef());
}

std::any EvalVisitor::visitIf_stmt(Python3Parser::If_stmtContext *ctx) {
  auto test_list = ctx->test();
  auto suite_list = ctx->suite();
  if (test_list.size() - suite_list.size() != -1 &&
      test_list.size() - suite_list.size() != 0)
    throw InterpretException("test and suite doesn't match in If_stmt");
  for (int i = 0; i < test_list.size(); i++)
    if (Any2Bool(DeQuate(visitTest(test_list[i]), Variables)))
      return visitSuite(suite_list[i]);
  if (test_list.size() - suite_list.size() == 1)
    return visitSuite(suite_list[suite_list.size() - 1]);
  return nullptr;
}

std::any EvalVisitor::visitWhile_stmt(Python3Parser::While_stmtContext *ctx) {
  auto test = ctx->test();
  auto suite = ctx->suite();
  while (Any2Bool(DeQuate(visitTest(test), Variables))) {
    auto res = visitSuite(suite);
    FlowType *res_ptr = std::any_cast<FlowType>(&res);
    if (res_ptr) {
      if (res_ptr->Status == BREAK) break;
      if (res_ptr->Status == CONTINUE) continue;
      if (res_ptr->Status == RETURN) return *res_ptr;
    }
  }
  return nullptr;
}

std::any EvalVisitor::visitSuite(Python3Parser::SuiteContext *ctx) {
  if (ctx->simple_stmt()) return visitSimple_stmt(ctx->simple_stmt());
  auto stmt_list = ctx->stmt();
  std::any res;
  for (int i = 0; i < stmt_list.size(); i++) {
    res = visitStmt(stmt_list[i]);
    FlowType *res_ptr = std::any_cast<FlowType>(&res);
    if (res_ptr) return res;
  }
  return res;
}
std::any EvalVisitor::visitTestlist(Python3Parser::TestlistContext *ctx) {
  auto test_list = ctx->test();
  if (test_list.size() == 1) return visitTest(test_list[0]);
  std::vector<std::any> res;
  for (auto item : test_list) {
    res.push_back(visitTest(item));
  }
  return res;
}
std::any EvalVisitor::visitTest(Python3Parser::TestContext *ctx) {
  return visitOr_test(ctx->or_test());
}

std::any EvalVisitor::visitOr_test(Python3Parser::Or_testContext *ctx) {
  auto or_list = ctx->and_test();
  if (or_list.size() == 1) return visitAnd_test(or_list[0]);
  for (auto or_item : or_list) {
    if (Any2Bool(DeQuate(visitAnd_test(or_item), Variables))) return true;
  }
  return false;
}

std::any EvalVisitor::visitAnd_test(Python3Parser::And_testContext *ctx) {
  auto not_list = ctx->not_test();
  if (not_list.size() == 1) return visitNot_test(not_list[0]);
  for (auto not_item : not_list) {
    if (!Any2Bool(DeQuate(visitNot_test(not_item), Variables))) return false;
  }
  return true;
}

std::any EvalVisitor::visitNot_test(Python3Parser::Not_testContext *ctx) {
  if (!ctx->NOT()) return visitComparison(ctx->comparison());
  return !Any2Bool(DeQuate(visitNot_test(ctx->not_test()), Variables));
}

std::any EvalVisitor::visitComparison(Python3Parser::ComparisonContext *ctx) {
  auto arith_expr_list = ctx->arith_expr();
  auto last_val = visitArith_expr(arith_expr_list[0]);
  if (arith_expr_list.size() == 1) return last_val;
  last_val = DeQuate(last_val, Variables);
  auto op_list = ctx->comp_op();
  for (int i = 0; i < op_list.size(); i++) {
    auto cur = visitArith_expr(arith_expr_list[i + 1]);
    cur = DeQuate(cur, Variables);
    bool ok = false;
    if (op_list[i]->GREATER_THAN())
      ok = Greater(last_val, cur);
    else if (op_list[i]->LESS_THAN())
      ok = Less(last_val, cur);
    else if (op_list[i]->EQUALS())
      ok = Equal(last_val, cur);
    else if (op_list[i]->NOT_EQ_2())
      ok = NotEqual(last_val, cur);
    else if (op_list[i]->GT_EQ())
      ok = GreaterEqual(last_val, cur);
    else if (op_list[i]->LT_EQ())
      ok = LessEqual(last_val, cur);
    else
      throw InterpretException("visitComparison: Unknown operator");
    if (!ok) return false;
    last_val = cur;
  }
  return true;
}

std::any EvalVisitor::visitComp_op(Python3Parser::Comp_opContext *ctx) {
  throw FatalError("Function visitComp_op Shouldn't be called");
}

std::any EvalVisitor::visitArith_expr(Python3Parser::Arith_exprContext *ctx) {
  auto term_list = ctx->term();
  auto ans = visitTerm(term_list[0]);
  if (term_list.size() == 1) return ans;
  auto op_list = ctx->addorsub_op();
  ans = DeQuate(ans, Variables);
  for (int i = 0; i < op_list.size(); i++) {
    auto cur = visitTerm(term_list[i + 1]);
    cur = DeQuate(cur, Variables);
    if (op_list[i]->ADD())
      SelfAdd(ans, cur);
    else if (op_list[i]->MINUS())
      SelfSub(ans, cur);
    else
      throw FatalError("visitArith_expr: Unknown operator");
  }
  return ans;
}

std::any EvalVisitor::visitAddorsub_op(Python3Parser::Addorsub_opContext *ctx) {
  throw FatalError("Function visitAddorsub_op Shouldn't be called");
}

std::any EvalVisitor::visitTerm(Python3Parser::TermContext *ctx) {
  auto factor_list = ctx->factor();
  auto ans = visitFactor(factor_list[0]);
  if (factor_list.size() == 1) return ans;
  auto op_list = ctx->muldivmod_op();
  ans = DeQuate(ans, Variables);
  for (int i = 0; i < op_list.size(); i++) {
    auto cur = visitFactor(factor_list[i + 1]);
    cur = DeQuate(cur, Variables);
    if (op_list[i]->STAR())
      SelfMul(ans, cur);
    else if (op_list[i]->DIV())
      SelfDiv(ans, cur);
    else if (op_list[i]->IDIV())
      SelfDivv(ans, cur);
    else if (op_list[i]->MOD())
      SelfMod(ans, cur);
    else
      throw FatalError("visitTerm: Unknown operator");
  }
  return ans;
}

std::any EvalVisitor::visitMuldivmod_op(
    Python3Parser::Muldivmod_opContext *ctx) {
  throw FatalError("Function visitMuldivmod_op Shouldn't be called");
}

std::any EvalVisitor::visitFactor(Python3Parser::FactorContext *ctx) {
  if (ctx->atom_expr()) return visitAtom_expr(ctx->atom_expr());
  std::any res = visitFactor(ctx->factor());
  res = DeQuate(res, Variables);
  if (ctx->MINUS()) res = Neg(res);
  return res;
}

std::any EvalVisitor::visitAtom_expr(Python3Parser::Atom_exprContext *ctx) {
  if (!ctx->trailer()) return visitAtom(ctx->atom());
  std::string func_name = ctx->atom()->NAME()->getText();
  std::vector<ParaArguItemType> args =
      std::any_cast<std::vector<ParaArguItemType>>(
          visitTrailer(ctx->trailer()));
  return Functions.CallFunction(func_name, args, Variables);
}

std::any EvalVisitor::visitTrailer(Python3Parser::TrailerContext *ctx) {
  if (!ctx->arglist()) return std::vector<ParaArguItemType>();
  return visitArglist(ctx->arglist());
}
std::any EvalVisitor::visitArglist(Python3Parser::ArglistContext *ctx) {
  auto argument_list = ctx->argument();
  std::vector<ParaArguItemType> resolved_args;
  for (auto item : argument_list) {
    resolved_args.push_back(
        std::any_cast<ParaArguItemType>(visitArgument(item)));
  }
  return resolved_args;
}

std::any EvalVisitor::visitArgument(Python3Parser::ArgumentContext *ctx) {
  auto arg_list = ctx->test();
  if (arg_list.size() != 1 && arg_list.size() != 2)
    throw FatalError("visitArgument: arg_list.size()!=1&&arg_list!=2");
  if (arg_list.size() == 1)
    return ParaArguItemType("", DeQuate(visitTest(arg_list[0]), Variables));
  else
    return ParaArguItemType(arg_list[0]->getText(),
                            DeQuate(visitTest(arg_list[1]), Variables));
}
std::any EvalVisitor::visitAtom(Python3Parser::AtomContext *ctx) {
  if (ctx->NONE())
    return NoneType();
  else if (ctx->TRUE())
    return true;
  else if (ctx->FALSE())
    return false;
  else if (ctx->NUMBER()) {
    std::string num = ctx->getText();
    if (num.find('.') == std::string::npos)
      return std::move(ZYM::int2048(num));
    else
      return std::stod(num);
  } else if (ctx->test())
    return visitTest(ctx->test());
  else if (ctx->NAME()) {
    return RawVarible(ctx->getText());
  } else {
    auto string_lists = ctx->STRING();
    std::string res;
    for (auto item : string_lists) {
      const std::string &tmp = item->getText();
      res.append(tmp, 1, tmp.size() - 2);
    }
    // std::cerr<<"[Log] The string is : "<<res<<std::endl;
    return res;
  }
}