///////////////////////////////////////////////////////////////////////
//
// LECTURA DE EXPRESIONES BOOLEANAS
//
// Asume que las querys estan bien escritas (son sintacticamente correctas)
// para simplificar el codigo del "parser"
// - toda consulta esta delimitada por las simbolos BEGIN_QUERY END_QUERY
// - los operadores son: and, or, not
// - pueden usarse parentesis para modificar la prioridad (NOT > AND > OR)
//   pero deben estar separados por blancos del resto de 
//   simbolos en la entrada
// - las expresiones basicas son TAG <etiqueta> y DATE dd mm aaaa dd mm aaaa
// - las etiquetas son una secuencia de caracteres no espaciadores consecutivos
//   (letras, digitos, subrayados,...); se distinguen mayusculas de minusculas

// Ejemplos:

// Fotos de la Casa Blanca
// BEGIN_QUERY TAG casa_blanca END_QUERY

// Fotos del ao pasado
// BEGIN_QUERY DATE 1 1 2018 31 12 2018 END_QUERY

// Gatos del siglo XX
// BEGIN_QUERY DATE 1 1 1901 31 12 2000 and TAG gato END_QUERY

// Fotos sin perros o con gatos antes de este ao
// BEGIN_QUERY DATE 1 1 1700 31 12 2018 and TAG gato or not TAG perro END_QUERY 

// Fotos anteriores a este ao o fotos (de este ao) en las que no salen perros sin gatos 
// BEGIN_QUERY DATE 1 1 1700 31 12 2018 and ( TAG gato or not TAG perro ) END_QUERY

// IMPORTANTE:
// todo el codigo en este fichero debe ponerse
// directamente al final del fichero ConsultaBool.cc donde implementeis la clase para las consultas 
// booleanas

/** TIPOS Y CABECERAS DE FUNCIONES AUXILIARES; NO MODIFICAR **/

struct token {
    string tipo; // tipo de la consulta: TAG, DATE, AND, OR o NOT
    string etq; // si tipo == "TAG", esta es la etiqueta
    Fecha fini, ffin; // si tipo == "DATE", estas son las fechas de inicio y fin
                      // se asume que existe una clase Fecha y que hay una operacion para
                      // leer fechas
};

string toupper(const string s);
void read_input(istream& is, vector<token>& l);
int precedence(string oper);
void convert_infix_to_postfix(const vector<token>& infix, vector<token>& postfix);
ConsultaBool construct_bool_query(const vector<token>& postfix);

/** FUNCIONES QUE DEBEIS ADAPTAR A VUESTRA CLASE PARA LAS CONSULTAS BOOLEANAS **/

// construye la consulta ("arbol de expresion") a partir de una lista de tokens en
// notacion postfija, es decir convierte una secuencia como esta
//
// <DATE,2/2/2018,4/2/2018> <TAG,gato> <and> <TAG,perro> <not> <or>
//
// en el arbol de expresion correspondiente
//
//                                   or
//                                  /  \
//                                 /    \
//                                /      \
//                               /        \
//                              and      not
//                             /  \        |
//                     DATE(...) TAG(gato) TAG(perro)
//
// Vuestra clase para consultas booleanas ha de ofrecer metodos para crear consultas TAG, consultas DATE, 
// crear una consulta AND o OR dadas dos consultas, o una consulta NOT con una consulta dada
// En el codigo que damos mas abajo las consultas TAG y DATE se generan con constructoras
// de la clase (con parametros diferenciados)
// y las otras mediante metodos AND, OR y NOT que operan el parametro implicito 
// con otra consulta (AND, OR) o sin otras consulta (NOT) y devuelven una nueva consulta 
// es decir, se asume que teneis las siguientes operaciones en la clase ConsultaBool:
//
//    ConsultaBool(const string& s) // constructora, crea una consulta TAG con la etiqueta s
//    ConsultaBool(const Fecha& fini, const Fecha& ffin) // constructora, crea una consulta DATE con las 
//                                                       // fechas de inicio y de fin dadas
//    ConsultaBool AND(const ConsultaBool& q2) // devuelve una consulta que es el AND 
//                                             // del parametro (consulta) implicito y de la consulta q2
//    ConsultaBool OR(const ConsultaBool& q2)  // devuelve una consulta que es el OR 
//                                             // del parametro (consulta) implicito y de la consulta q2
//    ConsultaBool NOT()                       // devuelve una consulta que la negacin (NOT)
//                                             // del parametro (consulta) implicito y la consulta q2

// cambiad nombres o la sintaxis de las llamadas para adaptar este codigo vuestra clase ConsultaBool,
ConsultaBool construct_bool_query(const vector<token>& postfix) {
    stack<ConsultaBool> S;
    for (int i = 0; i < postfix.size(); ++i) {
        token tk = postfix[i];
        if (tk.tipo == "TAG") S.push(ConsultaBool(tk.etq)); // apila una consulta TAG
        if (tk.tipo == "DATE") S.push(ConsultaBool(tk.fini, tk.ffin)); // apila una consulta DATE
        if (tk.tipo == "AND" or tk.tipo == "OR") {
            ConsultaBool q2 = S.top(); S.pop();
            ConsultaBool q1 = S.top(); S.pop();
            if (tk.tipo == "AND") S.push(q1.AND(q2)); // apila una consulta 'q1 and q2'
            if (tk.tipo == "OR") S.push(q1.OR(q2)); // apila una consulta 'q1 or q2'
        }
        if (tk.tipo == "NOT") {
            ConsultaBool q = S.top(); S.pop(); 
            S.push(q.NOT()); // apila una consulta 'not q'
        }
    }
    return S.top();
}

// Operacion de lectura de vuestra clase de consultas booleanas
// - lee la expresion desde el canal de entrada 'is'  (mas general que leer desde 'cin')
// - normalmente no deberiais cambiar nada, excepto quizs
// el nombre la clase on la que representais las consultas, es decir, cambiar ConsultaBool por lo
// que corresponda y el nombre del metodo si os gusta mas otro, por ejemplo read ==> llegir
void ConsultaBool::read(istream& is) {
    vector<token> infix, postfix;
    read_input(is, infix);
    if (infix.empty()) { 
        *this = ConsultaBool(); 
        return; 
    }
    convert_infix_to_postfix(infix, postfix);
    *this = construct_bool_query(postfix);
}

// esta funcion asume que lee una secuencia de strings que empieza con BEGIN_QUERY y acaba
// con END_QUERY y sigue el formato descrito en este mismo fichero asi como en el enunciado de
// la practica; en vez de leer desde el 'cin', es mas general y lee desde un canal de entrada 'is' 
// proporcionado como parametro de entrada/salida del procedimiento;
// es posible/probable que tengais que cambiar el codigo (marcado con /** .... **/) que lee fechas de inicio
// y final para una subexpresion booleana correspondiente a una consulta DATE
// pero no os har falta cambiar nada ms
void read_input(istream& is, vector<token>& l) {
    string s; 
    is >> s; 
    while (s != "END_QUERY") {
        token tk;
        s = toupper(s);
        tk.tipo = s;
        if (s == "(" or s == ")" or s == "AND" or s == "OR" or s == "NOT") { 
            l.push_back(tk);
        }
        if (s == "DATE") { /** usad el metodo Fecha::llegir() o similar si no teneis
                            sobrecargado el operador >> para leer fechas **/
            is >> tk.fini >> tk.ffin; 
            l.push_back(tk);
        }
        if (s == "TAG") {
            is >> tk.etq;
            l.push_back(tk);
        }      
        is >> s;
    }
}

// si quereis sobrecargar el operador >> para poder escribir codigo como por ejemplo este
// 
// string id_album, id_album_resultado;
// ConsultaBool q; 
// cin >> id_album >> id_album_resultado >> q;
// ...
// 
// poned la cabecera de este operator>> en vuestro fichero .hh pero fuera de la definicion de class
// y descomentarizad el codigo que implementa la funcion, poniendolo en vuestro fichero .cc
// istream& operator>>(istream& is, ConsultaBool& q) {
//    q.read(is);
//    return is;
// }


/** NO CAMBIAR NADA A PARTIR DE ESTE PUNTO **/
string toupper(const string& s) {
    string res = "";
    for (int i = 0; i < s.length(); ++i)
        res += toupper(s[i]);
    return res;
}

int precedence(string oper) {
    if (oper == "OR") return 1;
    if (oper == "AND") return 2;
    if (oper == "NOT") return 3;
    return 0;
}

void convert_infix_to_postfix(const vector<token>& infix, vector<token>& postfix) {
    stack<token> S;
    token spc; spc.tipo = "#"; S.push(spc);
    for (int i = 0; i < infix.size(); ++i) {
        token tk = infix[i];
        if (tk.tipo == "DATE" or tk.tipo == "TAG")
            postfix.push_back(tk);
        else if (tk.tipo == "(" or tk.tipo == "NOT") 
            S.push(tk);
        else if (tk.tipo == ")") {
            while (S.top().tipo != "#" and S.top().tipo != "(") {
                postfix.push_back(S.top()); S.pop();
            }
            S.pop();
        } else {
            while (S.top().tipo != "#" and 
                precedence(tk.tipo) <= precedence(S.top().tipo)) {
                    postfix.push_back(S.top()); S.pop();
            }
            S.push(tk);
        }
    }
    while (S.top().tipo != "#") {
        postfix.push_back(S.top()); S.pop();
    } 
}
