/***************************************************************************
**
**  This file is part of QGpCoreStat.
**
**  QGpCoreStat 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.
**
**  QGpCoreStat 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-06-11
**  Copyright: 2017-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "HistogramDensities.h"
#include "Likelihood.h"

namespace QGpCoreStat {

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

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  HistogramDensities::HistogramDensities(QObject * parent)
    : QObject(parent)
  {
    TRACE;
    _currentIndex=-1;
    _stopIndex=0;
    _na=nullptr;
    _densities=nullptr;
  }

  /*!
    Description of destructor still missing
  */
  HistogramDensities::~HistogramDensities()
  {
    TRACE;
    if(_na) {
      _na->stop();
      _na->wait();
      delete _na;
    }
    delete [] _densities;
  }

  void HistogramDensities::setHistogram(Histogram2D * h)
  {
    TRACE;
    _histogram=h;
    delete [] _densities;
    _densities=new Densities[_histogram->nx()];
  }

  void HistogramDensities::start()
  {
    TRACE;
    _currentIndex=-1;
    _stopIndex=_histogram->nx();
    internalStart();
  }

  void HistogramDensities::start(int index)
  {
    TRACE;
    if(index<0) {
      index=0;
    }
    _stopIndex=index+1;
    if(_stopIndex>_histogram->nx()) {
      _stopIndex=_histogram->nx();
    }
    _currentIndex=index-1;
    internalStart();
  }

  void HistogramDensities::internalStart()
  {
    TRACE;
    if(_na) {
      _na->stop();
      _na->wait();
      delete _na;
      _na=nullptr;
    }

    _na=new ModelRepository;
    Likelihood forward(&_parameters);
    QVector<double> samples;
    do {
      // Extract values for current index from histogram samples
      do {
        _currentIndex++;
        if(_currentIndex==_stopIndex) {
          App::freeze(false);
          delete _na;
          _na=nullptr;
          emit finished();
          return;
        }

        samples.clear();
        for(int i=_histogram->sampleCount()-1; i>=0; i--) {
          const Histogram2D::Sample& s=_histogram->sample(i);
          if(s.isValid() && _histogram->indexOfX(s.x())==_currentIndex) {
            samples.append(s.y());
          }
        }
        App::freeze(false);
        App::log(1, tr("      %1/%2 at x=%3 found %4 samples\n")
                         .arg(_currentIndex+1)
                         .arg(_histogram->nx())
                         .arg(_histogram->x(_currentIndex))
                         .arg(samples.count()));
        App::freeze(!_parameters.verbose());
      } while (samples.isEmpty());

      // Current implementation is uggly: we do not know the type of Y axis, so wavenumber is assumed
      // Parameter kmin and kmax should be renamed as min and max acceptable values
      // No transformation should take place here.
      double k1, k2;
      if(_parameters.kmin()>0.0) {   // kmin/2
        k1=0.5*_parameters.kmin();
        k2=_histogram->minimumAxis(YAxis);
        forward.setMinimumValue(k1<k2 ? k2 : k1);
      } else {
        forward.setMinimumValue(_histogram->minimumAxis(YAxis));
      }
      if(_parameters.kmax()>0.0) {   // kmax assumed to be computed with threshold set to 0.25
        k1=_parameters.kmax();
        k2=_histogram->maximumAxis(YAxis);
        forward.setMaximumValue(k1<k2 ? k1 : k2);
      } else {
        forward.setMaximumValue(_histogram->maximumAxis(YAxis));
      }

      forward.setSampleCount(samples.count());
      for(int i=samples.count()-1; i>=0; i--) {
        forward.setSample(i, samples[i]);
      }
    } while(!forward.setInversionParameters() ||
            !_na->setForward(&forward));

    QString reportFileName="/tmp/gaussmix.report";
    ReportWriter::initReport(reportFileName, "", ReportWriter::Overwrite);
    _na->openReport(reportFileName);

    _na->setStorage();
    _na->setStableMisfitCount(2000);
    _na->setSignificantMisfitReduction(0.05);
    _na->setBestModelCount(10);
    _na->setMaximumModelCount(50);
    connect(_na, SIGNAL(finished()), this, SLOT(monteCarloFinished()));
    _na->start(INT_MAX, Generator::MonteCarlo);
  }

  void HistogramDensities::wait()
  {
    TRACE;
    while(_currentIndex<_histogram->nx()) {
      CoreApplication::processEvents(QEventLoop::AllEvents, 1000);
    }
  }

  void HistogramDensities::monteCarloFinished()
  {
    TRACE;
    disconnect(_na, SIGNAL(finished()));
    _na->setMaximumModelCount(50000);
    connect(_na, SIGNAL(finished()), this, SLOT(neighborhoodFinished()));
    _na->start(INT_MAX, Generator::Neighborhood);
  }

  void HistogramDensities::neighborhoodFinished()
  {
    TRACE;
    disconnect(_na, SIGNAL(finished()));
    int bestIndex=_na->bestModelIndex();
    if(bestIndex>=0) {
      Likelihood * f=static_cast<Likelihood *>(_na->forward());
      f->setValues(_na->model(bestIndex));
      _densities[_currentIndex]=*f;
    }
    emit histogramFinished(_currentIndex);

    internalStart();
  }

} // namespace QGpCoreStat

