#ifndef SEMANTICO_AUX_H
#define SEMANTICO_AUX_H

#include "error.h"
#include "lexer.h"
#include "parser.h"
#include <string>

enum modificador_tipo {
   ESCALAR = 0, APUNTADOR, ARREGLO
};

enum tiempo_vida {
   TEMPORAL = 0, VARIABLE
};

enum contexto_tipo {
   RETORNO, PARAMETRO, LOCAL
};

struct tipo_evaluado {
   tipo_token base;
   modificador_tipo modificador;
   tiempo_vida vida;
   int tamanyo = -1;
};

bool tipo_compatible(tipo_evaluado a, tipo_evaluado b) {
   return a.base == b.base && a.modificador == b.modificador && (a.modificador != ARREGLO || a.tamanyo == b.tamanyo || a.tamanyo == -1 || b.tamanyo == -1);
}

int evalua_literal_entera(token t) {
   try {
      return std::stoi(std::string(t.vista));
   } catch (...) {
      throw error("Literal entera no representable", t.vista);
   }
}

std::string evalua_literal_cadena(token t) {
   std::string s;
   const char* p = t.vista.begin( ) + 1;
   while (*p != '"') {
      if (*p != '\\') {
         s += *p++;
      } else {
         ++p;
         if (*p == 'n') {
            s += '\n';
         } else if (*p == '\\') {
            s += '\\';
         } else if (*p == '"') {
            s += '"';
         }
         ++p;
      }
   }
   return s;
}

tipo_evaluado analiza_como_tipo(expresion* ex, contexto_tipo contexto) {
   tiempo_vida vida = (contexto == RETORNO ? TEMPORAL : VARIABLE);
   if (auto p = dynamic_cast<expresion_termino*>(ex); p != nullptr) {
      if (p->t.tipo == INT || p->t.tipo == CHAR || (contexto == RETORNO && p->t.tipo == VOID)) {
         return tipo_evaluado(p->t.tipo, ESCALAR, vida);
      }
   } else if (auto p = dynamic_cast<expresion_posfija*>(ex); p != nullptr) {
      if (auto q = dynamic_cast<expresion_termino*>(p->ex); q != nullptr) {
         if (p->operador.tipo == COMILLA && (q->t.tipo == INT || q->t.tipo == CHAR)) {
            return tipo_evaluado(q->t.tipo, APUNTADOR, vida);
         }
      }
   } else if (auto p = dynamic_cast<expresion_indexacion*>(ex); p != nullptr) {
      if (auto i = dynamic_cast<expresion_termino*>(p->izq); i != nullptr) {
         if (auto d = dynamic_cast<expresion_termino*>(p->dentro); d != nullptr) {
            if ((i->t.tipo == INT || i->t.tipo == CHAR) && d->t.tipo == LITERAL_ENTERA && contexto == LOCAL) {
               return tipo_evaluado(i->t.tipo, ARREGLO, vida, evalua_literal_entera(d->t));
            }
         }
      }
   }

   throw error("Tipo invalido", ex->vista);
}

tipo_evaluado analiza_semantica(expresion* ex, auto&... params) {
   if (auto p = dynamic_cast<expresion_termino*>(ex); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<expresion_binaria*>(ex); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<expresion_prefija*>(ex); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<expresion_posfija*>(ex); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<expresion_llamada*>(ex); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<expresion_indexacion*>(ex); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<expresion_secuencia*>(ex); p != nullptr) {
      return analiza_semantica(p, params...);
   } else {
      return { };    // no debería ocurrir
   }
}

void analiza_semantica(sentencia* s, auto&... params) {
   if (auto p = dynamic_cast<sentencia_expresion*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_declaracion*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_if*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_while*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_do*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_for*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_return*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_break*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_continue*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   } else if (auto p = dynamic_cast<sentencia_exit*>(s); p != nullptr) {
      return analiza_semantica(p, params...);
   }
}

#endif
