#ifndef PARSER_SENTENCIA_H
#define PARSER_SENTENCIA_H

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

struct sentencia {
   std::string_view vista;

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

   virtual ~sentencia( ) = default;
};

struct sentencia_expresion : sentencia {
   expresion* ex;

   sentencia_expresion(const control_vista& cv, expresion* e)
   : sentencia(cv), ex(e) {
   }
};

struct sentencia_declaracion : sentencia {
   expresion* tipo;
   token nombre;
   expresion* inicializador;

   sentencia_declaracion(const control_vista& cv, expresion* t, token n, expresion* i)
   : sentencia(cv), tipo(t), nombre(n), inicializador(i) {
   }
};

struct sentencia_if : sentencia {
   expresion* condicion;
   std::vector<sentencia*> cuerpo_si;
   std::vector<sentencia*> cuerpo_no;

   sentencia_if(const control_vista& cv, expresion* ex, const std::vector<sentencia*>& si, std::vector<sentencia*>& no)
   : sentencia(cv), condicion(ex), cuerpo_si(si), cuerpo_no(no) {
   }
};

struct sentencia_while : sentencia {
   expresion* condicion;
   std::vector<sentencia*> cuerpo;

   sentencia_while(const control_vista& cv, expresion* ex, const std::vector<sentencia*>& vec)
   : sentencia(cv), condicion(ex), cuerpo(vec) {
   }
};

struct sentencia_do : sentencia {
   expresion* condicion;
   std::vector<sentencia*> cuerpo;

   sentencia_do(const control_vista& cv, expresion* ex, const std::vector<sentencia*>& vec)
   : sentencia(cv), condicion(ex), cuerpo(vec) {
   }
};

struct sentencia_for : sentencia {
   sentencia_declaracion* inicializacion;
   expresion* condicion;
   expresion* actualizacion;
   std::vector<sentencia*> cuerpo;

   sentencia_for(const control_vista& cv, sentencia_declaracion* i, expresion* c, expresion* a, const std::vector<sentencia*>& vec)
   : sentencia(cv), inicializacion(i), condicion(c), actualizacion(a), cuerpo(vec) {
   }
};

struct sentencia_break : sentencia {
   sentencia_break(const control_vista& cv)
   : sentencia(cv) {
   }
};

struct sentencia_continue : sentencia {
   sentencia_continue(const control_vista& cv)
   : sentencia(cv) {
   }
};

struct sentencia_return : sentencia {
   expresion* ex;

   sentencia_return(const control_vista& cv, expresion* e)
   : sentencia(cv), ex(e) {
   }
};

struct sentencia_exit : sentencia {
   sentencia_exit(const control_vista& cv)
   : sentencia(cv) {
   }
};

sentencia* parser_stmt(const token*&);

std::vector<sentencia*> parser_bloque(const token*& p) {
   std::vector<sentencia*> cuerpo;
   espera(p, LLAVE_IZQ);
   while (p->tipo != LLAVE_DER) {
      cuerpo.push_back(parser_stmt(p));
   }
   ++p;
   return cuerpo;
}

sentencia* parser_stmt(const token*& p) {
   control_vista cv(p);
   if (p->tipo == IF) {
      ++p;
      expresion* ex = parser_expr(p);
      std::vector<sentencia*> cuerpo_si = parser_bloque(p);
      std::vector<sentencia*> cuerpo_no;
      if (p->tipo == ELSE) {
         ++p;
         if (p->tipo == IF) {
            cuerpo_no.push_back(parser_stmt(p));
         } else {
            cuerpo_no = parser_bloque(p);
         }
      }
      return new sentencia_if(cv, ex, cuerpo_si, cuerpo_no);
   } else if (p->tipo == WHILE) {
      ++p;
      expresion* ex = parser_expr(p);
      std::vector<sentencia*> cuerpo = parser_bloque(p);
      return new sentencia_while(cv, ex, cuerpo);
   } else if (p->tipo == DO) {
      // tarea 5
   } else if (p->tipo == FOR) {
      ++p;
      sentencia_declaracion* decl = nullptr;
      if (es_inicio_expr(p)) {
         control_vista cv(p);
         expresion* tipo = parser_expr(p);
         token nombre = espera(p, IDENTIFICADOR);
         expresion* ex = (p->tipo == ASIGNACION ? parser_expr(++p) : nullptr);
         decl = new sentencia_declaracion(cv, tipo, nombre, ex);
      }
      espera(p, PUNTO_COMA);
      expresion* cond = (es_inicio_expr(p) ? parser_expr(p) : nullptr);
      espera(p, PUNTO_COMA);
      expresion* act = (es_inicio_expr(p) ? parser_expr(p) : nullptr);
      std::vector<sentencia*> cuerpo = parser_bloque(p);
      return new sentencia_for(cv, decl, cond, act, cuerpo);
   } else if (p->tipo == BREAK) {
      ++p;
      espera(p, PUNTO_COMA);
      return new sentencia_break(cv);
   } else if (p->tipo == CONTINUE) {
      ++p;
      espera(p, PUNTO_COMA);
      return new sentencia_continue(cv);
   } else if (p->tipo == RETURN) {
      ++p;
      expresion* ex = (es_inicio_expr(p) ? parser_expr(p) : nullptr);
      espera(p, PUNTO_COMA);
      return new sentencia_return(cv, ex);
   } else if (p->tipo == EXIT) {
      ++p;
      espera(p, PUNTO_COMA);
      return new sentencia_exit(cv);
   } else {
      expresion* ex = parser_expr(p);
      if (p->tipo == IDENTIFICADOR) {
         token nombre = *p++;
         expresion* init = (p->tipo == ASIGNACION ? parser_expr(++p) : nullptr);
         espera(p, PUNTO_COMA);
         return new sentencia_declaracion(cv, ex, nombre, init);
      } else {
         espera(p, PUNTO_COMA);
         return new sentencia_expresion(cv, ex);
      }
   }
}

#endif
