/***************************************************************
 * Copyright (C) 2020, 2026, Val Doroshchuk <valbok@gmail.com> *
 *                                                             *
 * This file is part of QtAVPlayer.                            *
 * Free Qt Media Player based on FFmpeg.                       *
 ***************************************************************/

#pragma once

#include <QObject>
#include <QtAVPlayer/qavplayer.h>
#include <QtAVPlayer/qavaudiooutput.h>
#include <QtAVPlayer/qavsubtitletextparser.h>
#if defined(QT_AVPLAYER_LIBASS)
#include <QtAVPlayer/qavassrenderer.h>
#endif
#include <QVideoSink>
#include <QVideoFrame>
#include <QString>
#include <qqml.h>
#include <QAtomicInt>
#include <QQuickItem>
#include <QTimer>
#include <QMutex>
#include <QMediaDevices>

class SubtitleItem : public QQuickItem
{
    Q_OBJECT
public:
    explicit SubtitleItem(QQuickItem *parent = nullptr);

public slots:
    void setImage(const QImage &img);

protected:
    QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) override;

private:
    QImage m_image;
    QMutex m_mutex;
};

/**
 * PlayerController
 *
 * Thin bridge between QAVPlayer (C++) and the QML UI.
 * Exposes playback controls, position, duration and media status
 * as Q_PROPERTYs and slots callable from QML.
 *
 * Video frames are pushed directly into a QVideoSink which is
 * consumed by a QML VideoOutput element (copy-free when HW accel
 * is active and the Qt render backend supports it).
 */
class PlayerController : public QObject
{
    Q_OBJECT
    QML_ELEMENT

    Q_PROPERTY(bool playing READ isPlaying NOTIFY playingChanged)
    Q_PROPERTY(qint64 position READ position NOTIFY positionChanged)
    Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged)
    Q_PROPERTY(qreal volume READ volume WRITE setVolume NOTIFY volumeChanged)
    Q_PROPERTY(bool hasMedia READ hasMedia NOTIFY hasMediaChanged)
    Q_PROPERTY(QString errorString READ errorString NOTIFY errorOccurred)

    Q_PROPERTY(QStringList subtitleTracks READ subtitleTracks NOTIFY subtitleTracksChanged)
    Q_PROPERTY(QStringList audioTracks READ audioTracks NOTIFY audioTracksChanged)
    Q_PROPERTY(QStringList videoTracks READ videoTracks NOTIFY videoTracksChanged)
    Q_PROPERTY(QStringList audioDevices READ audioDevices NOTIFY audioDevicesChanged)
    Q_PROPERTY(int audioDeviceIndex READ audioDeviceIndex NOTIFY audioDeviceChanged)

public:
    explicit PlayerController(QObject *parent = nullptr);

    // Property accessors
    bool isPlaying() const { return m_playing; }
    qint64 position() const { return m_position; }
    qint64 duration() const { return m_duration; }
    qreal volume() const { return m_volume; }
    bool hasMedia() const { return m_hasMedia; }
    QString errorString() const { return m_errorString; }
    QStringList subtitleTracks() const;
    QStringList audioTracks() const;
    QStringList videoTracks() const;
    QStringList audioDevices() const;
    int audioDeviceIndex() const;
    void setSubtitleItem(SubtitleItem *item);

    void setVolume(qreal v);

public slots:
    // Called from QML
    void setVideoSink(QVideoSink *sink);
    void openFile(const QString &path);
    void openUrl(const QString &url);
    void play();
    void pause();
    void stop();
    void seek(qint64 ms);
    void stepForward();
    void stepBackward();
    void setSubtitleTrack(int index);
    void setAudioTrack(int index);
    void setAudioDevice(int index);
    void setVideoTrack(int index);
    void setVideoCodec(const QString &codec);
    void setCopyFreeRender(bool copyfree);

signals:
    void playingChanged();
    void positionChanged();
    void durationChanged();
    void volumeChanged();
    void hasMediaChanged();
    void errorOccurred();
    void subtitleTracksChanged();
    void subtitleTrackChanged(int index);
    void subtitleTextChanged(const QString &text, int duration);
    void subtitleImageChanged(const QImage &img, int duration);
    void audioTracksChanged();
    void audioTrackChanged(int index);
    void audioDevicesChanged();
    void audioDeviceChanged();
    void videoTracksChanged();
    void videoTrackChanged(int index);
    void hwDeviceChanged(bool hw);
    void assRendererChanged(bool enabled);

private:
    void connectPlayerSignals();
    void setSource(const QString &source);
    void notifyStreams();
    void reset();

    QAVAudioOutput m_audioOutput;
    QMediaDevices m_mediaDevices;
    QVideoSink *m_videoSink = nullptr;
    QAVSubtitleTextParser m_subtitleParser;
#if defined(QT_AVPLAYER_LIBASS)
    QAVASSRenderer m_subtitleRenderer;
#endif
    bool m_playing = false;
    qint64 m_position = 0;
    qint64 m_duration = 0;
    qreal m_volume = 1.0;
    bool m_hasMedia = false;
    QString m_errorString;
    QString m_videoCodec;
    QAtomicInt m_copyFreeRender{1};
    // Keep the last to be destroyed first
    QAVPlayer m_player;
    QTimer m_posTimer;
};
