#include <iostream>
#include <vector>
#include <cmath>
#include "calculadora.h"
using namespace std;

Calculadora::Calculadora(Parametros* param, ConjuntoDiscos* set, QPlainTextEdit* cuadroResultado, QObject *parent): QObject(parent)
{
    this->param=param;
    this->set=set;
    this->cuadroResultado=cuadroResultado;
}

void Calculadora::calcular(){
    //std::cout << "calculando" << std::endl;
    Resultado result;
    double tolerancia = param->getTolerancia();
    //cout << tolerancia << std::endl;

    double paso = param->getPaso();
    //cout << paso << std::endl;

    bool isCm = param->getUnidad();
    //cout << isCm << std::endl;

    bool unidadMala = false;

    if(!isCm && paso<15){
        unidadMala = true;
        //cout<<"unidad mala"<<endl;
    }
    else if(isCm && ((paso < 0.5) || (paso > 10))){
        unidadMala = true;
        //cout<<"unidad mala"<<endl;
    }

    int size = static_cast<int>(set->getSize());
    //cout << size << std::endl;

    vector<int> A;//(size);
    A = set->getConjunto(); // todo el conjunto?

    /*
    cout << "Old vector elements are : ";
    for (int i=0; i<set->getConjunto().size(); i++)
        cout <<set->getConjunto()[i] << " ";
    cout << endl;

    cout << "New vector elements are : ";
    for (int i=0; i< A.size(); i++)
        cout <<  A[i] << " ";
    cout<< endl;
*/
    if(!unidadMala){
        double m;
        if(isCm){
            m = paso;
        }else{
            m = 25.4/paso;
        }
        // crear vector de resultados
        vector<Resultado> resultados;
        vector<double> errors;

        for (int i = 0; i < size; ++i) {
            for (int j = 0; j < size; ++j) {
                for (int k = 0; k < size; ++k) {
                    double d = 60.0 * A[i] / (A[j] * A[k]);
                    double error = abs(d-m);
                    if (error <= tolerancia && A[i] != A[j] && A[j] != A[k] && A[i] != A[k]) {
                        //std::vector<int> D = {i + 1, j + 1, k + 1};
                        std::vector<int> D = {A[i], A[j], A[k]};
                        //crear resultado
                        Resultado res;
                        //meterle error y discos
                        res.setDiscos(D);
                        res.setError(error);
                        errors.push_back(error);
                        //meter resultado en vector
                        resultados.push_back(res);
                        /*
                        std::cout << "D: [" << D[0] << ", " << D[1] << ", " << D[2] << "]" << std::endl;
                        std::cout << "A(i): " << A[i] << std::endl;
                        std::cout << "A(j): " << A[j] << std::endl;
                        std::cout << "A(k): " << A[k] << std::endl;
                        */
                        //break;
                    }
                }
            }
        }

        //eliminar resultados repetidos
        resultados=eliminarRepetidos(resultados);

        // comparar por error y ordenar
        //cout << "Vector sin repeticion"<<endl;
        for(int i = 0; i< resultados.size() ; i++){
            //cout << resultados[i].getDiscos()[0] << std::endl;
            //cout << resultados[i].getDiscos()[1] << std::endl;
            //cout << resultados[i].getDiscos()[2] << std::endl;
            //cout << "error: " << resultados[i].getError() << std::endl;
            //cout << " " << std::endl;
        }
        //cout << "tamaño: " << resultados.size() << endl;
        ordenarVector(resultados);
        //cout << "Vectores ordenados"<<endl;
        for(int i = 0; i< resultados.size() ; i++){
            //cout << resultados[i].getDiscos()[0] << std::endl;
            //cout << resultados[i].getDiscos()[1] << std::endl;
            //cout << resultados[i].getDiscos()[2] << std::endl;
            //cout << "error: " << resultados[i].getError() << std::endl;
            //cout << " " << std::endl;
        }

        // imprimir los 3 mejores
        int tamaño_resultado=3;
        if (resultados.size()<3){
            tamaño_resultado=resultados.size();
        }
        for(int i=0; i<tamaño_resultado;i++){

            QString str_Numres;
            QTextStream(&str_Numres) << "Opción " << i+1<<":\n";
            cuadroResultado->insertPlainText(str_Numres);

            QString str_res;
            QTextStream(&str_res) << "["<< resultados[i].getDiscos()[0] << " "<< resultados[i].getDiscos()[1] << " "<< resultados[i].getDiscos()[2] << "]"<<"\n";
            cuadroResultado->insertPlainText(str_res);

            QString str_err;
            QTextStream(&str_err) << "Error: " <<resultados[i].getError();
            cuadroResultado->insertPlainText(str_err);

            cuadroResultado->insertPlainText("\n\n");
        }
    }
    else{
        cuadroResultado->insertPlainText("Se han ingrsado mal los parámetros.");
    }
}

void Calculadora::ordenarVector(vector<Resultado> &_res){
    //cout << "ODENADO?" << endl;
    int j;
    Resultado aux;
    int i = 0;
    int size = _res.size();
    bool ord = false;
    //cout << "size: "<<size << endl;
    // Ordenaciones
    while(!ord){
        // Comparaciones
        ord = true;
        for(j=0 ; j < size - i-1 ; j++){
            //if(v[j]>v[j+1]){
            //cout << "i: "<<i << endl;
            //cout << "j: "<<j << endl;
            //cout << " "<< endl;
            if(_res[j].getError() > _res[j+1].getError()){
                // Intercambiar los elementos
                //aux=v[j];
                aux = _res[j];

                //v[j]=v[j+1];
                _res[j] = _res[j+1];

                //v[j+1]=aux;
                _res[j+1] = aux;
                ord = false;    // Indicador de vector ordenado
            }
        }
        i++;
        //cout << "i: "<<i << endl;
    }
}

//podria retornar el vator<Resultado>
vector<Resultado> Calculadora::eliminarRepetidos(vector<Resultado> &resultados){
    //cout << "LIMPIO?"<<endl;
    Resultado triadaBuenaRes;
    vector<int> triadaBuena;
    vector<Resultado> resBuenos;
    triadaBuena.push_back(resultados[0].getDiscos()[0]);
    triadaBuena.push_back(resultados[0].getDiscos()[1]);
    triadaBuena.push_back(resultados[0].getDiscos()[2]);
    triadaBuenaRes.setDiscos(triadaBuena);
    triadaBuenaRes.setError(resultados[0].getError());
    resBuenos.push_back(triadaBuenaRes);

    for(int i = 1; i< resultados.size() ; i++){
        int n_discosIguales=3;
        //cout << "Triada "<< triadaBuena[0]<< " "<< triadaBuena[1]<< " "<< triadaBuena[2]<<endl;
        for(int j = 0;j<3;j++){
            //cout <<"¿esta el disco? "<<resultados[i].getDiscos()[j]<<endl;
            if(find(triadaBuena.begin(), triadaBuena.end(), resultados[i].getDiscos()[j]) == triadaBuena.end()){
                //cout <<"no esta en el vector" <<endl;
                n_discosIguales--;
            }
        }
        //cout <<"discos iguales: " <<n_discosIguales <<endl;
        if(n_discosIguales!=3){
            //cout <<"Se agrega a los resultados" <<endl;
            triadaBuena[0]=resultados[i].getDiscos()[0];
            triadaBuena[1]=resultados[i].getDiscos()[1];
            triadaBuena[2]=resultados[i].getDiscos()[2];
            triadaBuenaRes.setDiscos(triadaBuena);
            triadaBuenaRes.setError(resultados[i].getError());
            resBuenos.push_back(triadaBuenaRes);
        }

    }


    return resBuenos;
}

/*void Calculadora::setResultado(Resultado *_result){
    resultado = _result;
}*/
