#ifndef LEXER_H
#define LEXER_H

#include "error.h"
#include <cctype>
#include <map>
#include <string>
#include <string_view>
#include <vector>

enum tipo_token {
   INT = 0,
   CHAR,
   VOID,
   MAS,
   MENOS,
   POR,
   ENTRE,
   MODULO,
   ASIGNACION,
   MAS_ASIGNACION,
   MENOS_ASIGNACION,
   POR_ASIGNACION,
   ENTRE_ASIGNACION,
   MODULO_ASIGNACION,
   MENOR,
   MENOR_IGUAL,
   MAYOR,
   MAYOR_IGUAL,
   IGUAL,
   DIFERENTE,
   AND,
   OR,
   NOT,
   TAMANYO,
   DIRECCION,
   COMILLA,
   PARENTESIS_IZQ,
   PARENTESIS_DER,
   CORCHETE_IZQ,
   CORCHETE_DER,
   LLAVE_IZQ,
   LLAVE_DER,
   COMA,
   PUNTO_COMA,
   IF,
   ELSE,
   WHILE,
   DO,
   FOR,
   BREAK,
   CONTINUE,
   RETURN,
   PRINT,
   SCAN,
   EXIT,
   IDENTIFICADOR,
   LITERAL_ENTERA,
   LITERAL_CADENA,
   FIN_ARCHIVO
};

std::map<std::string_view, tipo_token> palabras_reservadas = {
   { "int", INT },
   { "char", CHAR },
   { "void", VOID },
   { "if", IF },
   { "else", ELSE },
   { "while", WHILE },
   { "do", DO },
   { "for", FOR },
   { "break", BREAK },
   { "continue", CONTINUE },
   { "return", RETURN },
   { "print", PRINT },
   { "scan", SCAN },
   { "exit", EXIT }
};

std::map<std::string_view, tipo_token> simbolos = {
   { "+", MAS },
   { "-", MENOS },
   { "*", POR },
   { "/", ENTRE },
   { "%", MODULO },
   { "=", ASIGNACION },
   { "+=", MAS_ASIGNACION },
   { "-=", MENOS_ASIGNACION },
   { "*=", POR_ASIGNACION },
   { "/=", ENTRE_ASIGNACION },
   { "%=", MODULO_ASIGNACION },
   { "<", MENOR },
   { "<=", MENOR_IGUAL },
   { ">", MAYOR },
   { ">=", MAYOR_IGUAL },
   { "==", IGUAL },
   { "!=", DIFERENTE },
   { "&", AND },
   { "|", OR },
   { "!", NOT },
   { "#", TAMANYO },
   { "@", DIRECCION },
   { "'", COMILLA },
   { "(", PARENTESIS_IZQ },
   { ")", PARENTESIS_DER },
   { "[", CORCHETE_IZQ },
   { "]", CORCHETE_DER },
   { "{", LLAVE_IZQ },
   { "}", LLAVE_DER },
   { ",", COMA },
   { ";", PUNTO_COMA }
};

struct token {
   tipo_token tipo;
   std::string_view vista;

   token(tipo_token t, const char* ini, const char* fin)
   : tipo(t), vista(ini, fin - ini) {
   }
};

void esquiva_espacios(const char*& p) {
   while (std::isspace(*p)) {
      ++p;
   }
}

bool es_comentario_linea(const char*& p) {
   if (*p == '/' && *(p + 1) == '/') {
      p += 2;
      while (*p != '\n' && *p != '\0') {
         ++p;
      }
      return true;
   }

   return false;
}

bool es_comentario_bloque(const char*& p) {
   // tarea 4
}

bool es_literal_entera(const char*& p) {
   if (std::isdigit(*p)) {
      do {
         ++p;
      } while (std::isdigit(*p));
      return true;
   }

   return false;
}

bool es_literal_cadena(const char*& p) {
   // tarea 4
}

bool es_palabra_o_id(const char*& p) {
   if (std::isalpha(*p) || *p == '_') {
      do {
         ++p;
      } while (std::isalnum(*p) || *p == '_');
      return true;
   }

   return false;
}

bool es_simbolo(const char*& p) {
   for (int k : { 2, 1 }) {
      if (simbolos.contains(std::string_view(p, k))) {
         p += k;
         return true;
      }
   }
   return false;
}

std::vector<token> lexer(const std::string& entrada) {
   std::vector<token> res;
   const char* p = &entrada[0];
   for (;;) {
      esquiva_espacios(p);

      const char* t = p;
      if (es_comentario_linea(p) ||
          es_comentario_bloque(p)) {
         continue;
      } else if (*p == '\0') {
         res.push_back(token(FIN_ARCHIVO, t, p));
         return res;
      } else if (es_literal_entera(p)) {
         res.push_back(token(LITERAL_ENTERA, t, p));
      } else if (es_literal_cadena(p)) {
         res.push_back(token(LITERAL_CADENA, t, p));
      } else if (es_palabra_o_id(p)) {
         std::string_view buscar(t, p - t);
         if (palabras_reservadas.contains(buscar)) {
            res.push_back(token(palabras_reservadas[buscar], t, p));
         } else {
            res.push_back(token(IDENTIFICADOR, t, p));
         }
      } else if (es_simbolo(p)) {
         std::string_view buscar(t, p - t);
         res.push_back(token(simbolos[buscar], t, p));
      } else {
         throw error("Token desconocido", std::string_view(p, 1));
      }
   }
}

#endif
