/***************************************************************************
**
**  This file is part of ArrayCore.
**
**  ArrayCore 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 3 of the License, or
**  (at your option) any later version.
**
**  ArrayCore 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2008-07-15
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "GaussianFrequencyBand.h"

namespace ArrayCore {

  /*!
    \class GaussianFrequencyBand GaussianFrequencyBand.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  GaussianFrequencyBand::GaussianFrequencyBand()
  {
    _gaussian=nullptr;
    _gaussianPtr=nullptr;
  }

  GaussianFrequencyBand::GaussianFrequencyBand(const GaussianFrequencyBand& o)
  {
    _iFreqMin=o._iFreqMin;
    _iFreqMax=o._iFreqMax;
    int n=_iFreqMax-_iFreqMin+1;
    _gaussian=new double [n];
    _gaussianPtr=_gaussian-_iFreqMin;
    memcpy(_gaussian, o._gaussian, sizeof(double)*n);
  }

  /*!
    Description of destructor still missing
  */
  GaussianFrequencyBand::~GaussianFrequencyBand()
  {
    delete [] _gaussian;
  }

  /*!
    Description still missing
  */
  void GaussianFrequencyBand::calculate(const FrequencyBand& f, double windowLength,
                                        double samplingFrequency)
  {
    TRACE;
//#define COMPATIBILITY_2_5_0
#define GAUSS_WEIGHTING
    // Calculate frequency indexes of the bandwidth limits
#ifdef COMPATIBILITY_2_5_0
    _iFreqMin=qRound(windowLength*f.lowLimit());
    _iFreqMax=qRound(windowLength*f.highLimit());
#else
    if(f.relativeWidth()==0.0) {
      _iFreqMin=qRound(windowLength*f.center());
      _iFreqMax=_iFreqMin;
    } else {
      _iFreqMin=qCeil(windowLength*f.lowLimit());
      _iFreqMax=qFloor(windowLength*f.highLimit());
    }
#endif
    // Check that frequency range is available
    if(_iFreqMin<0) {
      _iFreqMin=0;
    }
    if(_iFreqMax<0) {
      _iFreqMax=0;
    }
    // Check that the frequency index range is available on signals
    // See Nyquist index computation in DoubleSignal constructor
    int iFreqMax=qFloor(windowLength*samplingFrequency) >> 1;
    if(_iFreqMax>iFreqMax) {
      _iFreqMax=iFreqMax;
    }
    if(_iFreqMin>iFreqMax) {
      _iFreqMin=iFreqMax;
    }
    int iSub=_iFreqMax-_iFreqMin;
    delete [] _gaussian;
    _gaussian=new double[iSub+1];
    _gaussianPtr=_gaussian-_iFreqMin;
    if(_iFreqMin==_iFreqMax) { // Frequency range reduced to one sample
      _gaussianPtr[_iFreqMin]=1.0;
#ifndef COMPATIBILITY_2_5_0
      _exactFrequency=_iFreqMin/windowLength;
#endif
    } else {
      _exactFrequency=f.center();
#ifdef GAUSS_WEIGHTING
      /*
        Multiplication of spectrum by a Gaussian window function:
        G(f)=exp(-((2/(b*fc)*(f-fc))^2), hence sigma=b*Fc/sqrt(8), sqrt(8)=2.83
        which imply that G(fc-b*fc)=0.01378
        gpcurve -function "b=0.1;fc=4;sigma=b*fc/Math.sqrt(8);return Math.exp(-Math.pow(2/(b*fc)*(x-fc),2))" -min 3 -max 5 -dx 0.01 | figue -c

        Tried with a log-normal distribution but the sigma is rather low here and
        it is not so different from a normal distribution
        To plot an example around 4 Hz:
        gpcurve -function "b=0.1;fc=4;sigma=Math.log(1+b*fc/Math.pow(fc,2));mu=Math.log(fc)+Math.pow(sigma, 2);return 1/(x*sigma*Math.sqrt(2*3.141592))*Math.exp(-Math.pow(Math.log(x)-mu,2)/(2*Math.pow(sigma,2)))" -min 3 -max 5 -dx 0.01 | figue -c
      */
      double b=2.0/f.relativeWidth();
      double a=2.0/(windowLength*f.halfWidth());
      for(int i=_iFreqMin; i<=_iFreqMax; i++) {
        double v=a*i-b;
        _gaussianPtr[i]=exp(-v*v);
      }
#else
      for(int i=_iFreqMin; i<=_iFreqMax; i++) {
        _gaussianPtr[i]=1.0;
      }
#endif
    }
    if(App::verbosity()>=3) {
      QString str=tr("Weighting function from index %1 to %2\n");
      str=str.arg(_iFreqMin).arg(_iFreqMax);
      for(int i=_iFreqMin; i<=_iFreqMax; i++) {
        str+=tr("  %1 %2 %3\n").arg(i).arg(i/windowLength, 0, 'g', 20).arg(_gaussianPtr[i], 0, 'g', 20);
      }
      App::log(str);
    }
  }

  /*!
    To be conform with previous versions. the filter was applied to the power spectrum.
    In cross spectrum construction, the raw spectrum is multipled by this function.
  */
  void GaussianFrequencyBand::sqrt()
  {
    for(int i=_iFreqMin; i<=_iFreqMax; i++) {
      _gaussianPtr[i]=::sqrt(_gaussianPtr[i]);
    }
  }

} // namespace ArrayCore
