/***************************************************************************
**
**  This file is part of QGpCoreTools.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2007-06-20
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef PARALLELLOOP_H
#define PARALLELLOOP_H

#include <time.h>

#include "QGpCoreToolsDLLExport.h"
#include "CoreApplication.h"
#include "Thread.h"

namespace QGpCoreTools {

class LoopWorker;

class QGPCORETOOLS_EXPORT ParallelLoop : public QObject
{
  Q_OBJECT
public:
  ParallelLoop(QObject * parent=nullptr);
  virtual ~ParallelLoop();

  virtual void start(int iStart, int iEnd, bool forceParallel=false);
  void terminate();
  void waitFinished();

  virtual int maximumThreadCount() const {return CoreApplication::instance()->maximumThreadCount();}
  virtual int idealThreadCount(int /*stepCount*/) {return Thread::idealThreadCount();}
protected:
  virtual LoopWorker * newWorker()=0;
protected slots:
  virtual void workerFinished(LoopWorker * t=nullptr);
  void statusChanged(QString msg);
  void progressChanged(int value);
  void progressInit(int maximumValue);
signals:
  void statusChanged(int processIndex, QString msg);
  void progressChanged(int processIndex, int progress);
  void progressInit(int processIndex, int maximumValue);
  void finished();
private:
  inline LoopWorker * newWorker(int iEnd);
  struct TaskInfo {
    int index;
    int maximumProgress;
  };
  QMap<LoopWorker *, TaskInfo> _workers;
  Mutex _indexMutex;
  int _index;
};

class QGPCORETOOLS_EXPORT LoopWorker : public Thread
{
  Q_OBJECT
public:
  LoopWorker() : Thread() {_terminated=false; _affinity=-1;}

  void terminate() {_terminated.fetchAndStoreOrdered(true);}
  bool terminated() {return _terminated.testAndSetOrdered(true,true);}

  void setIndex(int * index, Mutex * mutex) {_index=index; _indexMutex=mutex;}
  void setEndIndex(int index) {_endIndex=index;}

  void setAffinity(int aff) {_affinity=aff;}
  int affinity() const {return _affinity;}

  virtual void run();
protected:
  virtual void run(int index)=0;
  void setProgressValue(int value);
  void setProgressMaximum(int value) {emit progressInit(value);}
  void setStatus(QString msg) {emit statusChanged(msg);}
  int endIndex() const {return _endIndex;}
signals:
  void statusChanged(QString msg);
  void progressInit(int maximumValue);
  void progressChanged(int value);
private:
  QAtomicInt _terminated;
  Mutex * _indexMutex;
  int * _index;
  int _endIndex;
  int _affinity;
  QAtomicInt _lastProgressTime;
};

inline void LoopWorker::setProgressValue(int value)
{
  TRACE;
  int now=time(nullptr);
  if(_lastProgressTime.fetchAndStoreOrdered(now)!=now) {
    emit progressChanged(value);
  }
}

} // namespace QGpCoreTools

#endif // PARALLELLOOP_H
