#include "VideoPlayer.h"

#include "DecodeVedio.h"

#include <QPainter>
#include <QResizeEvent>
#include <QEventLoop>
#include <QVBoxLayout>

#include "spdlog/spdlog.h"
#include "FmtLog/fmtlog.h"


VideoPlayer::VideoPlayer(QWidget *parent) : QWidget(parent)
{
    // /* 初始化解码线程 */
    // m_threadDecode = new QThread(this);
    // m_decodeVedio = new DecodeVedio(m_threadDecode);

    m_previewImage = 2;
    m_fps = 0;

    m_semRefresh = new QSemaphore(0);

    m_timerRefreshUI.setSingleShot(false);
    /* 设置精度毫秒级 */
    m_timerRefreshUI.setTimerType(Qt::PreciseTimer);
    connect(&m_timerRefreshUI, &QTimer::timeout, this, &VideoPlayer::do_refreshUI);
    connect(this, &VideoPlayer::signal_refreshImage, this, &VideoPlayer::do_refreshSamImage);

    SPDLOG_TRACE("播放器线程ID:{}", QThread::currentThreadId());
    QStringList listDecoder;
    DecodeVedio::findHWDecoder(listDecoder);
    if(listDecoder.isEmpty())
    {
        SPDLOG_WARN("没有找到硬件解码器");
    }else {
        SPDLOG_DEBUG("支持的硬件解码器:");
        for(auto it : listDecoder)
        {
            SPDLOG_DEBUG("{}", it.toStdString());
        }
    }
}

VideoPlayer::~VideoPlayer()
{
    if(m_timerRefreshUI.isActive())
    {
        m_timerRefreshUI.stop();
    }
    delete m_decodeVedio;
    if(m_image)
    {
        delete m_image;
    }
    SPDLOG_DEBUG("视频播放器已关闭");
}

/**
 * @brief 设置播放视频,启动定时器,定时器间隔决定播放的速度
 *        视频的宽和高使用QImage进行缩放
 *        视频大小在直接设置这个类的resize即可,有最小大小限制
 * 
 * @param fileName 
 */
void VideoPlayer::openPlayVedio(const QString& fileName)
{
    if(m_isOpenFile)
    {
        m_isOpenFile = false;
        stop();
    }
    if(m_decodeVedio == nullptr)
    {
        /* 初始化解码线程 */
        m_threadDecode = new QThread(this);
        m_decodeVedio = new DecodeVedio(m_threadDecode);
        connect(m_decodeVedio, &DecodeVedio::signal_playCompleted, this, &VideoPlayer::do_playCompleted);
    }
    if(m_decodeVedio->isDecoding())
    {
        m_decodeVedio->stopDecodeVedio();
    }
    if(fileName.isEmpty())
    {
        SPDLOG_WARN("文件名为空");
        return;
    }
    m_fileName = fileName;
    m_isOpenFile = true;
    m_isLocalFile = isLocalFile(fileName);

    m_decodeVedio->openVedio(fileName);
    /* 获取原始视频信息 */
    m_srcWidth = m_decodeVedio->getSrcVideoSize().width();
    m_srcHeight = m_decodeVedio->getSrcVideoSize().height();
    m_fps = m_decodeVedio->getFPS();
    m_duration = m_decodeVedio->getDuration();
    auto totalFarame = m_decodeVedio->getTotalFrame();
    SPDLOG_INFO("视频编码格式:{}", m_decodeVedio->getDecoderName().toStdString());
    int hh = m_duration / 3600000;
    int mm = (m_duration % 3600000) / 60000;
    int ss = (m_duration % 60000) / 1000;
    int ms = m_duration % 1000;
    SPDLOG_INFO("视频分辨率:{}x{} 帧率:{} 总帧数:{}", m_srcWidth, m_srcHeight, m_fps, totalFarame);
    SPDLOG_INFO("时长:{}h:{}m:{}.{}s 总时长:{}ms", hh, mm, ss, ms, m_duration);

    /* 设置视频宽和高的最小大小 */
    this->setMinimumSize(160,90);
    /* 开启定时器刷新 */
    if(m_fps <= 0)
    {
        m_fps = 25;
    }

    /* 开启解码,手动刷新第一帧 */
    m_decodeVedio->startDecodeVedio();
    m_semRefresh->release(2);
    emit signal_refreshImage();
    this->show();
    SPDLOG_DEBUG("打开视频成功:{}", fileName.toStdString());
}

/* 播放视频 */
bool VideoPlayer::play()
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return false;
    }
    if(m_playStatus)
    {
        return false;
    }
    
    /* 设置刷新时间 */
    m_timerRefreshUI.setSingleShot(false);
    m_interval = qRound64(1000.0 / m_fps);
    SPDLOG_DEBUG("刷新UI的定时间隔:{}",m_interval);
    m_timerRefreshUI.start(m_interval);
    m_playStatus = true;
    
    return true;
}


/* 暂停播放 */
void VideoPlayer::pause()
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return;
    }
    if(!m_isLocalFile)
    {
        SPDLOG_ERROR("不是本地视频文件,无法暂停!");
        return;
    }
    if(!m_playStatus)
    {
        return;
    }
    m_timerRefreshUI.stop();
    m_playStatus = false;
}

/* 停止播放,停止后停止解码,将时间等复位到开始时间 */
void VideoPlayer::stop()
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return;
    }
    SPDLOG_DEBUG("...停止播放...");
    m_fileName = QString();
    if(m_timerRefreshUI.isActive())
    {
        m_timerRefreshUI.stop();
    }
    // SPDLOG_DEBUG("...停止解码...");
    /* 删除解码器 */
    delete m_decodeVedio;
    m_decodeVedio = nullptr;
    delete m_threadDecode;
    m_threadDecode = nullptr;
    
    m_playStatus = false;
    m_isOpenFile = false;
    /* 绘制黑帧 */
    SPDLOG_DEBUG("绘制黑帧");
    m_image = new QImage(m_nowWidth, m_nowHeight, QImage::Format_RGB32);
    m_image->fill(Qt::black);
    update();
    
}

/* 后退,单位ms */
void VideoPlayer::backward(qint64 ms)
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return;
    }
    if(!m_isLocalFile)
    {
        SPDLOG_ERROR("不是本地视频文件,无法后退!");
        return;
    }
    /* 获取当前位置 */
    qint64 pos = m_decodeVedio->getCurrentPos();
    pos = pos - ms;
    if(pos < 0)
    {
        pos = 0;
    }

    setCurrentPos(pos);

}

/* 前进,单位ms */
void VideoPlayer::forward(qint64 ms)
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return;
    }
    if(!m_isLocalFile)
    {
        SPDLOG_ERROR("不是本地视频文件,无法前进!");
        return;
    }
    /* 获取当前位置 */
    qint64 pos = m_decodeVedio->getCurrentPos();
    SPDLOG_DEBUG("pos:{} ms:{}", pos, ms);
    pos = pos + ms;
    
    setCurrentPos(pos);

}

/* 获取视频时长 */
qint64 VideoPlayer::getDuration()
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return -1;
    }
    auto duration = m_decodeVedio->getDuration();
    if(duration <= 0)
    {
        return 0;
    }
    return duration;
}

/* 获取当前播放位置 */
qint64 VideoPlayer::getCurrentPos()
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return -1;
    }
    auto pos = m_decodeVedio->getCurrentPos();
    if(pos < 0)
    {
        return 0;
    }
    return pos;
}

/* 设置当前播放位置,单位ms */
void VideoPlayer::setCurrentPos(qint64 pos)
{
    if(!m_isOpenFile)
    {
        SPDLOG_ERROR("未打开视频文件!");
        return;
    }
    if(!m_isLocalFile)
    {
        SPDLOG_ERROR("不是本地视频文件,无法设置播放位置!");
        return;
    }
    if(pos < 0)
    {
        pos = 0;
    }
    /* 先停止播放 */
    bool temp = m_playStatus;
    if(m_playStatus)
    {
        m_timerRefreshUI.stop();
        m_playStatus = false;
    }
    m_decodeVedio->setCurrentPos(pos);
    /* 继续播放 */
    if(temp)
    {
        // SPDLOG_INFO("..........开启定时器..........");
        m_timerRefreshUI.start(m_interval);
        m_playStatus = true;
    }else
    {
        /* 刷新2张照片 */
        m_semRefresh->release(m_previewImage);
        emit signal_refreshImage();
    }
    SPDLOG_INFO("设置播放位置:{} s", m_decodeVedio->getCurrentPos() / 1000.0);
}

/* 设置播放视频大小 */
void VideoPlayer::setPlayWidgetSize(int width,int height)
{
    /* 对宽和高就行缩放,保持比例,同时将其居中放置
     * 先计算出比例,和16/9相对比
     * 大于16/9,以高为最大极限,计算出宽度和x坐标
     * 小于16/9,以宽为最大极限,计算出高度和y坐标 */
    double srcRatio = m_srcWidth*1.0 / m_srcHeight;
    double ratio = width*1.0 / height;
    long w1 = 0, h1 = 0;
    int srcX = this->pos().rx(), srcY = this->pos().ry();
    int x1 = srcX, y1 = srcY;
    if(ratio > srcRatio)
    {
        w1 = height * srcRatio;
        x1 = (width - w1) / 2;
        h1 = height;
        y1 = srcY;
    }
    else if(ratio < srcRatio)
    {
        h1 = width / srcRatio;
        y1 = (height - h1) / 2;
        w1 = width;
        x1 = srcX;
    }else {
        w1 = width;
        h1 = height;
        x1 = srcX;
        y1 = srcY;
    }
    this->move(x1, y1);
    
    m_nowWidth = w1;
    m_nowHeight = h1;
    this->resize(w1, h1);
    // SPDLOG_DEBUG("设置窗口位置:{}x{}, 大小:{}x{}, 传入大小:{}x{}", x1, y1, w1, h1, width, height);
    SPDLOG_DEBUG("现在位置和大小:{}x{}, {}x{}", this->pos().rx(), this->pos().ry(), this->width(), this->height());
}

/**
 * @brief 设置播放窗口,这用于独占一个传入的widget,这里会自动添加一个布局,外面窗口变化,这里也跟随着变化
 * 
 * @param widget 
 * @param flag 
 *  @arg true:独占widget,并设置一个layout,会随着传入的widget大小变化
 *  @arg false:不独占
 */
void VideoPlayer::setPlayWidget(QWidget* widget, bool flag)
{
    if(widget == nullptr)
    {
        SPDLOG_WARN("传入的widget为空");
        return;
    }
    if(flag)
    {
        /* 设置布局 */
        QVBoxLayout* layout = new QVBoxLayout(widget);
        layout->addWidget(this);
        layout->setMargin(0);
        layout->setSpacing(0);
        widget->setLayout(layout);
    }else 
    {
        this->setParent(widget);
        /* 设置窗口大小 */
        setPlayWidgetSize(widget->width(), widget->height());
    }
    
}


/**
 * @brief 设置预览图片数目,在暂停时跳转,可能会有花屏或者黑帧,可以设置跳转图片个数跳过黑帧
 *        默认是2帧
 * 
 * @param num 
 */
void VideoPlayer::setPreviewImage(int num)
{
    m_previewImage = num;
}

/**
 * @brief 设置帧率,有些视频无法获取到帧率,就会使用默认的25fps,如果需要,可以通过这个函数设置
 *        注意:这个函数需要在打开视频文件之后设置,打开一次视频文件会覆盖这个参数
 * 
 * @param fps 
 */
void VideoPlayer::setFPS(int fps)
{
    m_fps = fps;
    if(m_decodeVedio != nullptr)
    {
        m_decodeVedio->setFPS(fps);
    }
    if(m_timerRefreshUI.isActive())
    {
        m_timerRefreshUI.stop();
        m_interval = qRound64(1000.0 / m_fps);
        m_timerRefreshUI.start(m_interval);
    }
}


/* 设置播放回调函数 */
// void VideoPlayer::setPlayCallBack(std::function<Play_CallBack> playCallBack,void* context)
// {
//     m_funcPlayCB = playCallBack;
//     m_context = context;
// }


void VideoPlayer::paintEvent(QPaintEvent *event)
{
    if(m_image != nullptr)
    {
        // SPDLOG_TRACE("开始绘制画面...");
        /* 对图像进行缩放 */
        if(m_srcWidth != m_nowWidth || m_srcHeight != m_nowHeight)
        {
            *m_image = m_image->scaled(m_nowWidth, m_nowHeight, Qt::KeepAspectRatio, Qt::SmoothTransformation);
        }
        QPainter painter(this);
        painter.drawImage(0, 0, *m_image);
    }
}

void VideoPlayer::resizeEvent(QResizeEvent *event)
{
    SPDLOG_TRACE("窗口大小改变...");
    m_nowWidth = event->size().width();
    m_nowHeight = event->size().height();

    QWidget::resizeEvent(event);
}

/* 刷新一张图片,直到有图片为止 */
void VideoPlayer::refreshOneUIUntilHave()
{
    if(m_decodeVedio != nullptr)
    {
        // SPDLOG_DEBUG("取出一帧图片...");
        /* 删除上一帧图片 */
        if(m_image != nullptr)
        {
            delete m_image;
            m_image = nullptr;
        }
        /* 如果没有图片,这个函数会阻塞 */
        m_image = m_decodeVedio->getOneImageUntilHave();
        
        if(m_image)
        {
            if(m_image->isNull())
            {
                SPDLOG_WARN("取出的图片为空...");
                return;
            }
            // SPDLOG_DEBUG("绘制画面...");
            update();
        }
    }
}


/* 双击事件函数 */
// void VideoPlayer::mouseDoubleClickEvent(QMouseEvent *event)
// {
//     if(event->button() == Qt::LeftButton)
//     {
//         // SPDLOG_DEBUG("双击事件...");
//         // if(m_funcPlayCB != nullptr)
//         // {
//         //     m_funcPlayCB(this, 5, nullptr, 0, m_context);
//         // }else {
//         //     SPDLOG_INFO("没有设置回调函数");
//         // }
//     }
// }

/* 取出画面,刷新UI */
void VideoPlayer::do_refreshUI()
{
    if(m_decodeVedio != nullptr)
    {
        // SPDLOG_DEBUG("取出一帧图片...");
        /* 删除上一帧图片 */
        if(m_image != nullptr)
        {
            delete m_image;
            m_image = nullptr;
        }
        m_image = m_decodeVedio->getOneImage();
        
        if(m_image)
        {
            if(m_image->isNull())
            {
                SPDLOG_WARN("取出的图片为空...");
                return;
            }
            // SPDLOG_DEBUG("绘制画面...");
            update();
        }
        // m_decodeVedio->wakeUpCondQueueNoEmpty();
    }
}


/* 通过信号刷新第一张图片 */
void VideoPlayer::do_refreshSamImage()
{
    if(!m_isOpenFile)
    {
        return;
    }
    while(m_semRefresh->tryAcquire(1))
    {
        /* 取出第一张 */
        if(m_decodeVedio != nullptr)
        {
            // SPDLOG_DEBUG("取出一帧图片...");
            /* 删除上一帧图片 */
            if(m_image != nullptr)
            {
                delete m_image;
                m_image = nullptr;
            }
            /* 等待图片,最多等待50ms */
            m_image = m_decodeVedio->getOneImageUntilHave(100);
            if(m_image)
            {
                if(m_image->isNull())
                {
                    SPDLOG_WARN("取出的图片为空...");
                    return;
                }
                SPDLOG_DEBUG("绘制预览画面。");
                update();
            }
        }
    }
}

/* 播放完成 */
void VideoPlayer::do_playCompleted()
{
    SPDLOG_INFO("视频播放完成。");
    m_timerRefreshUI.stop();
    /* 手动刷新剩余的环形队列中的图片 */
    while(true)
    {
        if(m_decodeVedio != nullptr)
        {
            QImage* image = nullptr;
            image = m_decodeVedio->getOneImage();
            if(image == nullptr)
            {
                break;
            }
            /* 删除上一帧图片 */
            if(m_image != nullptr)
            {
                delete m_image;
                m_image = nullptr;
            }
            m_image = image;
            
            if(m_image->isNull())
            {
                SPDLOG_WARN("取出的图片为空...");
                return;
            }
            // SPDLOG_DEBUG("绘制画面...");
            update();
        }
    }
    m_playStatus = false;
    // if(m_funcPlayCB != nullptr)
    // {
    //     /* 播放完成的回调函数 */
    //     m_funcPlayCB(this, 2, nullptr, 0, m_context);
    // }
}

/* 判断是否是本地文件 */
bool VideoPlayer::isLocalFile(const QString& fileName)
{
    if(fileName.isEmpty())
    {
        return false;
    }
    if(fileName.startsWith("http://") || fileName.startsWith("rtsp://")
     || fileName.startsWith("rtmp://") || fileName.startsWith("https://"))
    {
        return false;
    }
    return true;
}