package timeseriesweka.filters;

import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;

/**
 * 
 * **********************************************
 * NOT IN USE. NEVER FINISHED THIS, FIGURED OUT HOW TO USE THE MATLAB EQUIVALENT INSTEAD
 * **********************************************
 * 
 * 
 * 
 * 
 * Super simple extension of the FFT.java class. FFT returns the (potentially
 * truncated) real/complex FT coefficients, this will return the timeseries
 * smoothed by inversing the transform after truncation.
 *
 * Essentially just adds the inverse step onto the end of
 * FFT.process(Instances...)
 *
 * @author James Large (james.large@uea.ac.uk)
 */
public class FFTSmoothing extends FFT {

    private double truncProp = 1.0;
    
    public double getTruncateProportion() {
        return truncProp;
    }
    
    public void setTruncateProportion(double prop) {
        truncProp = prop;
    }
    
    @Override
    protected Instances determineOutputFormat(Instances inputFormat) throws Exception {
        //Check all attributes are real valued, otherwise throw exception
        for (int i = 0; i < inputFormat.numAttributes(); i++) {
            if (inputFormat.classIndex() != i) {
                if (!inputFormat.attribute(i).isNumeric()) {
                    throw new Exception("Non numeric attribute not allowed in FFT");
                }
            }
        }
        
        return new Instances(inputFormat, 0);
    }

    /**
     *
     * @param instances
     * @return Fourier transforms, each consecutive two terms are the
     * real/imaginary
     * @throws Exception This process only stores half the Fourier terms, since
     * the second half are just a duplicate of the first half with a different
     * sign for the imaginary term If the DFT algorithm is used, it returns
     * exactly m terms (where m is the original series length If FFT is used it
     * returns x/2, where x is either the smallest power of 2 greater than m
     * (padding), or the largest power of 2 less than m (truncating). If the
     * variable pad is true, it ALWAYS pads, if pad==false it will go to the
     * closest power of 2 above or below.
     */
    @Override
    public Instances process(Instances instances) throws Exception {

        Instances output = determineOutputFormat(instances);

        int originalLength = instances.numAttributes();
        if (instances.classIndex() >= 0) {
            originalLength--;
        }
        //Get the length of the full complex series, which might be padded or truncated. 
        int fullLength = findLength(instances);
        //For each data, first extract the relevant data
        //Note the transform will be at least twice as long as the original                
        //Length is the number of COMPLEX terms, which is HALF the length of the original series. 

        for (int i = 0; i < instances.numInstances(); i++) {

            //1. Get original series stored in a complex array. This may be padded or truncated
            //depending on the original length. If DFT is being used, it is neither. 
            Complex[] c = new Complex[fullLength];
            int count = 0;
            double seriesTotal = 0;
            for (int j = 0; j < originalLength && count < c.length; j++) { //May cut off the trailing values
                if (instances.classIndex() != j) {
                    c[count] = new Complex(instances.instance(i).value(j), 0.0);
                    seriesTotal += instances.instance(i).value(j);
                    count++;
                }
            }
            //Add any Padding required  
            double mean = seriesTotal / count;
            while (count < c.length) {
                c[count++] = new Complex(mean, 0);
            }
            //2. Find FFT/DFT of series.	
            if (algo == AlgorithmType.FFT) {
                fft(c, c.length);
            } else {
                c = dft(c);
            }
            //Extract out the terms and set the attributes.

//NEWWWWWWWWWWWW
//            if (truncProp < 1.0)
//                inverseFFT(c, (int) (c.length * truncProp));

            int numCoeffsToKeep = (int)(truncProp*c.length);
            double[] smoothedSignal = new double[c.length+1];
            for (int a = 0; a < originalLength; ++a) {
                smoothedSignal[a] = .0;
                for (int j = 0; j < numCoeffsToKeep; j++) {
                    smoothedSignal[a] += c[j].real*Math.sin(a);
                    smoothedSignal[a] += c[j].imag*Math.cos(a);
                }
            }
            smoothedSignal[c.length] = instances.instance(i).classValue();
            Instance inst = new DenseInstance(1.0, smoothedSignal);

            
//            Instance inst = new DenseInstance(c.length + 1);
//            for (int j = 0; j < c.length / 2; j++) {
//                inst.setValue(2 * j, c[j].real);
//                inst.setValue(2 * j + 1, c[j].imag);
//            }
//            //Set class value.
//            if (instances.classIndex() >= 0) {
//                inst.setValue(output.classIndex(), instances.instance(i).classValue());
//            }

//END OF NEW
            
            output.add(inst);
        }
        return output;
    }

}
