/*
 * The MIT License
 *
 * Copyright © 2014 Filip Plesinger, Juraj Jurco.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 * Authors: 
 * 
 * Filip Plesinger <fplesinger@isibrno.cz>
 * Institute of Scientific Instruments of the ASCR, v. v. i.
 * Brno, Czech Republic
 * 
 * Juraj Jurco <xjuraj@isibrno.cz>
 * Institute of Scientific Instruments of the ASCR, v. v. i.
 * Brno, Czech Republic
 */

package cz.academyofscience.upt.challenge2014.filters;

/**
 *
 * @author xjuraj
 */
public class FIR {

    public static double[] generateLowPass(double order, double cutoff, double sampleFrequency) { //---založeno na Braindead + jedna prezentace z matlabu 
        double f = cutoff / (sampleFrequency / 2f);
        double[] fir = new double[(int) order];
        int i = 0;
        for (double n = -((order - 1) / 2); n <= (order - 1) / 2; n++) {
            double nr = n;
            if (nr == 0) {
                nr = Double.MIN_VALUE;
            }

            double val = Math.sin(nr * f * Math.PI) / (nr * Math.PI);
            //double weight = 0.54 + 0.46 * Math.Cos(2 * Math.PI * nr / order);

            double weight = 1;

            fir[i] = val * weight;
            i++;
        }

        return fir;
    }

    public static double[] generateHighPass(double order, double cutoff, double sampleFrequency) { //---založeno na Braindead + jedna prezentace z matlabu 
        double f = cutoff / (sampleFrequency / 2f);
        double[] fir = new double[(int) order];

        int i = 0;
        for (double n = -((order - 1) / 2); n <= (order - 1) / 2; n++) {
            double nr = n;
            if (nr == 0) {
                nr = Double.MIN_VALUE;
            }

            double val = (Math.sin(nr * Math.PI) - Math.sin(nr * f * Math.PI)) / (nr * Math.PI);
            //double weight = 0.54 + 0.46 * Math.Cos(2 * Math.PI * nr / order);

            double weight = 1;

            fir[i] = val * weight;
            i++;
        }

        return fir;
    }

    public static double[] generateBandPass(double order, double cutoff, double sampleFrequency, double freq2) { //---založeno na Braindead + jedna prezentace z matlabu 
        double f1 = cutoff / (sampleFrequency / 2f);
        double f2 = freq2 / (sampleFrequency / 2f);
        double[] fir = new double[(int) order];

        int i = 0;
        for (double n = -((order - 1) / 2); n <= (order - 1) / 2; n++) {
            double nr = n;
            if (nr == 0) {
                nr = Double.MIN_VALUE;
            }

            double val = Math.sin(nr * f2 * Math.PI) / (nr * Math.PI) - Math.sin(nr * f1 * Math.PI) / (nr * Math.PI);
            //double weight = 0.54 + 0.46 * Math.Cos(2 * Math.PI * nr / order);

            double weight = 1;

            fir[i] = val * weight;
            i++;
        }
        return fir;
    }

    public static double[] doFIR(double[] inputSignal, double[] firCoefficients) {
        int M = firCoefficients.length;
        int n = inputSignal.length;

        double[] output = new double[inputSignal.length];

        for (int j = 0; j < n; j++) {
            double t = 0.0;

            for (int i = 0; i < M; i++) {
                int cI = i - M / 2;
                int sampleIndex = j - cI;

                if (sampleIndex >= 0 && sampleIndex < inputSignal.length) {
                    t += firCoefficients[i] * inputSignal[sampleIndex];
                }
            }
            output[j] = t;
        }
        return output;
    }
    
    public static double[] generateByString(String filterSettings, double sampleFrequency){
        String[] firBp = filterSettings.split("\\|");
        if(firBp.length != 2){
            throw new IllegalArgumentException("Wrong filter string! FilterString has to be in format: [FIR|IIR]:[order]|[LP|HP|BP]:[CUT_FREQ|FROM_FREQ-TO_FREQ]");
        }
        String[] fir = firBp[0].split(":");
        String[] bp = firBp[1].split(":");
        if(fir.length != 2 || !fir[0].toUpperCase().equals("FIR")){
            throw new IllegalArgumentException("Wrong filter string! FilterString has to be in format: [FIR|IIR]:[order]|[LP|HP|BP]:[CUT_FREQ|FROM_FREQ-TO_FREQ]");
        }
        if(bp.length != 2){
            throw new IllegalArgumentException("Wrong filter string! FilterString has to be in format: [FIR|IIR]:[order]|[LP|HP|BP]:[CUT_FREQ|FROM_FREQ-TO_FREQ]");
        }
        double order = Double.parseDouble(fir[1]);
        String[] freqs = bp[1].split("-");
        double fstFreq = Double.parseDouble(freqs[0]);
        double sndFreq = Double.NaN;
        if(freqs.length == 2){
            sndFreq = Double.parseDouble(freqs[1]);
        }
        switch(bp[0].toUpperCase()){
            case "BP": return FIR.generateBandPass(order, fstFreq, sampleFrequency, sndFreq); 
            case "LP": return FIR.generateBandPass(order, fstFreq, sampleFrequency, sndFreq); 
            case "HP": return FIR.generateBandPass(order, fstFreq, sampleFrequency, sndFreq);
            default: 
                throw new IllegalArgumentException("Wrong filter string! FilterString has to be in format: [FIR|IIR]:[order]|[LP|HP|BP]:[CUT_FREQ|FROM_FREQ-TO_FREQ]");
        }
    }
}
