/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sdsimulator.runnable.expresiones;
import java.util.Vector;
import sdsimulator.runnable.*;

/**
 *
 * @author Miguel Herreros
 */
public class Operadores{
    private Vector operadores         = new Vector();
    private Vector prioridadesEntrada = new Vector();
    private Vector prioridadesPila    = new Vector();
    private Vector numeroOperandos    = new Vector(); //número de operandos que necesita este operador.
    
    public Operadores(){
        
        operador("$",-2,-1,0);
        operador("(",-1,10,0);
        operador(")",0,0,0);
        operador("¬",5,6,1);//Las prioridades de entrada y de pila son distintas,
        //porque el operando tiene asociatividad por la derecha. 
        operador("}",4,4,2); operador("{",4,4,2); operador(">",4,4,2); operador("<",4,4,2); //Obsérvese que se ha 
        //sustituido >= por } y <= por {
        operador("==",3,3,2); operador("!=",3,3,2);
        operador("&&",2,2,2);
        operador("||",1,1,2);    
    }
    
    //..........................................................................
    private void operador(String operador, int prioridadPila, int prioridadEntrada, int numOps){
        operadores.add(operador);
        prioridadesEntrada.add(prioridadEntrada);
        prioridadesPila.add(prioridadPila);
        numeroOperandos.add(numOps);
    }
    //..........................................................................
    /** Devuelve verdadero si el String que recibe como parámetro es un operador.
     * @param token
     * @return
     */
    public boolean esOperador(String token){
        return operadores.contains(token);
    }
    //..........................................................................
    /** Devuelve el operador almacenado en la posición i del vector de operadores
     *  Si no lo encuentra, devuelve la cadena vacía.
     * @param i
     * @return
     */
    public String getOperador(int i) throws Exception{
        if (i >= operadores.size() || i<0){
            Exception e = new Exception("The operand number " + i + "does not exist.");
            throw e;
        }
        
        return (String)(operadores.get(i));
    }
    
    //..........................................................................
    /**
     * Si el token es un operador, devuelve la prioridad del operador cuando es 
     * leído de la operación infija inicial.
     * @param token
     * @return
     */
    public int getPrioridadEntrada(String token)throws Exception{
        int posicion=operadores.indexOf(token);
        if (posicion==-1){
            Exception e = new Exception("The token is not an operand.");
            throw e;
        }
        else
            return (Integer)(prioridadesEntrada.get(posicion));
    }
    //..........................................................................
    /**
     * Si el token es un operador, devuelve la prioridad del operador cuando es 
     * apilado durante el proceso de conversión a notación polaca.
     * @param token
     * @return
     */
    public int getPrioridadPila(String token) throws Exception{
        int posicion=operadores.indexOf(token);
        if (posicion==-1){
            Exception e = new Exception("The token is not an operand.");
            throw e;
        }
        else
            return (Integer)(prioridadesPila.get(posicion));
    }
    //..........................................................................
    /** Devuelve el número de operadores disponibles.
     * @return
     */
    public int size(){
        return operadores.size();
    }
    //..........................................................................
    /** Devuelve el número de operandos que necesita el operador pasado como 
     * parámetro.
     * @param token
     * @return
     * @throws java.lang.Exception
     */
    public int getNumeroOperandos(String token) throws Exception{
        int posicion=operadores.indexOf(token);
        if (posicion==-1){
            Exception e = new Exception("The token is not an operand.");
            throw e;
        }
        else
            return (Integer)(numeroOperandos.get(posicion));
    }
    //..........................................................................
    /**Aplica el operando pasado como parámetro a el/los operandos que también 
     * entran como parámetro. Si el operando requiere un sólo operador, el segun-
     * do operando será null.
     * @param operador
     * @param operando1
     * @param operando2
     * @return
     * @throws java.lang.Exception
     */
    public Variable aplicar(String operador, Variable operando1, Variable operando2) throws Exception{
        // Para seguir el resultado de la expresión ///////
        System.out.println("Aplicando " + operador + " a " + 
                operando1.getNombre() + " /" + operando1.getValor() + "\\ y a " );
                
        if (operando2!=null) System.out.println(operando2.getNombre() + " /" + operando2.getValor() + "\\");
        //////////////////////////////////////////////////
        
        if (operador.equalsIgnoreCase("¬"))
            return aplicarNOT(operando1);
        
        else if (   operador.equalsIgnoreCase(">") || operador.equalsIgnoreCase("<") ||
                    operador.equalsIgnoreCase("}") || operador.equalsIgnoreCase("{")    )
            return aplicarRELACIONAL(operador,operando1,operando2);
        
        else if (operador.equalsIgnoreCase("==") || operador.equalsIgnoreCase("!="))  
            return aplicarIGUALDAD(operador,operando1,operando2);
        
        else if (operador.equalsIgnoreCase("||") || operador.equalsIgnoreCase("&&"))
            return aplicarBOOLE(operador,operando1,operando2);
        
        else 
            throw (new Exception("Operand not aplicable."));
    }
    //..........................................................................
     public Variable aplicarNOT(Variable operando1) throws Exception{
         Variable retorno = new Variable("TEMP","Boolean","false");
         
         if (operando1.getValor().equalsIgnoreCase("true"))
             retorno.setValor("false");
         else if (operando1.getValor().equalsIgnoreCase("false"))
             retorno.setValor("true");
         else
             throw (new Exception("Non boolean operand"));
         
         return retorno;
     }
   
     //.........................................................................
     public Variable aplicarRELACIONAL(String operador, Variable operando1, Variable operando2) throws Exception{
        Variable retorno = new Variable("TEMP","Boolean","false");
        int op1=0,op2=0;
        
        if (operando1.getValor().equalsIgnoreCase("true"))
            op1=1; //true tendrá un valor 1 para relacionales
        else if (operando1.getValor().equalsIgnoreCase("false"))
            op2=0; //falste tendrá un valor 0 para relacionales
        else{
            op1=Integer.parseInt(operando1.getValor()); //Si no contiene un entero, se eleva una excepción
            op2=Integer.parseInt(operando2.getValor()); //Si no contiene un entero, se eleva una excepción
        }
        // FUNDAMENTAL. LOS OPERANDOS SE APILAN EN EL ORDEN EN QUE APARECEN, ES
        //DECIR, A<B SE APILAN PRIMERO A Y LUEGO B. CUANDO EL OPERADOR RELACIONAL
        //DE LA EXPRESIÓN POLACA PIDE DOS PARÁMETROS, SE LE PASAN EN ORDEN INVER-
        //SO EN EL QUE SE APILARON, PRIMERO B Y LUEGO A. EN EL CASO DE OPERACIONES
        //CON PROPIEDAD CONMUTATIVA, ESTO DARÍA LO MISMO, PERO LOS OPERADORES RE-
        //LACIONALES NO CUMPLEN LA PROPIEDAD CONMUTATIVA. DE AHÍ QUE EN LAS EVALUA-
        //CIONES CODIFICADAS DEBAJO, SE HAYAN INVERTIDO, PARA APLICAR EL OPERADOR
        //RELACIONAL EN EL MISMO ORDEN EN QUE APARECÍAN ESCRITAS LAS VARIABLES.
        if (operador.equalsIgnoreCase(">"))
            if (op2>op1)  retorno.setValor("true");
            else          retorno.setValor("false");
        
        else if (operador.equalsIgnoreCase("}"))
            if (op2>=op1) retorno.setValor("true");
            else          retorno.setValor("false");
        
        else if (operador.equalsIgnoreCase("<"))
            if (op2<op1)  retorno.setValor("true");
            else          retorno.setValor("false");
        
        else if (operador.equalsIgnoreCase("{"))
            if (op2<=op1) retorno.setValor("true");
            else          retorno.setValor("false");
        
        return retorno;         
     }
        
     //.........................................................................
     public Variable aplicarIGUALDAD(String operador, Variable operando1, Variable operando2) throws Exception{
        Variable retorno = new Variable("TEMP","Boolean","false");
        boolean resultado;
        
        if (operador.equalsIgnoreCase("=="))
            resultado=operando1.getValor().equalsIgnoreCase(operando2.getValor());
        else
            resultado=!operando1.getValor().equalsIgnoreCase(operando2.getValor());
        
        if (resultado)
            retorno.setValor("true");
        
         return retorno;
     }
        
     //.........................................................................
     public Variable aplicarBOOLE(String operador, Variable operando1, Variable operando2) throws Exception{
         Variable retorno = new Variable("TEMP","Integer","0");
         boolean op1,op2,resultado;
        
         if (operando1.getValor().equalsIgnoreCase("true"))
             op1=true;
         else if (operando1.getValor().equalsIgnoreCase("false"))
             op1=false;
         else
             throw (new Exception("Non boolean operand"));
        
         if (operando2.getValor().equalsIgnoreCase("true"))
             op2=true;
         else if (operando2.getValor().equalsIgnoreCase("false"))
             op2=false;
         else
             throw (new Exception("Non boolean operand"));
        
         if (operador.equalsIgnoreCase("||"))
             resultado=op1||op2;
         else
             resultado=op1&&op2;
        
         if (resultado)
             retorno.setValor("true");
        
         return retorno;         
     }
}
