#include "AdvDeconvolutionByFFT.hh"

#include <iostream>

const std::string AdvDeconvolutionByFFT::className=std::string("AdvDeconvolutionByFFT");

void AdvDeconvolutionByFFT::setDefaultConvolutionType(AdvParamSet& paramSet) {
    paramSet.add(AdvDeconvolutionByFFT::CONVOLUTION_TYPE, AdvDeconvolutionByFFT::CYCLIC);
}

Bool AdvDeconvolutionByFFT::checkConvolutionType(AdvParamSet& paramSet) {
    return paramSet.getInt4(AdvDeconvolutionByFFT::CONVOLUTION_TYPE) == AdvDeconvolutionByFFT::CYCLIC;
}

void AdvDeconvolutionByFFT::addZeroSeqAtBack(std::vector<Double>& val, std::vector<Double>& err, const UInt4 n) {
    val.insert(val.end(), n, 0.0);
    err.insert(err.end(), n, 0.0);
//    val.insert(val.end(), n, val.at(val.size()-1));
//    err.insert(err.end(), n, err.at(err.size()-1));

}

void AdvDeconvolutionByFFT::eraseZeroSeqAtBack(std::vector<Double>& val, std::vector<Double>& err, const UInt4 n) {
    UInt4 ct=0;
    while (val.back() == 0.0 && ct < n) {
        val.erase(--(val.end()));
        err.erase(--(err.end()));
    }
}

void AdvDeconvolutionByFFT::eraseZeroSeqAtBack(std::vector<Double>& val, std::vector<Double>& err) {
    while ( val.size() > 0 && val.back() == 0.0) {
        val.erase(--(val.end()));
        err.erase(--(err.end()));
    }
}

void AdvDeconvolutionByFFT::eval() {
    std::string memberName=std::string("eval()");

    DebugMessage(className, memberName, __FILE__, __LINE__, "enter\n");

    for (std::map<UInt4, std::vector<UInt4> >::iterator entry=this->dataLengthMap.begin(); entry != this->dataLengthMap.end(); ++entry) {

        //DebugMessage(className, memberName, __FILE__, __LINE__, "data   size: %d\n", (*entry).first);
        //DebugMessage(className, memberName, __FILE__, __LINE__, "resY   size: %d\n", this->resY.size());
        //DebugMessage(className, memberName, __FILE__, __LINE__, "resErr size: %d\n", this->resErr.size());

        AdvGslFFTComplex fft = AdvGslFFTComplex((*entry).first);
        Int4 status;

        if (this->resY.size() < (*entry).first) {
            this->addZeroSeqAtBack(this->resY,   this->resErr, (*entry).first - this->resY.size());
        } else if (this->resY.size() > (*entry).first) {
            this->eraseZeroSeqAtBack(this->resY,   this->resErr, this->resY.size() - (*entry).first);
        }

        int shift =resY.size()/2;
        rotate(resY.begin(),resY.begin()+shift,resY.end());
        rotate(resErr.begin(),resErr.begin()+shift,resErr.end());

        gsl_vector_complex *resYArray  =this->createGslVectorComplex(AdvVectorComplexTool::REAL, this->resY);
        //DebugMessage(className, memberName, __FILE__, __LINE__, "allocated resYArrayr (size: %d)\n", resYArray->size);
        gsl_vector_complex *resErrArray=this->createGslVectorComplex(AdvVectorComplexTool::REAL, this->resErr);
        //DebugMessage(className, memberName, __FILE__, __LINE__, "allocated resErrArrayr (size: %d)\n", resErrArray->size);
        gsl_vector_complex *yArray   =gsl_vector_complex_alloc((*entry).first);
        //DebugMessage(className, memberName, __FILE__, __LINE__, "allocated yArrayr (size: %d)\n", yArray->size);
        gsl_vector_complex *errArray =gsl_vector_complex_alloc((*entry).first);
        //DebugMessage(className, memberName, __FILE__, __LINE__, "allocated errArrayr (size: %d)\n", errArray->size);
        gsl_vector_complex *resultYArray  =gsl_vector_complex_alloc((*entry).first);
        //DebugMessage(className, memberName, __FILE__, __LINE__, "allocated resultYArrayr (size: %d)\n", resultYArray->size);
        gsl_vector_complex *resultErrArray=gsl_vector_complex_alloc((*entry).first);
        //DebugMessage(className, memberName, __FILE__, __LINE__, "allocated resultErrArrayr (size: %d)\n", resultErrArray->size);

        status=fft.forward(resYArray);
        //     if (status) {
        //         errorMessage(className, memberName, __FILE__, __LINE__, "status: %d\n%s\n", status, gsl_strerror(status));
        //     }
        status=fft.forward(resErrArray);
        //     if (status) {
        //         errorMessage(className, memberName, __FILE__, __LINE__, "status: %d\n%s\n", status, gsl_strerror(status));
        //     }

        std::cerr << "data size: " << (*entry).first << ", data index:";
        for (std::vector<UInt4>::iterator index = (*entry).second.begin(); index != (*entry).second.end(); ++index) {
            std::cerr << " " << *index << std::endl;

            int shiftS =srcYs.at(*index).size()/2;
            rotate(srcYs.at(*index).begin(),srcYs.at(*index).begin()+shiftS,srcYs.at(*index).end());
            rotate(srcErrs.at(*index).begin(),srcErrs.at(*index).begin()+shiftS,srcErrs.at(*index).end());

            this->initGslVectorComplex(AdvVectorComplexTool::REAL, this->srcYs.at(  *index), yArray  );
            this->initGslVectorComplex(AdvVectorComplexTool::REAL, this->srcErrs.at(*index), errArray);

            status=fft.forward(yArray);
            //if (status) {
            //    errorMessage(className, memberName, __FILE__, __LINE__, "status: %d\n%s\n", status, gsl_strerror(status));
            //}
            status=fft.forward(errArray);
            //if (status) {
            //    errorMessage(className, memberName, __FILE__, __LINE__, "status: %d\n%s\n", status, gsl_strerror(status));
            //}



            for (UInt4 i=0; i<(*entry).first; ++i) {
                gsl_complex rv = gsl_vector_complex_get(resYArray,   i);
                gsl_complex re = gsl_vector_complex_get(resErrArray, i);
                gsl_complex sv = gsl_vector_complex_get(yArray,   i);
                gsl_complex se = gsl_vector_complex_get(errArray, i);




                if(abs(GSL_REAL(rv))<1e-8)
                    {
                    GSL_SET_COMPLEX(&rv, 1e-8, 0);
                    GSL_SET_COMPLEX(&re, 1e-20, 0);
                    }

                if(abs(GSL_REAL(sv))<1e-8)
                    {
                    GSL_SET_COMPLEX(&sv, 1e-12, 0);
                    GSL_SET_COMPLEX(&se, 1e-20, 0);
                    }


/*
                if(GSL_REAL(rv)<0)
                    {
                    GSL_SET_COMPLEX(&rv, -GSL_REAL(rv),0);
                    }else
                    {
                    GSL_SET_COMPLEX(&rv, GSL_REAL(rv),0);
                    }


                if(GSL_REAL(sv)<0)
                    {
                    GSL_SET_COMPLEX(&sv, -GSL_REAL(sv),0);
                    }else
                    {
                    GSL_SET_COMPLEX(&sv, GSL_REAL(sv),0);
                    }
*/

                //gsl_vector_complex_set(resultYArray,   i, gsl_complex_mul(rv, sv));
                //gsl_vector_complex_set(resultErrArray, i, gsl_complex_add(gsl_complex_add(gsl_complex_mul(rv, se), gsl_complex_mul(re, sv)), gsl_complex_mul(re, se)));
                gsl_vector_complex_set(resultYArray, i, gsl_complex_div(sv, rv));

                //gsl_vector_complex_set(resultYArray, i,rv);
                //gsl_vector_complex_set(resultYArray, i, gsl_complex_mul(sv, rv));
                gsl_vector_complex_set(resultErrArray, i, gsl_complex_mul(gsl_complex_div(sv, rv), gsl_complex_sub(gsl_complex_div(se, sv), gsl_complex_div(re, rv))));
                //gsl_complex zv = gsl_vector_complex_get(resultYArray,   i);
                //gsl_complex ze = gsl_vector_complex_get(resultErrArray, i);

                //std::cout <<  " resY\t" << GSL_REAL(rv) << ", \t" << GSL_IMAG(rv);
                //std::cout << ", \tsrcY\t" << GSL_REAL(sv) << ", \t" << GSL_IMAG(sv);
                //std::cout << ", \tresultY\t" << GSL_REAL(zv) << ", \t" << GSL_IMAG(zv)<<std::endl;

                //std::cerr << "i=" << i;
                //std::cerr << "  resY=(" << GSL_REAL(rv) << ", " << GSL_IMAG(rv);
                //std::cerr << ") (" << GSL_REAL(re) << ", " << GSL_IMAG(re);
                //std::cerr << ") srcY=(" << GSL_REAL(sv) << ", " << GSL_IMAG(sv);
                //std::cerr << ") (" << GSL_REAL(se) << ", " << GSL_IMAG(se);
                //std::cerr << ") resultY=(" << GSL_REAL(zv) << ", " << GSL_IMAG(zv);
                //std::cerr << ") (" << GSL_REAL(ze) << ", " << GSL_IMAG(ze);
                //std::cerr << ")";
                //std::cerr << std::endl;

            }

            fft.inverse(resultYArray);
            //if (status) {
            //    errorMessage(className, memberName, __FILE__, __LINE__, "status: %d\n%s\n", status, gsl_strerror(status));
              //}
            fft.inverse(resultErrArray);
            //if (status) {
            //    errorMessage(className, memberName, __FILE__, __LINE__, "status: %d\n%s\n", status, gsl_strerror(status));
            //}
            //for (UInt4 i=0; i<(*entry).first; ++i) {
            //    gsl_complex zv = gsl_vector_complex_get(resultYArray,   i);
            //    gsl_complex ze = gsl_vector_complex_get(resultErrArray, i);
            //    std::cerr << "i=" << i;
            //    std::cerr << " result=(" << GSL_REAL(zv) << ", " << GSL_IMAG(zv);
            //    std::cerr << ") (" << GSL_REAL(ze) << ", " << GSL_IMAG(ze);
            //    std::cerr << ")";
            //    std::cerr << std::endl;
            //}


            this->resultYs.at(*index)  =*(this->getPartAsVector(AdvVectorComplexTool::REAL, resultYArray));
            this->resultErrs.at(*index)=*(this->getPartAsVector(AdvVectorComplexTool::REAL, resultErrArray));

            int shift =resultYs.at(*index).size()/2;
            rotate(resultYs.at(*index).begin(),resultYs.at(*index).begin()+shift,resultYs.at(*index).end());
            rotate(resultErrs.at(*index).begin(),resultErrs.at(*index).begin()+shift,resultErrs.at(*index).end());



            for(UInt4 i = 0; i<resultYs.at(*index).size();i++){
                if(resultYs.at(*index).at(i)<0){
                    resultYs.at(*index).insert(resultYs.at(*index).begin()+i+1,abs(resultYs.at(*index).at(i)));
                    resultYs.at(*index).erase(resultYs.at(*index).begin()+i);

                    }
                }



        }
        std::cerr << std::endl;
        std::cout <<std::endl;
        std::cout << "NumOfY=" << resY.size() <<std::endl;
        std::cout << "NumOfX=" << resBin.size() <<std::endl;

        if (this->resY.size() > (this->resBin.size() - 1) ) {
            this->eraseZeroSeqAtBack(this->resY, this->resErr, this->resY.size() - (this->resBin.size()-1));
        }
    }

    DebugMessage(className, memberName, __FILE__, __LINE__, "exit\n");
}
