package net.heartshield.filter;

import junit.framework.TestCase;

import net.heartshield.signal.DVec;
import net.heartshield.signal.FirFilters;
import net.heartshield.signal.Signal;

import org.apache.commons.math3.complex.Complex;
import org.jfree.data.xy.XYSeries;

/**
 * @author David Madl (git@abanbytes.eu)
 * @date 2017-03-20
 */
public class PlotFilterTests extends TestCase {

    public void testPlotSine() {
        double[] x = new double[100];
        final double F = 1.0, FPS = 30.0;
        for(int i = 0; i < x.length; i++)
            x[i] = Math.sin(2*Math.PI*F/FPS * i);

        x = DVec.add(x, DVec.linspace(0.0, 3.0, x.length, false));

        LineChart lc = LineChart.plot(x);
        //LineChart lc = LineChart.plot(DVec.linspace(0.0, 3.0, x.length, false));
        lc.showChart();
        lc.waitChart();
    }

    public void testDetrend() {
        double[] t = new double[100];
        double[] x = new double[100];
        final double F = 1.0, FPS = 30.0;
        for(int i = 0; i < x.length; i++) {
            t[i] = (double) i / FPS;
            x[i] = Math.sin(2 * Math.PI * F * t[i]);
        }

        x = DVec.add(x, DVec.linspace(0.0, 3.0, x.length, false));

        final int PLOT_SAMPLES = 100;
        final double TOTAL_TIME = 15.0;

        PlotFilter pf = new PlotFilter(FPS, (int) (TOTAL_TIME * FPS + 1), PLOT_SAMPLES);
        for(int i = 0; i < x.length; i++)
            pf.addUneven(t[i], x[i]);

        // to do: why is getPlotTimes() off by a few samples?! (time starts at 0.1 sec)
        PlotFilter.Series ser = pf.getPlotSeries();
        double[] times = ser.t;
        //double[] frames = pf.getEvenFrames();
        double[] frames = ser.x;
        //LineChart lc = LineChart.plot(frames);
        LineChart lc = LineChart.plot(times, frames);
        lc.showChart();
        lc.waitChart();
    }

    public void testSigGen() {
        double FPS = 30.0;

        ////

        double mStartTime = ((double) System.currentTimeMillis()) / 1000;

        double dt = 1.0 / FPS;
        double duration = 75.0;
        double f = 1.0;
        double end = mStartTime + duration;

        double[] arr = new double[(int) (duration*FPS+1)];
        int i = 0;
        for(double t = mStartTime; t < end; t += dt) {
            double val = Math.sin(2 * Math.PI * f * t);
            arr[i++] = val;
        }

        LineChart lc = LineChart.plot(arr);
        lc.showChart();
        lc.waitChart();
    }

    public void testHilbertImag() {
        double[] arr = new double[200];
        arr[100] = 1;

        HilbertImag hi = new HilbertImag();
        DataSink out = new DataSink();
        hi.connect(out);

        hi.put(arr);
        double[] res = out.getData();

        if(res.length != arr.length)
            throw new IllegalStateException("output.length=" + res.length + " != input.length=" + arr.length);

        for(int j = 0; j < res.length; j++) {
            System.out.println(j + " " + res[j]);
        }

        final XYSeries inSeries = new XYSeries("impulse");
        final XYSeries outSeries = new XYSeries("response");

        for(int j = 0; j < arr.length; j++) {
            inSeries.add(j, arr[j]);
            outSeries.add(j, res[j]);
        }

        LineChart lc = new LineChart("hilbert", new XYSeries[]{ inSeries, outSeries });
        lc.showChart();
        lc.waitChart();
    }

    public void testHilbertImpulse() {
        double[] arr = new double[200];
        arr[100] = 1;

        Hilbert hi = new Hilbert();
        CDataSink out = new CDataSink();
        hi.connect(out);

        hi.put(arr);
        Complex[] res = out.getData();

        if(res.length != arr.length)
            throw new IllegalStateException("output.length=" + res.length + " != input.length=" + arr.length);

        for(int j = 0; j < res.length; j++) {
            System.out.println(j + " " + res[j]);
        }

        final XYSeries realSeries = new XYSeries("real");
        final XYSeries imagSeries = new XYSeries("imag");

        for(int j = 0; j < res.length; j++) {
            realSeries.add(j, res[j].getReal());
            imagSeries.add(j, res[j].getImaginary());
        }

        LineChart lc = new LineChart("hilbert", new XYSeries[]{ realSeries, imagSeries });
        lc.showChart();
        lc.waitChart();
    }

    public void testHilbert() {
        double FPS = 30.0;

        ////

        //double mStartTime = ((double) System.currentTimeMillis()) / 1000;
        double mStartTime = 0.0;

        double dt = 1.0 / FPS;
        double duration = 7.5;
        double f = 1.0;
        double end = mStartTime + duration;

        double[] arr = new double[(int) (duration*FPS+1)];
        int i = 0;
        for(double t = mStartTime; t < end; t += dt) {
            double val = Math.sin(2 * Math.PI * f * t);
            arr[i++] = val;
        }

        Hilbert hilbert = new Hilbert();
        CDataSink out = new CDataSink();
        hilbert.connect(out);

        hilbert.put(arr);
        Complex[] analytic = out.getData();

        if(analytic.length != arr.length)
            throw new IllegalStateException("output.length=" + analytic.length + " != input.length=" + arr.length);

        final XYSeries realSeries = new XYSeries("real");
        final XYSeries imagSeries = new XYSeries("imag");

        for(int j = 0; j < analytic.length; j++) {
            realSeries.add(j, analytic[j].getReal());
            imagSeries.add(j, analytic[j].getImaginary());
        }

        LineChart lc = new LineChart("hilbert", new XYSeries[]{ realSeries, imagSeries });
        lc.showChart();
        lc.waitChart();
    }


    public void testLowpass() {
        double FPS = 30.0;

        ////

        //double mStartTime = ((double) System.currentTimeMillis()) / 1000;
        double mStartTime = 0.0;

        double dt = 1.0 / FPS;
        double duration = 75.0;
        double f = 0.1;
        double end = mStartTime + duration;

        double[] arr = new double[(int) (duration*FPS+1)];
        int i = 0;
        for(double t = mStartTime; t < end; t += dt) {
            double val = Math.sin(2 * Math.PI * f * t);
            arr[i++] = val;
        }

        LowPassFilter lowPass = new LowPassFilter(FPS, 2*f, f*0.5);
        DataSink out = new DataSink();
        lowPass.connect(out);

        lowPass.put(arr);
        double[] filtered = out.getData();

        if(filtered.length != arr.length)
            throw new IllegalStateException("output.length != input.length");

        //LineChart lc = LineChart.plot(arr);
        LineChart lc = LineChart.plot(filtered);
        lc.showChart();
        lc.waitChart();
    }


    public void testHighpass() {
        double FPS = 30.0;

        ////

        //double mStartTime = ((double) System.currentTimeMillis()) / 1000;
        double mStartTime = 0.0;

        double dt = 1.0 / FPS;
        double duration = 75.0;
        double f = 0.1;
        double end = mStartTime + duration;

        double[] arr = new double[(int) (duration*FPS+1)];
        int i = 0;
        for(double t = mStartTime; t < end; t += dt) {
            double val = Math.sin(2 * Math.PI * f * t);
            arr[i++] = val;
        }

        HighPassFilter highPass = new HighPassFilter(FPS, 0.3*f, f*0.5);
        System.out.println("delay=" + highPass.getDelay());
        DataSink out = new DataSink();
        highPass.connect(out);

        highPass.put(arr);
        double[] filtered = out.getData();

        if(filtered.length != arr.length)
            throw new IllegalStateException("output.length != input.length");

        //LineChart lc = LineChart.plot(arr);
        LineChart lc = LineChart.plot(filtered);
        lc.showChart();
        lc.waitChart();
    }

    public void testBandpassResponse() {
        double FPS = 30.0;
        double f = 0.1;
        double[] imp = FirFilters.bandPass(FPS, 0.7*f, 1.3*f, f*0.2);
        LineChart lc = LineChart.plot(imp);
        lc.showChart();
        lc.waitChart();
    }

    public void testBandpass() {
        double FPS = 30.0;

        ////

        //double mStartTime = ((double) System.currentTimeMillis()) / 1000;
        double mStartTime = 0.0;

        double dt = 1.0 / FPS;
        double duration = 75.0;
        double f = 0.1;
        double end = mStartTime + duration;

        double[] arr = new double[(int) (duration*FPS+1)];
        int i = 0;
        for(double t = mStartTime; t < end; t += dt) {
            double val = Math.sin(2 * Math.PI * f * t) + 3 * Math.sin(2 * Math.PI * (f * 0.05) * t) + 2 * Math.sin(2 * Math.PI * (f * 3.0) * t);
            //double val = Math.sin(2 * Math.PI * f * t);
            arr[i++] = val;
        }

        BandPassFilter bandPass = new BandPassFilter(FPS, 0.2*f, 1.8*f, f*0.5);
        DataSink out = new DataSink();
        bandPass.connect(out);

        bandPass.put(arr);
        double[] filtered = out.getData();

        for(int j = 0; j < filtered.length; j++) {
            System.out.println(j + " " + filtered[j]);
        }

        if(filtered.length != arr.length)
            throw new IllegalStateException("output.length != input.length");


        final XYSeries inSeries = new XYSeries("input");
        final XYSeries outSeries = new XYSeries("output");

        for(int j = 0; j < arr.length; j++) {
            inSeries.add(j, arr[j]);
            outSeries.add(j, filtered[j]);
        }

        LineChart lc = new LineChart("bandpass", new XYSeries[]{ inSeries, outSeries });

        //LineChart lc = LineChart.plot(filtered);
        lc.showChart();
        lc.waitChart();
    }


    public void testBandreject() {
        double FPS = 30.0;

        ////

        //double mStartTime = ((double) System.currentTimeMillis()) / 1000;
        double mStartTime = 0.0;

        double dt = 1.0 / FPS;
        double duration = 75.0;
        double f = 0.1;
        double end = mStartTime + duration;

        double[] arr = new double[(int) (duration*FPS+1)];
        int i = 0;
        for(double t = mStartTime; t < end; t += dt) {
            double val = 3 * Math.sin(2 * Math.PI * f * t) + Math.sin(2 * Math.PI * (f * 0.05) * t) + 2 * Math.sin(2 * Math.PI * (f * 2.75) * t);
            //double val = Math.sin(2 * Math.PI * f * t);
            arr[i++] = val;
        }

        BandRejectFilter bandReject = new BandRejectFilter(FPS, 0.2*f, 1.8*f, f*0.5);
        DataSink out = new DataSink();
        bandReject.connect(out);

        bandReject.put(arr);
        double[] filtered = out.getData();

        for(int j = 0; j < filtered.length; j++) {
            System.out.println(j + " " + filtered[j]);
        }

        if(filtered.length != arr.length)
            throw new IllegalStateException("output.length != input.length");


        final XYSeries inSeries = new XYSeries("input");
        final XYSeries outSeries = new XYSeries("output");

        for(int j = 0; j < arr.length; j++) {
            inSeries.add(j, arr[j]);
            outSeries.add(j, filtered[j]);
        }

        LineChart lc = new LineChart("bandreject", new XYSeries[]{ inSeries, outSeries });

        //LineChart lc = LineChart.plot(filtered);
        lc.showChart();
        lc.waitChart();
    }

    public void testImpulse() {
        double[] taps = Signal.triangle(200);
        double[] sig = new double[2000];
        sig[1000] = 1.0;

        FirFilterRunning fir = new FirFilterRunning(taps);
        double[] y = fir.batch(sig);


        final XYSeries inSeries = new XYSeries("input");
        final XYSeries outSeries = new XYSeries("output");

        for(int j = 0; j < sig.length; j++) {
            inSeries.add(j, sig[j]);
            outSeries.add(j, y[j] * 100);
        }

        LineChart lc = new LineChart("running_fir", new XYSeries[]{ inSeries, outSeries });

        //LineChart lc = LineChart.plot(filtered);
        lc.showChart();
        lc.waitChart();
    }

    public void testSine() {
        double[] taps = Signal.triangle(200);
        double[] sig = new double[2000];

        double FPS = 200.0;
        double F = 0.1;
        for(int i = 0; i < sig.length; i++)
            sig[i] = Math.sin(2*Math.PI*F/FPS*i);

        FirFilterRunning fir = new FirFilterRunning(taps);
        double[] y = fir.batch(sig);


        final XYSeries inSeries = new XYSeries("input");
        final XYSeries outSeries = new XYSeries("output");

        for(int j = 0; j < sig.length; j++) {
            inSeries.add(j, sig[j]);
            outSeries.add(j, y[j]);
        }

        LineChart lc = new LineChart("running_fir", new XYSeries[]{ inSeries, outSeries });

        //LineChart lc = LineChart.plot(filtered);
        lc.showChart();
        lc.waitChart();
    }

}
