#ifndef DECODEVEDIO_H
#define DECODEVEDIO_H

#include <QObject>
#include <QQueue>
#include <QTimer>
#include <QMutex>
#include <QWaitCondition>
#include <QImage>

#include "RingQueue/RingQueue.hpp"
#include "FrameFormat.h"


extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
// #include <libswscale/swscale.h>
// #include <libavutil/imgutils.h>
}





/**
 * 使用方式：
 *      1. 初始化FFmpeg:initFFmpeg()
 *      2. 开启解码线程:startDecodeVedio()
 *      3. 获取一帧图像:getOneImage()
 *      4. 停止解码线程:stopDecodeVedio()
 *      5. 在初始化完成后，未进入第二步的之前，可以获取视频的宽高信息，也可以设置宽高信息
 *      
 */
class DecodeVedio : public QObject
{
    Q_OBJECT

enum class DecodeState
{
    NONE = 0,
    DecodeRun,          /* 解码运行中 */
    DecodePause,        /* 暂停解码 */
    DecodeSeek,         /* 跳转中 */
    DecodeStop,         /* 停止解码，但是并没有退出解码线程 */
    DecodeExit          /* 退出解码 */
};

public:
    explicit DecodeVedio(QThread* thread, QObject* parent = nullptr);
    ~DecodeVedio();

    /* 打开视频，同时初始化解码器） */
    bool openVedio(const QString& fileName);
    
    /* 开始解码视频，开始前先打开视频文件 */
    void startDecodeVedio();
    /* 停止解码视频，也是停止线程 */
    void stopDecodeVedio();
    /* 获取解码状态 */
    bool isDecoding() { return m_threadRuning; }

    /* 设置当前播放位置，单位ms */
    void setCurrentPos(qint64 pos);
    /* 获取当前播放位置，单位ms */
    qint64 getCurrentPos();
    /* 获取视频时长 */
    qint64 getDuration();
    qint64 getTotalFrame() { return m_totalFrame; }
    

    /* 获取一帧图像 */
    FrameBase* getOneImage();
    /* 获取一帧图像，直到有图像为止，可以设置超时时间 */
    FrameBase* getOneImageUntilHave(int timeOut = -1);

    /* 获取帧数 */
    int getFPS() const { return m_fps; }
    /* 设置帧数 */
    void setFPS(int fps) { m_fps = fps; }
    /* 获取图像宽度 */
    QSize getSrcVideoSize() const {return m_srcSize; }
    /* 获取解码器名称（编码格式） */
    QString getDecoderName() const { return m_decoderName; }
    /* 获取硬件解码器名称列表 */
    QStringList getHWDecoderList() const { return m_listDecoderName; }
    /* 查找硬件解码器 */
    static void findHWDecoder(QStringList& listDecoderName);

signals:
    void signal_oneImage();                         /* 一帧图像信号 */
    void signal_playCompleted();                    /* 播放完成信号 */
    void signal_startDecode();                      /* 开始解码信号 */
    
private:
    /* 清空环形队列 */
    void clearQueueFrame();
    /* 删除一帧图片 */
    void deleteOneImage();

    /*----------------------- 解码相关的函数 ------------------------*/
    /* 查找硬件解码器 */
    void findHWDecoder();
    /* 初始化硬件解码器 */
    void initHWDecoder(const AVCodec* codec);
    /* 拷贝数据，从GPU显存拷贝到内存中 */
    bool copyDataFromGPU(AVFrame* pFrameHW, AVFrame* pFrameSRC);
                
    /* 软解码线程 */       
    void threadDecodeUsingCPU();
    /* 硬件解码线程 */
    void threadDecodeUsingGPU();
    /* 对生成的图像进行格式转换，并添加到环形队列中 */
    inline void convertImageFormatAndPushToQueue(AVFrame* pFrame);
    /* 将yuv420p的uint8_t数组缓存转换成Image_YUV420P结构体 */
    inline void convertToYUV420P(AVFrame* pFrame, Image_YUV420P* image);
    /* 转换成RGBA格式 */
    inline void convertToRGBA(AVFrame* pFrame, QImage* image);
    /* 退出线程 */      
    void exitThread();
    /* 暂停解码 */
    void pauseDecode();
    /* 继续解码 */
    void continueDecode();
    /* 将AVRational转换为double */
    qreal rationalToDouble(AVRational* rational);
    /* 释放所有资源 */
    void freeAll();

    /* 打印出错误信息 */
    void printErrorStr(int errRet);

private slots:
    void do_startDecodeVedio();                     /* 开启解码 */

private:
    QThread* m_thread = nullptr;                    /* 解码线程 */
    /* 线程状态 */
    std::atomic_bool m_threadRuning = false;        /* 解码线程是运行标志 */
    std::atomic_bool m_initFFmpeg = false;          /* ffmpeg初始化标志 */
    std::atomic_bool m_pauseDecode = false;         /* 暂停解码 */
    std::atomic_bool m_decodeStatus = false;        /* 解码状态，这里主要是检测是否暂停解码 */
    std::atomic_bool m_isSeek = false;              /* 是否在跳转中 */
    std::atomic_bool m_flushDecoder = false;        /* 刷新解码器 */
    std::atomic<DecodeState> m_decodeState = DecodeState::NONE;
    /* 视频解码相关变量信息 */
    QString m_fileName;                             /* 解码的视频文件名称 */
    AVFormatContext *m_pFormatContext = nullptr;    /* 格式上下文，贯穿全局 */
    AVCodecContext *m_pCodecContext = nullptr;      /* 解码器上下文 */
    AVPacket* m_packet = nullptr;                   /* 存储解码前的数据，一个数据包 */
    AVFrame* m_pFrameSRC = nullptr;                 /* 存储解码后的一帧数据原始视频编码 */
    AVFrame* m_pFrameHW = nullptr;                  /* 存储解码后的一帧数据，硬件解码 */
    struct SwsContext *m_sws_ctx = nullptr;         /* 视频转换上下文 */
    uint8_t *m_buffer = nullptr;                    /* 存储解码后的一帧数据，RGB格式 */
    int m_videoStream = -1;                         /* 记录视频流是第几个流 */
    
    bool m_supportHWDecoder = false;                /* 是否使用硬件解码 */
    AVBufferRef* m_hw_device_ctx = nullptr;         /* 对数据缓冲区的引用 */
    QList<int> m_listHWDeviceType;                  /* 保存当前环境支持的硬件解码器 */
    QStringList m_listDecoderName;                  /* 硬件解码器列表名称 */

    /* 视频相关信息 */
    QSize m_srcSize;                                /* 原始视频分辨率大小 */
    qint64 m_totalFrame = 0;                        /* 视频总帧数 */
    int m_fps = 0;                                  /* 每秒的帧数 */
    qint64 m_duration = 0;                          /* 视频时长，单位毫秒 */
    qint64 m_startPos = 0;                          /* 开始播放的位置，摄像机视频的位置不是从0开始的，需要在初始化的时候取出这个值 */
    std::atomic<qint64> m_pts = 0;                  /* 当前帧显示时间，也就是当前的进度时间 */
    
    qint64 m_targetPos = -1;                        /* 跳转的目标播放位置 */
    qint64 m_currentFrame = 0;                      /* 当前已播放的帧数 */
    QString m_decoderName;                          /* 解码器名称 */

    // EFrameFormat m_frameFormat = EFrameFormat::Frame_YUV420P; /* 视频帧格式，默认是YUV420P */
    RingQueue<FrameBase*> m_queueFrame;             /* 环形队列，存储生成的图像 */
};





#endif /* DECODEVEDIO_H */
