#include "ScreenWidget.h"

#include "MediaPlayer.h"

#include "FileHelper.h"

#include <QGridLayout>
#include <QLabel>
#include <QMessageBox>
#include <QPixmap>
#include <QParallelAnimationGroup>
#include <QApplication>
#include <QPropertyAnimation>
#include <QOpenGLPixelTransferOptions>
#include <QRunnable>
#include <QSettings>
#include <QPainter>
#include <QThreadPool>
#include <QDir>

#include <QGridLayout>

#include "MainWindow.h"
#include "Config.h"
#include "ShapeWidget.h"
#include "VideoFrameDisplay.h"

QMap<int, QPoint> g_shapeOffsetPoint = {
    {1, {-15,-50}},
    {2, {100, -35}},
    {3, {95, -95}},
    {4, {100, 0}},
    {5, {0, -95}}
};


class LoadImageTask : public QObject, public QRunnable {
public:
    using HandleResoult = std::function<void(int index, QImage image)>;
public:
    LoadImageTask(int index, const QVector<QString>& texturePaths, HandleResoult handle, QObject* parent = nullptr)
        : QObject(parent)
        , m_index(index)
        , m_texturePaths(texturePaths)
        , m_handle(std::move(handle)) {
        setAutoDelete(true);
    }

    void run() override {
        const QString& path = m_texturePaths[m_index];
        QPixmap px(path);
        QImage image = px.toImage().mirrored();
        m_handle(m_index, std::move(image));
    }

private:
    int m_index{ 0 };
    const QVector<QString>& m_texturePaths;
    HandleResoult m_handle;
};

ScreenWidget::ScreenWidget( QWidget* widget)
	:QOpenGLWidget(widget) {
    //setStyleSheet("background-color: rgb(0, 0, 0); background:transparent;");
    makeCurrent();
    setWindowFlags(Qt::FramelessWindowHint);

    const QString configPath = QApplication::applicationDirPath() + "/ProjectDisplay/project.ini";
    QSettings setting(configPath, QSettings::IniFormat);

    m_x = setting.value("x", 0).toInt();
    m_y = setting.value("y", 0).toInt();
    m_sx = setting.value("sx", 1.0f).toFloat();
    m_sy = setting.value("sy", 1.0f).toFloat();
    m_r = setting.value("r", 0).toInt();
    QString bg = setting.value("bg", 0).toString();

    const QString path = QApplication::applicationDirPath() + "/ProjectDisplay/" + bg;
    m_bg.load(path);


    connect(&vlcMediaPlayer_, &VlcMediaPlayer::VideoDataOutput, this, &ScreenWidget::slotSetOneFrame, Qt::QueuedConnection);
    m_maskImage = QImage(m_bg.width(), m_bg.height(), QImage::Format_RGBA8888);
    m_maskImage.fill(QColor(0, 0, 0, 0));
    InitMask();
    //connect(&vlcMediaPlayer_, &VlcMediaPlayer::VideoDataOutput, Display, &VideoFrameDisplay::slotSetOneFrame, Qt::QueuedConnection);
}

ScreenWidget::~ScreenWidget() {
    makeCurrent();
    if (vlcMediaPlayer_.IsPlaying()) {
        vlcMediaPlayer_.Stop();
    }
}

void ScreenWidget::OnMessage(const QString& cmd) {
    const QStringList params = cmd.split("|");
    if (params.count() < 3) {
        qWarning() << "invalid cmd:" << cmd;
        return;
    }

    makeCurrent();

    m_maskImage.fill(QColor(0, 0, 0, 0));
    QOpenGLPixelTransferOptions uploadOptions;
    uploadOptions.setAlignment(1);

    m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, m_maskImage.constBits(), &uploadOptions);
    m_town = params[0].toInt();
    m_place = params[1].toInt();

    const QString townName = MainWindow::Get().GetTownName(m_town);
    m_scope = MainWindow::Get().GetScope(m_town, m_place);

    if (vlcMediaPlayer_.IsPlaying()) {
        vlcMediaPlayer_.Stop();
    }

    m_cricleFrame = 0;
    int wh = qMin(m_scope.width, m_scope.height);
    m_moveRect = QRect(m_scope.x, m_scope.y, wh, wh);
    m_drawRect = QRect(m_moveRect.x(), m_bg.height() - m_moveRect.y() - m_moveRect.height(), m_moveRect.width(), m_moveRect.height());

    m_lastTime = QTime::currentTime();

   // PlayCricle(m_scope);
    //Play(scop);
}

void ScreenWidget::keyPressEvent(QKeyEvent* event) {
#if 0
    if (event->key() == Qt::Key_Control) {
        m_ctrlPress = true;
    }

    if (Qt::ControlModifier & event->modifiers()) {
        if (event->key() == Qt::Key_Up) {
            QRect rect = Display->geometry();
            int w = rect.width();
            rect.setWidth(w + 10);
            rect.setHeight(w + 10);
            Display->setGeometry(rect);
        }
        else if (event->key() == Qt::Key_Down) {
            QRect rect = Display->geometry();
            int w = rect.width();
            rect.setWidth(w - 10);
            rect.setHeight(w - 10);
            Display->setGeometry(rect);
        }
       /* else if (event->key() == Qt::Key_Left) {
            Display->Rotate(-5);
        }
        else if (event->key() == Qt::Key_Right) {
            Display->Rotate(5);
        }*/
    /*    else if (event->key() == Qt::Key_PageUp || event->key() == Qt::Key_J) {
            m_shapeWidget->Shape(1);
        }
        else if (event->key() == Qt::Key_PageDown || event->key() == Qt::Key_K) {
            m_shapeWidget->Shape(-1);
        }
        else if (event->key() == Qt::Key_S) {
            Save();
        }*/
    }

    if (Qt::ShiftModifier & event->modifiers()) {
        if (event->key() == Qt::Key_Left) {
            QRect rect = Display->geometry();
            rect.translate( -5, 0);
            Display->setGeometry(rect);
        }
        else if (event->key() == Qt::Key_Right) {
            QRect rect = Display->geometry();
            rect.translate(5, 0);
            Display->setGeometry(rect);
        } else if (event->key() == Qt::Key_Up) {
            QRect rect = Display->geometry();
            rect.translate(0, -5);
            Display->setGeometry(rect);
        }
        else if (event->key() == Qt::Key_Down) {
            QRect rect = Display->geometry();
            rect.translate(0, 5);
            Display->setGeometry(rect);
        }
        else if (event->key() == Qt::Key_W) {
            m_sy *= 1.1;
        }
        else if (event->key() == Qt::Key_S) {
            m_sy *= 0.9;
        }
        else if (event->key() == Qt::Key_A) {
            m_sx *= 1.1;
        }
        else if (event->key() == Qt::Key_D) {
            m_sx *= 0.9;
        }
        else if (event->key() == Qt::Key_R) {
            m_r++;
        }
        else if (event->key() == Qt::Key_T) {
            m_r--;
        }
      /*  else if (event->key() == Qt::Key_S) {
            const QString configPath = QApplication::applicationDirPath() + "/ProjectDisplay/project.ini";
            QSettings setting(configPath, QSettings::IniFormat);
            setting.setValue("x", m_x);
            setting.setValue("y", m_y);
            setting.setValue("sx", m_sx);
            setting.setValue("sy", m_sy);
            setting.setValue("r", m_r);
            setting.sync();
        }*/
        update();
    }
#endif
}

void ScreenWidget::keyReleaseEvent(QKeyEvent* event) {
    m_ctrlPress = false;
}

void ScreenWidget::mouseReleaseEvent(QMouseEvent* event) {
#if 0
    if (m_ctrlPress && event->button() == Qt::LeftButton) {
        QRect rect = Display->geometry();
        Display->move(event->pos() - QPoint(rect.width(), rect.height()) / 2);
    }
#endif
}

void ScreenWidget::initializeGL() {
    makeCurrent();
    initializeOpenGLFunctions();

    static float vertices[] = {
        // 位置               // 颜色              // 纹理坐标
        1.0f,  1.0f, 0.0f, 1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // 右上
        1.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // 右下
       -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // 左下
       -1.0f,  1.0f, 0.0f, 1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // 左上
    };
    static unsigned int indices[] = {
        // 注意索引从0开始!
        // 此例的索引(0,1,2,3)就是顶点数组vertices的下标,
        // 这样可以由下标代表顶点组合成矩形
        0, 1, 3, // 第一个三角形
        1, 2, 3  // 第二个三角形
    };


    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);// 设置清屏颜色
    glDisable(GL_DEPTH_TEST);    // 启用深度测试
    glEnable(GL_COLOR_BUFFER_BIT);  // 启用颜色缓冲区

    const QString shaderDir = QApplication::applicationDirPath() + "/ProjectDisplay/shader/";

    // 创建顶点着色器对象,并编译
    m_vshader = new QOpenGLShader(QOpenGLShader::Vertex);
    bool success = m_vshader->compileSourceFile(shaderDir + "vertex_shader.vs");
    if (!success) {
        const QString log = m_vshader->log();
        qDebug() << log;
    }
    // 创建片段着色器对象,并编译
    m_fshader = new QOpenGLShader(QOpenGLShader::Fragment);
    success = m_fshader->compileSourceFile(shaderDir + "screen_fragment_shader.fs");
    if (!success) {
        const QString log = m_fshader->log();
        qDebug() << log;

    }
    // 创建着色器程序对象,添加顶点着色器和片段着色器,并链接它们
    m_program = new QOpenGLShaderProgram;
    m_program->addShader(m_vshader);
    m_program->addShader(m_fshader);
    m_program->link();
    // 创建顶点数组对象,并绑定到当前上下文
    m_vao = new QOpenGLVertexArrayObject;
    m_vao->create();
    QOpenGLVertexArrayObject::Binder vaoBinder(m_vao);
    // 创建顶点缓冲对象,
    QOpenGLBuffer* vbo = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    vbo->create();
    vbo->bind();
    // 分配显存大小,并搬运至显存
    vbo->allocate(vertices, sizeof(vertices));

    // 创建元素缓冲对象,
    QOpenGLBuffer* ebo = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
    ebo->create();
    ebo->bind();
    // 分配显存大小,并搬运至显存
    ebo->allocate(indices, sizeof(indices));
    // 链接顶点属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), 0);
    // 顶点属性默认是禁用的,启用顶点属性0(location=0)
    m_program->enableAttributeArray(0);
    // 颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    m_program->enableAttributeArray(1);
    // 纹理坐标属性
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    m_program->enableAttributeArray(2);


    m_vao->release();
    vbo->release();

    m_program->bind();  //在修改uniform值之前,一定要绑定着色器程序到当前激活的opengl上下文
    m_program->setUniformValue("bgMap", 0);
    m_program->setUniformValue("moiveMap", 1);

    //connect(&m_timer, &QTimer::timeout, this, &ScreenWidget::OnTimeout);
    //m_timer.start(40);

    //QPixmap px(m_backgourndTexturePaths[m_backgroundCurrentFrame]);
    m_background = new QOpenGLTexture(QOpenGLTexture::Target2D);
    m_background->setAutoMipMapGenerationEnabled(false);
    m_background->setFormat(QOpenGLTexture::RGBA8_UNorm);
    m_background->setSize(m_bg.width(), m_bg.height());
    m_background->setMipLevels(1);
    m_background->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
    // 设置纹理环绕方式
    m_background->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
    m_background->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);

    QImage image = m_bg.toImage().mirrored();
    image = image.convertToFormat(QImage::Format_RGBA8888);
    QOpenGLPixelTransferOptions uploadOptions;
    uploadOptions.setAlignment(1);
    m_background->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits(), &uploadOptions);

    m_movie = new QOpenGLTexture(QOpenGLTexture::Target2D/*px.toImage().mirrored()*/);
    m_movie->setAutoMipMapGenerationEnabled(false);
    m_movie->setFormat(QOpenGLTexture::RGBA8_UNorm);
    m_movie->setSize(m_bg.width(), m_bg.height());
    m_movie->setMipLevels(1);
    m_movie->allocateStorage(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8);
    // 设置纹理环绕方式
    m_movie->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
    m_movie->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);

    QImage dest(m_bg.width(), m_bg.height(), QImage::Format_RGBA8888);
    dest.fill(QColor(0, 0, 0, 0));
    m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, dest.constBits(), &uploadOptions);

}

void ScreenWidget::paintGL() {
    makeCurrent();
    glClearColor(0.f, 0.f, 0.f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

   
    QMatrix4x4 projection;

    QMatrix4x4 view;
    view.translate(0.0f, 0.0f, -1.0000f);

    QMatrix4x4 model;

    m_background->bind(0);
    m_movie->bind(1);

    // 绑定着色器程序至当前上下文,相当于调用glUseProgram()
    m_program->bind();
    m_program->setUniformValue("view", view);
    m_program->setUniformValue("projection", projection);
    m_program->setUniformValue("model", model);
    m_program->setUniformValue("alpha", m_alpha);


    // 绑定VAO,调用设置的一组状态
    QOpenGLVertexArrayObject::Binder vaoBinder(m_vao);
    // 绘制三角形
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    m_program->release();
    // 交换缓存
    auto* context = QOpenGLContext::currentContext();
    context->swapBuffers(context->surface());
}

void ScreenWidget::resizeGL(int w, int h) {
    makeCurrent();
    glViewport(0, 0, w, h);

    update();
}

void ScreenWidget::Save() {
#if 0
    if (-1 == m_town || -1 == m_place) {
        qDebug() << __FUNCTION__ << m_place << ", m_town:" << m_town;
        return;
    }
    Scope scop = MainWindow::Get().GetScope(m_town, m_place);
    QRect rect = Display->geometry();
    scop.x = rect.x();
    scop.y = rect.y();
    scop.width = rect.width();
    scop.height = rect.height();
    scop.rotate = m_shapeWidget->GetRotate();
    //scop.shape = 1;// m_shapeWidget->GetShapeCode();

    MainWindow::Get().SetScope(m_town, m_place, scop);
    MainWindow::Get().SaveConfig();
#endif
}
//
//QPoint readPointFromFile() {
//    QFile file("D:/1.txt");
//    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
//        qWarning() << "Cannot open file:" << file.errorString();
//        return QPoint(); // 返回默认构造的QPoint,即(0, 0)
//    }
//
//    QTextStream in(&file);
//    QString line = in.readLine(); // 读取文件的第一行
//
//    bool okX, okY;
//    int x = line.section(',', 0, 0).trimmed().toInt(&okX);
//    int y = line.section(',', 1, 1).trimmed().toInt(&okY);
//
//    if (!okX || !okY) {
//        qWarning() << "Invalid data in file:";
//        return QPoint(); // 数据无效,返回默认QPoint
//    }
//
//    file.close();
//    return QPoint(x, y);
//}

void ScreenWidget::PlayCricle(const Scope& scop) {
    if (m_cricleFrame >= m_cricleFrameCount - 1) {
        if (m_cricleFrame == m_cricleFrameCount - 1) {
            Play(scop);
            ++m_cricleFrame;
        }
        return;
    }

    QTime time = QTime::currentTime();
    int elapsed = m_lastTime.msecsTo(time);
    if (elapsed <= MainWindow::Get().GetCrilceDelay()) {
        return;
    }
    m_lastTime = time;
 
    QRect rect = QRect(-m_bg.width(), -m_bg.height(), m_bg.width() * 2, m_bg.height() * 2);
    rect.moveCenter(m_drawRect.center() +g_shapeOffsetPoint[scop.shape]);

    makeCurrent();

    m_maskImage.fill(QColor(0, 0, 0, 0));
    QPainter painter(&m_maskImage);
    const QImage& maskImage = m_textureData[m_cricleFrame++ % m_cricleFrameCount];
    painter.drawImage(rect, maskImage);

    painter.end();

    QOpenGLPixelTransferOptions uploadOptions;
    uploadOptions.setAlignment(1);

    m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, m_maskImage.constBits(), &uploadOptions);
}

void ScreenWidget::Play(const Scope& scop) {
    const QString moviePath = QApplication::applicationDirPath() + "/ProjectDisplay/movie/" + QString::number(scop.shape) + ".mov";
    vlcMediaPlayer_.Play(moviePath);
}

void ScreenWidget::slotSetOneFrame(QImage image) {
    makeCurrent();

    m_maskImage.fill(QColor(0, 0, 0, 0));
    QPainter painter(&m_maskImage);

    image = image.mirrored();

    QImage maskImage = m_textureData[m_cricleFrame++ % m_cricleFrameCount];
    painter.drawImage(m_drawRect, image);
    painter.end();

    QOpenGLPixelTransferOptions uploadOptions;
    uploadOptions.setAlignment(1);

    m_movie->setData(0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, m_maskImage.constBits(), &uploadOptions);
}

void ScreenWidget::InitMask() {
    const QString imageDir = QApplication::applicationDirPath() + "/ProjectDisplay/circle";

    QStringList jpgFiles;

    QDir dir(imageDir);
    QStringList filters;
    filters << "*.png";
    QFileInfoList fileInfoList = dir.entryInfoList(filters, QDir::Files);

    m_cricleFrameCount = fileInfoList.size();
    m_textureData.resize(fileInfoList.size());

    QVector<QString> paths;
    foreach(const QFileInfo & fileInfo, fileInfoList) {
        paths.append(fileInfo.absoluteFilePath());
    }


    auto handle = [&](int index, QImage image) {
        m_textureData[index] = std::move(image);
        };

    QThreadPool pool;
    pool.setMaxThreadCount(std::thread::hardware_concurrency());
    for (int i = 0; i < paths.count(); ++i) {
        LoadImageTask* task = new LoadImageTask(i, paths, handle);
        pool.start(task);
    }

    pool.waitForDone();
}

void ScreenWidget::OnTimeout() {
    PlayCricle(m_scope);
    update();
}