/***************************************************************************
**
**  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: 2017-03-13
**  Copyright: 2017-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#ifndef FKRESULTS_H
#define FKRESULTS_H

#include <GeopsyCore.h>
#include <QGpCoreStat.h>

#include "AbstractArrayResults.h"
#include "ArrayCoreDLLExport.h"
#include "WaveNumberConverter.h"
#include "FKPeaks.h"
#include "ArrayStations.h"
#include "FKParameters.h"

namespace ArrayCore {

  class ARRAYCORE_EXPORT FKResults : public AbstractArrayResults
  {
  public:
    FKResults();
    virtual ~FKResults();

    void setArray(const ArraySelection * array);
    virtual void clear();

    double minimumSlowness() const {return _minimumSlowness;}
    void setMinimumSlowness(double s) {_minimumSlowness=s;}

    ArrayStations::Mode mode() const {return _mode;}
    const FKParameters * parameters() const {return static_cast<const FKParameters *>(_parameters);}

    inline bool add(const Point2D &k, double ell, double noise, double power,
                    const WaveNumberConverter& conv);
    inline void add(const FKPeaks& peaks);

    bool isEmpty() const {return _peaks.size()==0;}

    class ARRAYCORE_EXPORT AbstractAction
    {
    public:
      AbstractAction() {}
      virtual void run(Histogram2D * h)=0;
      virtual void run(Histogram2D * h, QGpCoreWave::Mode::Polarization) {run(h);}
      virtual QString title() const=0;
    };

    class ARRAYCORE_EXPORT SmallFractionFilter: public AbstractAction
    {
    public:
      SmallFractionFilter(double value);
      virtual void run(Histogram2D * h);
      virtual QString title() const {return tr("Small fraction filter");}
    private:
      double _value;
    };

    class ARRAYCORE_EXPORT HitCountFilter: public AbstractAction
    {
    public:
      HitCountFilter(double value);
      virtual void run(Histogram2D * h);
      virtual QString title() const {return tr("Hit count filter");}
    private:
      double _value;
    };

    class ARRAYCORE_EXPORT GaussianMixture: public AbstractAction
    {
    public:
      GaussianMixture(FKResults * results, const QString& baseName);
      virtual void run(Histogram2D *) {}
      virtual QList<Curve<RealStatisticalPoint>> scan(Histogram2D * h, QGpCoreWave::Mode::Polarization polarization);
      virtual QString title() const {return tr("Gaussian mixture");}
    private:
      FKResults * _results;
      QString _baseName;
    };

    class ARRAYCORE_EXPORT MedianCurve: public AbstractAction
    {
    public:
      MedianCurve(double maxMisfit, FKResults * results, const QString& baseName);
      virtual void run(Histogram2D * h);
      virtual void run(Histogram2D * h, QGpCoreWave::Mode::Polarization polarization);
      virtual QString title() const {return tr("Median curve");}
    private:
      double _maxMisfit;
      FKResults * _results;
      QString _baseName;
    };

    void aviosPick(const QString& groupName);

    void run(AbstractAction * a);
    void horizontalSingleDirection(double angleTolerance);

    bool save(QString fileName);
    bool load(QString baseName);

    void filterArrayLimits(Curve<RealStatisticalPoint>& c, double kminFactor=1.0, double kmaxFactor=1.0) const;
    static bool saveCurve(const Curve<RealStatisticalPoint>& c, const QString &fileName);
    static bool loadCurve(Curve<RealStatisticalPoint>& c, const QString &fileName);
    bool loadCurves(const QString& path);

    bool setRange(FKParameters& param) const;
    void setCurveRange(FKParameters& param, const Curve<RealStatisticalPoint>& curve, double fraction,
                       QGpCoreWave::Mode::Polarization polarization) const;

    const QList<Curve<RealStatisticalPoint> >& rayleighCurves() const {return _rayleighCurves;}
    const QList<Curve<RealStatisticalPoint> >& loveCurves() const {return _loveCurves;}
private:
    Histogram2D * initSlownessHistogram() const;
    Histogram2D * initSlownessWaveNumberHistogram() const;
    Histogram2D * initWaveNumberHistogram() const;
    Histogram2D * initAzimuthHistogram() const;
    Histogram2D * initEllipticityHistogram() const;

    Curve<RealStatisticalPoint> removeNonLinearPoints(Curve<RealStatisticalPoint> c, double maxNonLinear);
    void range(const QList<Curve<RealStatisticalPoint> >& curves, double& fMin, double& fMax, double& sMax) const;

    int _binCount;
    double _minimumSlowness;

    ArrayStations::Mode _mode;

    FKPeaks _peaks;

    QList<Curve<RealStatisticalPoint> > _rayleighCurves;
    QList<Curve<RealStatisticalPoint> > _loveCurves;

    QStringList _files;
  };

  inline bool FKResults::add(const Point2D &k, double ell, double noise, double power,
                             const WaveNumberConverter& conv)
  {
    return _peaks.add(k, ell, noise, power, conv, _minimumSlowness);
  }

  inline void FKResults::add(const FKPeaks& peaks)
  {
    return _peaks.add(peaks);
  }

} // namespace ArrayCore

#endif // FKRESULTS_H

