#ifndef UTILS_H
#define UTILS_H
#include <any>
#include <exception>
#include <iomanip>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

#include "Evalvisitor.h"
#include "Python3ParserBaseVisitor.h"
#include "int2048/int2048.h"
class InterpretException : public std::exception {
 public:
  InterpretException() {}
  InterpretException(const char *message) : m_message(message) {}

  const char *what() const noexcept override { return m_message; }

  ~InterpretException() noexcept override {}

 private:
  const char *m_message;
};

class FatalError : public std::exception {
 public:
  FatalError(const char *message) : m_message(message) {}

  const char *what() const noexcept override { return m_message; }

 private:
  const char *m_message;
};
enum FlowControlStatusType { BREAK, CONTINUE, RETURN };
struct FlowType {
  FlowControlStatusType Status;
  // TODO: return lists
  std::vector<std::any> ReturnValueLists;
  FlowType() {}
  FlowType(FlowControlStatusType Status) : Status(Status) {}
  FlowType(FlowControlStatusType Status, std::vector<std::any> ReturnValueLists)
      : Status(Status), ReturnValueLists(ReturnValueLists) {}
};

struct NoneType {};

ZYM::int2048 Any2Int(const std::any &value);
double Any2Float(const std::any &value);
std::string Any2String(const std::any &value);
bool Any2Bool(const std::any &value);

struct RawVarible {
  std::string name;
  RawVarible() {}
  RawVarible(const std::string &name) : name(name) {}
};
struct ParaArguItemType {
  std::string name;
  std::any value;
  ParaArguItemType() {}
  ParaArguItemType(const std::string &name) : name(name) {}
  ParaArguItemType(const std::string &name, const std::any &value)
      : name(name), value(value) {}
};
struct FunctionItem {
  Python3Parser::SuiteContext *code_address;
  std::vector<ParaArguItemType> para_list;
  FunctionItem() {}
  FunctionItem(Python3Parser::SuiteContext *code_address,
               const std::vector<ParaArguItemType> &para_list)
      : code_address(code_address), para_list(para_list) {}
};
class VariableContainer {
  std::unordered_map<std::string, std::any> GlobalScope;
  std::stack<std::unordered_map<std::string, std::any>> StackScopes;

 public:
  void CreateFrame();
  void DestroyFrame();
  std::any ReadVariable(const std::string &name);
  void WriteVariable(const std::string &name, const std::any &value,
                     bool cover = false);
};
class FucntionContainer {
  std::unordered_map<std::string, FunctionItem> FunctionIndex;

 public:
  void AddFunction(const std::string &name, const FunctionItem &item);
  std::any CallFunction(const std::string &name,
                        const std::vector<ParaArguItemType> &args,
                        VariableContainer &Varables);
};
std::any DeQuate(std::any val, VariableContainer &Variables);

int ConverToSameArithType(std::any &a, std::any &b,
                          bool allow_string_operation = false);
std::any Add(std::any a, std::any b);
std::any Sub(std::any a, std::any b);
std::any Mul(std::any a, std::any b);
std::any Div(std::any a, std::any b);
std::any Divv(std::any a, std::any b);
std::any Mod(std::any a, std::any b);
std::any &SelfAdd(std::any &a, std::any b);
std::any &SelfSub(std::any &a, std::any b);
std::any &SelfMul(std::any &a, std::any b);
std::any &SelfDiv(std::any &a, std::any b);
std::any &SelfDivv(std::any &a, std::any b);
std::any &SelfMod(std::any &a, std::any b);
std::any Neg(std::any a);

bool Greater(std::any a, std::any b);
bool Less(std::any a, std::any b);
bool Equal(std::any a, std::any b);
bool NotEqual(std::any a, std::any b);
bool GreaterEqual(std::any a, std::any b);
bool LessEqual(std::any a, std::any b);
#endif