/***

  Olive - Non-Linear Video Editor
  Copyright (C) 2022 Olive Team

  This program 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.

  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.

***/

#ifndef RENDERTICKET_H
#define RENDERTICKET_H

#include <QDateTime>
#include <QMutex>
#include <QWaitCondition>

#include "codec/frame.h"
#include "codec/samplebuffer.h"
#include "common/cancelableobject.h"
#include "common/timerange.h"
#include "node/output/viewer/viewer.h"

namespace olive {

class RenderTicket : public QObject, public CancelableObject
{
  Q_OBJECT
public:
  RenderTicket();

  /**
   * @brief Get the ticket's current state
   *
   * This function is thread safe, unless `lock` is set to false. Then the caller has responsibility
   * of locking the mutex before and unlocking after this function is called.
   */
  bool IsRunning(bool lock = true);

  /**
   * @brief Determine how many times ticket has been finished
   *
   * This function is thread safe, unless `lock` is set to false. Then the caller has responsibility
   * of locking the mutex before and unlocking after this function is called.
   */
  int GetFinishCount(bool lock = true);

  /**
   * @brief Check if this ticket has a result
   *
   * If this ticket is running, this will always return false.
   */
  bool HasResult();

  /**
   * @brief Get value, if any
   */
  QVariant Get();

  /**
   * @brief Wait for ticket to be finished
   *
   * If this ticket is not running, this function returns immediately.
   */
  void WaitForFinished();
  void WaitForFinished(QMutex* mutex);

  /**
   * @brief Access this ticket's mutex
   *
   * Use if you're doing several operations on a ticket and need to ensure thread safety while
   * doing so. Most of the time this isn't necessary since all functions are thread safe by default.
   */
  QMutex* lock()
  {
    return &lock_;
  }

  /**
   * @brief Signal to the ticket that it is running
   *
   * If any value is set, it is cleared.
   */
  void Start();

  /**
   * @brief Finish ticket with no value
   *
   * Sets ticket to no longer running and assume it has received no result.
   */
  void Finish();

  /**
   * @brief Finish ticket with value
   *
   * Sets ticket to no longer running and provide a value generated by the operation requested.
   */
  void Finish(QVariant result);

signals:
  /**
   * @brief Emitted when finish has been called by any means (either cancelled or with a result)
   */
  void Finished();

private:
  void FinishInternal(bool has_result, QVariant result);

  bool is_running_;

  QVariant result_;

  bool has_result_;

  int finish_count_;

  QMutex lock_;

  QWaitCondition wait_;

};

using RenderTicketPtr = std::shared_ptr<RenderTicket>;

class RenderTicketWatcher : public QObject
{
  Q_OBJECT
public:
  RenderTicketWatcher(QObject* parent = nullptr);

  RenderTicketPtr GetTicket() const
  {
    return ticket_;
  }

  void SetTicket(RenderTicketPtr ticket);

  bool IsRunning();

  void WaitForFinished();

  QVariant Get();

  bool HasResult();

  void Cancel();

signals:
  void Finished(RenderTicketWatcher* watcher);

private:
  RenderTicketPtr ticket_;

private slots:
  void TicketFinished();

};

}

Q_DECLARE_METATYPE(olive::RenderTicketPtr)

#endif // RENDERTICKET_H
