#ifndef FUTUREWATCHER_H
#define FUTUREWATCHER_H

#include "FutureWatcherInterface.h"
#include <QTimer>
#include <future>

namespace nemo_interface {

//!
//! class similar to QFutureWatcher suitable for a std::future or
//! std::shared_future. \note For single thread use only.
//!
template <class T, template <class> class Future = std::future>
class FutureWatcher : public FutureWatcherInterface {
  enum class STATE { EMPTY, STARTED, FINISHED };

public:
  typedef Future<T> FutureType;

  explicit FutureWatcher(QObject *parent = nullptr)
      : FutureWatcherInterface(parent), _state(STATE::EMPTY) {
    _init();
  }

  FutureWatcher(const FutureType &f, QObject *parent = nullptr)
      : FutureWatcherInterface(parent), _future(f), _state(STATE::STARTED) {
    _init();
  }

  FutureType future() const {
    Q_ASSERT(this->_state != STATE::EMPTY);
    return _future;
  }

  void setFuture(const FutureType &future) {
    _future = future;
    _state = STATE::STARTED;
    _timer.start(1);
    emit started();
  }

  T result() {
    Q_ASSERT(this->_state != STATE::EMPTY);
    return _future.get();
  }

  virtual void waitForFinished() override {
    Q_ASSERT(this->_state != STATE::EMPTY);
    if (_state == STATE::STARTED) {
      _timer.stop();
      _future.wait();
      _state = STATE::FINISHED;
    }
  }

  virtual bool isStarted() override { return _state == STATE::STARTED; }

  virtual bool isFinished() override { return _state == STATE::FINISHED; }

private:
  void _onTimeout() {
    Q_ASSERT(this->_state == STATE::STARTED);
    if (_state == STATE::STARTED) {
      auto status = _future.wait_for(std::chrono::seconds(0));
      if (status == std::future_status::ready) {
        _timer.stop();
        _state = STATE::FINISHED;
        emit finished();
      }
    }
  }

  void _init() {
    connect(&_timer, &QTimer::timeout, [this] { this->_onTimeout(); });
  }

  FutureType _future;
  QTimer _timer;
  STATE _state;
};
} // namespace nemo_interface
#endif // FUTUREWATCHER_H
