/***************************************************************************
    File                 : FFTFilter.cpp
    Project              : SciDAVis
    --------------------------------------------------------------------
    Copyright            : (C) 2007 by Ion Vasilief
    Email (use @ for *)  : ion_vasilief*yahoo.fr
    Description          : Numerical FFT filtering of data sets

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program is free software; you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation; either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#include "FFTFilter.h"

#include <QMessageBox>
#include <QLocale>

#include <gsl/gsl_fft_halfcomplex.h>

FFTFilter::FFTFilter(ApplicationWindow *parent, Graph *g, const QString& curveTitle, int m)
  : Filter(parent, g)
{
  d_sort_data=true;
  setDataFromCurve(curveTitle);
  init(m);
}

FFTFilter::FFTFilter(ApplicationWindow *parent, Graph *g, const QString& curveTitle,
                     double start, double end, int m)
  : Filter(parent, g)
{
  d_sort_data=true;
  setDataFromCurve(curveTitle, start, end);
  init(m);
}

void FFTFilter::init (int m)
{
  setObjectName(tr("FFT") + tr("Filtered"));
  setFilterType(m);
  d_points = d_n;
  d_offset = true;
  d_low_freq = 0;
  d_high_freq = 0;
}


void FFTFilter::setFilterType(int type)
{
  if (type < 1 || type > 4)
    {
      QMessageBox::critical((ApplicationWindow *)parent(), tr("SciDAVis") + " - " + tr("Error"),
                            tr("Unknown filter type. Valid values are: 1 - Low pass, 2 - High Pass, 3 - Band Pass, 4 - Band block."));
      d_init_err = true;
      return;
    }
  d_filter_type = (FilterType)type;
}

void FFTFilter::setCutoff(double f)
{
  if (d_filter_type > 2 && d_low_freq == f)
    return;

  d_low_freq = f;
}

void FFTFilter::setBand(double lowFreq, double highFreq)
{
  if (d_filter_type < 3)
    return;
  else if (lowFreq == highFreq)
    {
      QMessageBox::critical((ApplicationWindow *)parent(), tr("SciDAVis") + " - " + tr("Error"),
                            tr("Please enter different values for the band limits."));
      d_init_err = true;
      return;
    }

  if (lowFreq > highFreq)
    {
      d_low_freq = highFreq;
      d_high_freq = lowFreq;
    }
  else
    {
      d_low_freq = lowFreq;
      d_high_freq = highFreq;
    }
}

void FFTFilter::calculateOutputData(double *x, double *y)
{
  // interpolate y to even spacing
  double delta=(d_x[d_n-1]-d_x[0])/d_n;
  double xi=d_x[0];
  
  for (unsigned i=0, j=0; j<d_n && xi<=d_x[d_n-1]; j++)
    {
      x[j]=xi;
      if (i<d_n-1)
        y[j]=((d_x[i+1]-xi)*d_y[i] + (xi-d_x[i])*d_y[i+1])/(d_x[i+1]-d_x[i]);
      else
        y[j]=d_y[d_n-1];

      xi+=delta;
      while (i<d_n && xi>d_x[i]) i++;
      
    }

  double df = 1.0/(x[d_n-1]-x[0]);

  gsl_fft_real_workspace *work = gsl_fft_real_workspace_alloc(d_n);
  gsl_fft_real_wavetable *real = gsl_fft_real_wavetable_alloc(d_n);

  gsl_fft_real_transform (y, 1, d_n, real, work);
  gsl_fft_real_wavetable_free (real);

  d_explanation = QLocale().toString(d_low_freq) + " ";
  if (d_filter_type > 2)
    d_explanation += tr("to") + " " + QLocale().toString(d_high_freq) + " ";
  d_explanation += tr("Hz") + " ";

  switch ((int)d_filter_type)
    {
    case 1://low pass
      d_explanation += tr("Low Pass FFT Filter");
      for (int i = d_n-1; i >= 0 && ((i+1)/2)*df > d_low_freq; i--)
        y[i] = 0;
      break;

    case 2://high pass
      d_explanation += tr("High Pass FFT Filter");
      for (unsigned i = 0; i < d_n && ((i+1)/2)*df < d_low_freq; i++)
        y[i] = 0;
      break;

    case 3://band pass
      d_explanation += tr("Band Pass FFT Filter");
      for (unsigned i = d_offset ? 1 : 0; i < d_n; i++)
        if ((((i+1)/2)*df <= d_low_freq ) || (((i+1)/2)*df >= d_high_freq ))
          y[i] = 0;
      break;

    case 4://band block
      d_explanation += tr("Band Block FFT Filter");

      if(!d_offset)
        y[0] = 0;//substract DC offset

      for (unsigned i = 1; i < d_n; i++)
        if ((((i+1)/2)*df > d_low_freq ) && (((i+1)/2)*df < d_high_freq ))
          y[i] = 0;
      break;
    }

  gsl_fft_halfcomplex_wavetable *hc = gsl_fft_halfcomplex_wavetable_alloc (d_n);
  gsl_fft_halfcomplex_inverse (y, 1, d_n, hc, work);
  gsl_fft_halfcomplex_wavetable_free (hc);
  gsl_fft_real_workspace_free (work);
}
