#ifndef PARSER_EXPRESION_H
#define PARSER_EXPRESION_H

#include "lexer.h"
#include "parser_aux.h"
#include <vector>

struct expresion {
   std::string_view vista;

   expresion(const control_vista& cv)
   : vista(cv) {
   }

   virtual ~expresion( ) = default;
};

struct expresion_termino : expresion {
   token t;

   expresion_termino(const control_vista& cv, token tp)
   : expresion(cv), t(tp) {
   }
};

struct expresion_prefija : expresion {
   token operador;
   expresion* ex;

   expresion_prefija(const control_vista& cv, token t, expresion* e)
   : expresion(cv), operador(t), ex(e) {
   }
};

struct expresion_binaria : expresion {
   expresion* izq;
   token operador;
   expresion* der;

   expresion_binaria(const control_vista& cv, expresion* i, token t, expresion* d)
   : expresion(cv), izq(i), operador(t), der(d) {
   }
};

struct expresion_posfija : expresion {
   expresion* ex;
   token operador;

   expresion_posfija(const control_vista& cv, expresion* e, token t)
   : expresion(cv), ex(e), operador(t) {
   }
};

struct expresion_indexacion : expresion {
   expresion* izq;
   expresion* dentro;

   expresion_indexacion(const control_vista& cv, expresion* i, expresion* d)
   : expresion(cv), izq(i), dentro(d) {
   }
};

struct expresion_llamada : expresion {
   expresion* izq;
   std::vector<expresion*> dentro;

   expresion_llamada(const control_vista& cv, expresion* i, const std::vector<expresion*>& d)
   : expresion(cv), izq(i), dentro(d) {
   }
};

struct expresion_secuencia : expresion {
   std::vector<expresion*> ex;

   expresion_secuencia(const control_vista& cv, const std::vector<expresion*>& e)
   : expresion(cv), ex(e) {
   }
};

expresion* parser_expr(const token*&);

std::vector<expresion*> parser_lista_expr(const token*& p) {
   // tarea 5
}

expresion* parser_expr_primaria(const token*& p) {
   control_vista cv(p);
   if (es_termino(p)) {
      return new expresion_termino(cv, *p++);
   } else if (p->tipo == CORCHETE_IZQ) {
      ++p;
      std::vector<expresion*> ex = parser_lista_expr(p);
      espera(p, CORCHETE_DER);
      return new expresion_secuencia(cv, ex);
   } else {
      espera(p, PARENTESIS_IZQ);
      expresion* ex = parser_expr(p);
      espera(p, PARENTESIS_DER);
      return ex;
   }
}

expresion* parser_expr_unaria(const token*& p) {
   control_vista cv(p);
   if (es_operador_prefijo(p)) {
      token t = *p++;
      return new expresion_prefija(cv, t, parser_expr_unaria(p));
   } else {
      expresion* ex = parser_expr_primaria(p);
      while (es_operador_posfijo(p)) {
         if (p->tipo == CORCHETE_IZQ) {
            ++p;
            expresion* dentro = parser_expr(p);
            espera(p, CORCHETE_DER);
            ex = new expresion_indexacion(cv, ex, dentro);
         } else if (p->tipo == PARENTESIS_IZQ) {
            ++p;
            std::vector<expresion*> lista =  parser_lista_expr(p);
            espera(p, PARENTESIS_DER);
            ex = new expresion_llamada(cv, ex, lista);
         } else {
            ex = new expresion_posfija(cv, ex, *p++);
         }
      }
      return ex;
   }
}

expresion* parser_expr_n_aria(const token*& p, int prec) {
   control_vista cv(p);
   expresion* ex = parser_expr_unaria(p);
   while (es_operador_binario(p) && precedencia(p) >= prec) {
      token t = *p++;
      ex = new expresion_binaria(cv, ex, t, parser_expr_n_aria(p, precedencia(t.tipo) + asociatividad(t.tipo)));
   }
   return ex;
}

expresion* parser_expr(const token*& p) {
   return parser_expr_n_aria(p, 0);
}

#endif
