#include "PlayerGLWidget2.h"
#include "spdlog.h"
#include <QFile>
#include <QTextStream>
#include <cstdint>
#include <cstring>
#include <qobject.h>
#include <qopenglext.h>
#include <string.h>
#include <string>



PlayerGLWidget2::PlayerGLWidget2(QWidget *parent) : QOpenGLWidget(parent)
{
    m_vertexCount = 4; // 顶点数量
    m_vertexSize = 8; // 顶点大小
    // m_vertices = new float[m_vertexCount * m_vertexSize] {
    //     /* ----- 位置 -----|-------- 颜色 ----------|----- 纹理坐标 ------ */
    //     -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 0.0f,   0.0f, 0.0f,     // 左下
    //     0.5f, -0.5f, 0.0f,      0.0f, 0.0f, 1.0f,   1.0f, 0.0f,     // 右下
    //     0.5f,  0.5f, 0.0f,      1.0f, 0.0f, 0.0f,   1.0f, 1.0f,     // 右上
    //     -0.5f, 0.5f, 0.0f,      0.0f, 1.0f, 0.0f,   0.0f, 1.0f      // 右上
    // };

    m_vertices = new float[m_vertexCount * m_vertexSize] {
        /* ----- 位置 -----|-------- 颜色 ----------|----- 纹理坐标 ------ */
        -1.0f, -1.0f, 0.0f,     1.0f, 1.0f, 0.0f,   0.0f, 0.0f,     // 左下
        1.0f, -1.0f, 0.0f,      0.0f, 0.0f, 1.0f,   1.0f, 0.0f,     // 右下
        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,   0.0f, 1.0f      // 右上
    };

    m_indexCount = 6; // 索引数量
    m_indices = new uint32_t[m_indexCount] {
        0, 1, 2, // 第一个三角形
        2, 3, 0  // 第二个三角形
    };


    /* 打开着色器代码 */
    QString vertexShaderFile = ":/shader/vertexShader.glsl";
    if (!loadShaderCode(vertexShaderFile, &m_vertexShaderCode)) {
        return;
    }
    // SPDLOG_INFO("顶点着色器代码: \n{}", m_vertexShaderCode);

    QString fragmentShaderFile = ":/shader/fragmentShader.glsl";
    if (!loadShaderCode(fragmentShaderFile, &m_fragmentShaderCode)) {
        return;
    }
    // SPDLOG_INFO("片段着色器代码: \n{}", m_fragmentShaderCode);

    /* 加载问了图片 */
    QString imageFile = ":/Image/image/1.jpg";
    if (!loadImage(imageFile, m_imageTexture)) {
        return;
    }

    QString imageFile2 = ":/Image/image/awesomeface.png";
    if (!loadImage(imageFile2, m_imageTexture2)) {
        return;
    }

}
PlayerGLWidget2::~PlayerGLWidget2()
{
    if(m_vertices != nullptr)
    {
        delete[] m_vertices;
        m_vertices = nullptr;
    }
    if(m_indices != nullptr)
    {
        delete[] m_indices;
        m_indices = nullptr;
    }
    if(m_vertexShaderCode != nullptr)
    {
        delete[] m_vertexShaderCode;
        m_vertexShaderCode = nullptr;
    }
    if(m_fragmentShaderCode != nullptr)
    {
        delete[] m_fragmentShaderCode;
        m_fragmentShaderCode = nullptr;
    }
}

void PlayerGLWidget2::initializeGL()
{
    /* 初始化OpenGL函数 */
    initializeOpenGLFunctions();

    /* -------------------------------------------------------------------------------------
     * 顶点数组对象
     * ------------------------------------------------------------------------------------ */
    glGenVertexArrays(1, &m_VAO1); // 生成一个VAO，返回的ID存储在VAO1中
    glBindVertexArray(m_VAO1); // 绑定VAO

    /* -------------------------------------------------------------------------------------
     * 创建顶点数组缓冲区
     * ------------------------------------------------------------------------------------ */
    glGenBuffers(1, &m_VBO1);
    glBindBuffer(GL_ARRAY_BUFFER, m_VBO1); // 绑定VBO
    glBufferData(GL_ARRAY_BUFFER, m_vertexCount * m_vertexSize * sizeof(float), m_vertices, GL_STATIC_DRAW);

    /* -------------------------------------------------------------------------------------
     * 创建EBO
     * ------------------------------------------------------------------------------------ */
    glGenBuffers(1, &m_EBO1);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO1);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indexCount * sizeof(uint32_t), m_indices, GL_STATIC_DRAW); // 绑定EBO


    /* -------------------------------------------------------------------------------------
     * 链接顶点属性
     * ------------------------------------------------------------------------------------ */
    // 设置顶点属性指针，告诉OpenGL如何解析顶点数据
    glVertexAttribPointer(
                    0,                  /* 顶点属性位置，0表示第一个属性 */
                    3,                  /* 每个顶点属性的大小，这里是3，表示x、y、z坐标 */
                    GL_FLOAT, GL_FALSE, /* 数据类型和是否标准化 */
                    m_vertexSize * sizeof(float), /* 步长，表示每个顶点属性之间的间隔 */
                    (void*)0            /* 偏移量，表示该属性（顶点坐标）从第0个字节开始 */
                ); 
    /* 启用顶点属性 */
    glEnableVertexAttribArray(0);
 
    // 设置颜色位置，最后一个参数表示从第3个字节开始，倒数第二个参数是两个数据之间起始位置间隔8个字节
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, m_vertexSize * sizeof(float), (void*)(3*sizeof(float))); 
    glEnableVertexAttribArray(1);
 
    /* 设置纹理位置 */
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, m_vertexSize * sizeof(float), (void*)(6*sizeof(float))); // 纹理坐标位置
    glEnableVertexAttribArray(2); // 启用纹理坐标属性

    

    


    /* -------------------------------------------------------------------------------------
     * 着色器程序对象
     * ------------------------------------------------------------------------------------ */
    /* 编译顶点着色器 */
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    /* 着色器代码 */
    glShaderSource(vertexShader, 1, &m_vertexShaderCode, nullptr);
    glCompileShader(vertexShader); // 编译着色器
    /* 检查编译错误 */
    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if(success == GL_FALSE)
    {
        glGetShaderInfoLog(vertexShader, sizeof(infoLog), nullptr, infoLog);
        SPDLOG_ERROR("顶点着色器编译错误: {}", infoLog);
    }

    /* 编译片段着色器 */
    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    /* 加载着色器代码 */
    glShaderSource(fragmentShader, 1, &m_fragmentShaderCode, nullptr);
    glCompileShader(fragmentShader); // 编译着色器
    /* 检查编译错误 */
    success = 0;
    memset(infoLog, 0, sizeof(infoLog)); // 清空infoLog
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if(success == GL_FALSE)
    {
        glGetShaderInfoLog(fragmentShader, sizeof(infoLog), nullptr, infoLog);
        SPDLOG_ERROR("片段着色器编译错误: {}", infoLog);
    }

    /* 创建一个着色器程序对象 */
    m_shaderProgram = glCreateProgram();
    /* 将顶点着色器和片段着色器附加到着色器程序对象上 */
    glAttachShader(m_shaderProgram, vertexShader);
    glAttachShader(m_shaderProgram, fragmentShader);
    /* 链接着色器程序 */
    glLinkProgram(m_shaderProgram);
    /* 检查链接错误 */
    success = 0;
    memset(infoLog, 0, sizeof(infoLog)); // 清空infoLog
    glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &success);
    if(success == GL_FALSE)
    {
        glGetProgramInfoLog(m_shaderProgram, sizeof(infoLog), nullptr, infoLog);
        SPDLOG_ERROR("着色器链接错误: {}", infoLog);
    }
    /* 删除着色器对象 */
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    /* 解绑VAO */
    // glBindVertexArray(0); // 解绑VAO
    /* 解绑VBO */
    glBindBuffer(GL_ARRAY_BUFFER, 0); // 解绑VBO
    /* 解绑EBO */
    // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // 解绑EBO
    /* 解绑纹理对象 */
    // glBindTexture(GL_TEXTURE_2D, 0); // 解绑纹理对象
    /* 解绑着色器程序对象 */
    // glUseProgram(0); // 解绑着色器程序对象

    /* 使用着色器程序对象 */
    glUseProgram(m_shaderProgram);

    /* -------------------------------------------------------------------------------------
     * 纹理对象
     * ------------------------------------------------------------------------------------ */
    glGenTextures(1, &m_texture1); // 生成纹理对象
    glBindTexture(GL_TEXTURE_2D, m_texture1); // 绑定纹理对象
    /* 设置当前绑定的纹理对象的环绕、过滤方式 */
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // S轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // T轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // 纹理缩小过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理放大过滤方式

    /* 纹理数据，最后一个参数是纹理数据的指针，0表示数据在当前上下文中 */
    /* 纹理数据，最后一个参数是纹理数据的指针 */
    glTexImage2D(
        GL_TEXTURE_2D,
        0,                          // Mipmap 级别
        GL_RGBA,                    // 纹理内部格式
        m_imageTexture.width(),     // 纹理宽度
        m_imageTexture.height(),    // 纹理高度
        0,                          // 边框（必须为 0）
        GL_RGBA,                    // 数据格式（与 QImage 格式匹配）
        GL_UNSIGNED_BYTE,           // 数据类型
        m_imageTexture.bits()       // 数据指针
    );
    glGenerateMipmap(GL_TEXTURE_2D); // 生成多级渐远纹理

    glGenTextures(1, &m_texture2); // 生成纹理对象
    glBindTexture(GL_TEXTURE_2D, m_texture2); // 绑定纹理对象
    /* 设置当前绑定的纹理对象的环绕、过滤方式 */
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // S轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // T轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // 纹理缩小过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 纹理放大过滤方式

    /* 纹理数据，最后一个参数是纹理数据的指针，0表示数据在当前上下文中 */
    /* 纹理数据，最后一个参数是纹理数据的指针 */
    glTexImage2D(
        GL_TEXTURE_2D,
        0,                          // Mipmap 级别
        GL_RGBA,                    // 纹理内部格式
        m_imageTexture2.width(),     // 纹理宽度
        m_imageTexture2.height(),    // 纹理高度
        0,                          // 边框（必须为 0）
        GL_RGBA,                    // 数据格式（与 QImage 格式匹配）
        GL_UNSIGNED_BYTE,           // 数据类型
        m_imageTexture2.bits()       // 数据指针
    );
    glGenerateMipmap(GL_TEXTURE_2D); // 生成多级渐远纹理
    
}

void PlayerGLWidget2::resizeGL(int w, int h)
{
    glViewport(0, 0, w, h); // 设置视口大小
}

void PlayerGLWidget2::paintGL()
{
    glClearColor(0.1f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    /* 激活着色器程序对象 */
    glUseProgram(m_shaderProgram);
    /* 设置纹理单元，0表示第一个纹理单元，1表示第二个纹理单元 */
    glUniform1i(glGetUniformLocation(m_shaderProgram, "ourTexture1"), 0); // 纹理单元0
    glUniform1i(glGetUniformLocation(m_shaderProgram, "ourTexture2"), 1); // 纹理单元1
    /* 激活纹理单元 */
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_texture1); // 绑定纹理对象
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, m_texture2); // 绑定纹理对象
    /* 绑定VAO，绘制图形 */
    glBindVertexArray(m_VAO1); // 绑定VAO

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // 绘制四边形

    glBindVertexArray(0); // 解绑VAO

}


/**
 * @brief 打开GLSL文件，读取着色器代码
 * 
 * @param fileName 
 * @param shaderCode 取出的着色器代码，函数内部会分配内存，需要在使用完后释放
 * @return true 
 * @return false 
 */
bool PlayerGLWidget2::loadShaderCode(const QString &fileName, char** shaderCode)
{
    if(*shaderCode != nullptr) {
        delete[] *shaderCode;
        *shaderCode = nullptr;
    }
    /* 打开文件 */
    QFile file(fileName);
    if (!file.open(QIODevice::ReadOnly)) {
        SPDLOG_ERROR("Failed to open shader file: {}", fileName.toStdString());
        return false;
    }
    QByteArray ba = file.readAll();
    file.close();
    // SPDLOG_DEBUG("Shader file content: \n{}", ba.toStdString());

    if (ba.isEmpty()) {
        SPDLOG_ERROR("Shader file is empty: {}", fileName.toStdString());
        return false;
    }
    /* 计算字符串长度 */
    int len = ba.length() + 1; // +1是为了'\0'结尾
    /* 分配内存 */
    *shaderCode = new char[len];
    /* 复制字符串 */
    // strcpy_s(*shaderCode, len, ba.toStdString().c_str());
    std::strncpy(*shaderCode, ba.constData(), len);

    return true;
}

/* 加载图片 */
bool PlayerGLWidget2::loadImage(const QString &fileName, QImage &imageTexture)
{
    imageTexture.load(fileName);
    if (imageTexture.isNull()) {
        SPDLOG_ERROR("Failed to load image: {}", fileName.toStdString());
        return false;
    }
    /* 翻转图片，OpenGL的坐标系和Qt的坐标系是相反的，所以需要翻转图片 */
    // imageTexture = imageTexture.mirrored(false, true);
    // if (imageTexture.isNull()) {
    //     SPDLOG_ERROR("Failed to flip image: {}", fileName.toStdString());
    //     return false;
    // }
    /* 将图片转换为RGBA格式 */
    imageTexture = imageTexture.convertToFormat(QImage::Format_RGBA8888);
    if (imageTexture.isNull()) {
        SPDLOG_ERROR("Failed to convert image to RGBA format: {}", fileName.toStdString());
        return false;
    }
    
    return true;
}
