/***************************************************************************
**
**  This file is part of DinverCore.
**
**  DinverCore 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.
**
**  DinverCore 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: 2010-12-22
**  Copyright: 2010-2019
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef MODELREPOSITORY_H
#define MODELREPOSITORY_H

#include <QGpCoreTools.h>

#include "DinverCoreDLLExport.h"
#include "Generator.h"

namespace DinverCore {

  class ModelSet;
  class GeneratorModels;
  class NewModel;
  class ParentIndex;
  class AbstractForward;
  class ReportWriter;
  class RealSpace;
  class Parameter;

  class DINVERCORE_EXPORT ModelRepository: public QObject
  {
    Q_OBJECT
  public:
    ModelRepository(QObject * parent=nullptr);
    ~ModelRepository();

    bool operator==(const ModelRepository& o) const;

    bool setForward(const AbstractForward *f);
    void setStorage();
    void setBestModelCount(int nr);
    bool openReport(const QString& fileName);

    void setWalkCount(int n) {_walkCount=n;}
    void setMaximumModelCount(int n);
    void setMaximumBestModelCount(int n) {_maximumBestModelCount=n;}
    void setStableMisfitCount(int n) {_stableMisfitCount=n;}
    void setSignificantMisfitReduction(double relErr) {_significantMisfitReduction=relErr;}
    void setMaximumQueueLength(int l) {_maximumQueueLenght=l;}
    void setMaximumSavedMisfit(double m) {_maximumSavedMisfit=m;}
    void setGiveUp(double gu) {_giveUp=gu;}

    ParentIndex * createModel(double randomValue, RealSpace& parameterSpace) const;
    ModelSet * allModels() {return _allModels;}
    void addReport(NewModel * m, AbstractForward * forward);
    void addModel(NewModel * m);

    void init();
    bool start(int nThreads, Generator::Type type);
    void stop();
    void wait();

    bool importModels(QString fileName, bool strict=true);
    void clear();

    void lock() const;
    void unlock() const;

    int expectedModelCount() const {return _maximumModelCount;}
    int visitedModelCount() const;
    int validModelCount() const;
    int activeModelCount() const;
    int bestModelCount() const;
    int rejectedCount() const;
    int giveUpCount() const;
    int variableParameterCount () const;
    uint checksum() const {return _parameterSpaceChecksum;}

    double misfit(int modelIndex) const;
    const int * model(int modelIndex) const;
    int bestModelIndex() const;

    AbstractForward * forward() {return _forward;}
    const AbstractForward * forward() const {return _forward;}
    const Parameter * variableParameter(int paramIndex) const;
    double variableParameterValue(int modelIndex, int paramIndex) const;

    void printActiveModels();
  signals:
    void finished();
  private slots:
    void generatorFinished();
  private:
    void processQueue();

    int _walkCount;
    int _maximumModelCount;
    int _maximumBestModelCount;
    int _stableMisfitCount;
    double _significantMisfitReduction;
    double _lastBestMisfit;
    int _lastBestIndex;
    int _maximumQueueLenght;
    double _maximumSavedMisfit;
    double _giveUp;

    AtomicBoolean _terminated;
    QList<Generator *> _generators;

    AbstractForward * _forward;
    ModelSet * _allModels;

    mutable ReadWriteLock _generatorModelLock;
    GeneratorModels * _generatorModels;

    Mutex _queueLock;
    bool _processingQueue;
    QQueue<NewModel *> _newModels;

    Mutex _reportLock;
    ReportWriter * _report;
    uint _parameterSpaceChecksum;

    QElapsedTimer _chrono;
  };

} // namespace DinverCore

#endif // MODELREPOSITORY_H
