#pragma once

#include "colors.h"
#include <deque>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

namespace fs = std::filesystem;

enum class TokenType {
  EMPTY,
  SPACE,
  LEFT_PAREN,
  RIGHT_PAREN,
  COLONCOLON,
  EQ,
  COMMA,
  STRING,
  INTRINSIC,
  CONSTANT,
  VARIABLE
};

enum class Intrinsic { EMPTY, LET, UNLET, ENTER, EXIT, SHOW, HIDE };

enum class Constant { EMPTY, CHARACTER, LOCATION, IMAGE };

struct Token {
  TokenType type;
  Intrinsic intrinsic;
  Constant constant;
  std::string string;
  std::string name;
  inline bool operator==(const Token &right) {
    if (this->type == right.type && this->intrinsic == right.intrinsic &&
        this->constant == right.constant && this->string == right.string &&
        this->name == right.name)
      return true;
    return false;
  }
  inline bool operator!=(const Token &right) { return !(*this == right); }
};

struct VariableToken : Token {
  Token value;
  inline bool operator==(const VariableToken &right) {
    if ((Token) * this == (Token)right && this->value == right.value)
      return true;
    return false;
  }
  inline bool operator!=(const VariableToken &right) {
    return !(*this == right);
  }
};

std::map<std::string, Token> CV_Tokens{
    {"", Token{TokenType::EMPTY, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
    {" ", Token{TokenType::SPACE, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
    {"(",
     Token{TokenType::LEFT_PAREN, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
    {")",
     Token{TokenType::RIGHT_PAREN, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
    {"::",
     Token{TokenType::COLONCOLON, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
    {"=", Token{TokenType::EQ, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
    {",", Token{TokenType::COMMA, Intrinsic::EMPTY, Constant::EMPTY, "", ""}},
    {"let",
     Token{TokenType::INTRINSIC, Intrinsic::LET, Constant::EMPTY, "", ""}},
    {"unlet",
     Token{TokenType::INTRINSIC, Intrinsic::UNLET, Constant::EMPTY, "", ""}},
    {"enter",
     Token{TokenType::INTRINSIC, Intrinsic::ENTER, Constant::EMPTY, "", ""}},
    {"exit",
     Token{TokenType::INTRINSIC, Intrinsic::EXIT, Constant::EMPTY, "", ""}},
    {"show",
     Token{TokenType::INTRINSIC, Intrinsic::SHOW, Constant::EMPTY, "", ""}},
    {"hide",
     Token{TokenType::INTRINSIC, Intrinsic::HIDE, Constant::EMPTY, "", ""}},
    {"Character",
     Token{TokenType::CONSTANT, Intrinsic::EMPTY, Constant::CHARACTER, "", ""}},
    {"Location",
     Token{TokenType::CONSTANT, Intrinsic::EMPTY, Constant::LOCATION, "", ""}},
    {"Image",
     Token{TokenType::CONSTANT, Intrinsic::EMPTY, Constant::IMAGE, "", ""}}};

Token empty_token{TokenType::EMPTY, Intrinsic::EMPTY, Constant::EMPTY, "", ""};

struct parsed_token {
  Token lastToken;
  Token currentToken;
};

std::vector<VariableToken> var_tokens;

int read_file(std::deque<std::string> args);
int parse_line(std::string line);