#pragma once

#include <QObject>
#include <QPointer>
#include <QTimer>

#include <gst/gst.h>
#include <gst/app/gstappsink.h>

class VideoItem;

class RtspPlayer : public QObject
{
   Q_OBJECT

   Q_PROPERTY(QString status READ status NOTIFY statusChanged)
   Q_PROPERTY(QString errorString READ errorString NOTIFY errorStringChanged)
   Q_PROPERTY(bool playing READ playing NOTIFY playingChanged)
   Q_PROPERTY(int latency READ latency WRITE setLatency NOTIFY latencyChanged)

public:
   explicit RtspPlayer(QObject *parent = nullptr);
   ~RtspPlayer() override;

   QString status() const;
   QString errorString() const;
   bool playing() const;
   int latency() const;

   void setLatency(int latency);

   Q_INVOKABLE void attachVideoItem(QObject *item);
   Q_INVOKABLE void play(
      const QString &url,
      const QString &username,
      const QString &password,
      const QString &displayName
   );
   Q_INVOKABLE void stop();
   Q_INVOKABLE void restart();

signals:
   void statusChanged();
   void errorStringChanged();
   void playingChanged();
   void latencyChanged();

private slots:
   void pollBus();

private:
   static void onRtspPadAdded(
      GstElement *source,
      GstPad *newPad,
      gpointer userData
   );

   static void onDecoderPadAdded(
      GstElement *decoder,
      GstPad *newPad,
      gpointer userData
   );

   static GstFlowReturn onNewSample(
      GstAppSink *sink,
      gpointer userData
   );

   bool createPipeline(const QString &location);
   void destroyPipeline();
   void setStatus(const QString &status);
   void setErrorString(const QString &error);
   void setPlaying(bool playing);
   QString buildLocation(
      const QString &url,
      const QString &username,
      const QString &password
   ) const;

   QPointer<VideoItem> m_videoItem;

   GstElement *m_pipeline = nullptr;
   GstElement *m_source = nullptr;
   GstElement *m_depay = nullptr;
   GstElement *m_parser = nullptr;
   GstElement *m_decoder = nullptr;
   GstElement *m_convert = nullptr;
   GstElement *m_capsFilter = nullptr;
   GstElement *m_sink = nullptr;
   GstBus *m_bus = nullptr;

   QTimer m_busTimer;

   QString m_status = QStringLiteral("Configure a camera from the menu.");
   QString m_errorString;
   bool m_playing = false;
   int m_latency = 200;

   QString m_lastUrl;
   QString m_lastUsername;
   QString m_lastPassword;
   QString m_lastDisplayName;
};
