#include "ShaderBase.h"

#include <QString>
#include <QImage>
#include <QFile>

#include <QOpenGLWidget>
#include <QOpenGLFunctions_3_3_Core>
#include <QTextStream>

#include "spdlog/spdlog.h"


ShaderBase::ShaderBase()
{
}

ShaderBase::~ShaderBase()
{
    clearTexture();
    
    if(m_shaderProgramObj != nullptr) {
        delete m_shaderProgramObj;
        m_shaderProgramObj = nullptr;
    }
}


/* 初始化Shader，第一个是定点着色器，第二个是片段着色器 */
bool ShaderBase::loadShaderCode(const QString &vertexShaderFile, const QString &fragmentShaderFile)
{
    if(vertexShaderFile.isEmpty() || fragmentShaderFile.isEmpty()) 
    {
        SPDLOG_ERROR("顶点着色器或片段着色器文件为空");
        return false;
    }
    /* 打开文件 */
    QFile vertexFile(vertexShaderFile);
    if (!vertexFile.open(QIODevice::ReadOnly)) 
    {
        SPDLOG_ERROR("Failed to open vertex shader file: {}", vertexShaderFile.toStdString());
        return false;
    }
    m_vertexShaderCode = vertexFile.readAll();
    vertexFile.close();

    if (m_vertexShaderCode.isEmpty()) 
    {
        SPDLOG_ERROR("Vertex shader file is empty: {}", vertexShaderFile.toStdString());
        return false;
    }
    /* 打开文件 */
    QFile fragmentFile(fragmentShaderFile);
    if (!fragmentFile.open(QIODevice::ReadOnly)) 
    {
        SPDLOG_ERROR("Failed to open fragment shader file: {}", fragmentShaderFile.toStdString());
        return false;
    }
    m_fragmentShaderCode = fragmentFile.readAll();
    fragmentFile.close();
    if (m_fragmentShaderCode.isEmpty()) 
    {
        SPDLOG_ERROR("Fragment shader file is empty: {}", fragmentShaderFile.toStdString());
        return false;
    }

    return true;
}


/* 编译着色器程序 */
bool ShaderBase::compileShader()
{
    if(m_vertexShaderCode.isEmpty() || m_fragmentShaderCode.isEmpty()) {
        SPDLOG_ERROR("顶点着色器或片段着色器文件为空");
        return false;
    }
    /* 编译顶点着色器 */
    QOpenGLShader* vertexShader = new QOpenGLShader(QOpenGLShader::Vertex);
    if(!vertexShader->compileSourceCode(m_vertexShaderCode))
    {
        SPDLOG_ERROR("编译顶点着色器错误: {}", vertexShader->log().toStdString());
        return false;
    }
    /* 编译片段着色器 */
    QOpenGLShader* fragmentShader = new QOpenGLShader(QOpenGLShader::Fragment);
    if(!fragmentShader->compileSourceCode(m_fragmentShaderCode))
    {
        SPDLOG_ERROR("编译片段着色器错误: {}", fragmentShader->log().toStdString());
        return false;
    }

    /* 创建着色器程序对象 */
    m_shaderProgramObj = new QOpenGLShaderProgram();
    /* 添加顶点着色器和片段着色器 */
    m_shaderProgramObj->addShader(vertexShader);
    m_shaderProgramObj->addShader(fragmentShader);
    /* 链接着色器程序 */
    if (!m_shaderProgramObj->link()) {
        SPDLOG_ERROR("Failed to link shader program: {}", m_shaderProgramObj->log().toStdString());
        return false;
    }
    /* 检查链接错误 */
    if (m_shaderProgramObj->log().contains("ERROR")) {
        SPDLOG_ERROR("Shader program link error: {}", m_shaderProgramObj->log().toStdString());
        return false;
    }
    /* 删除着色器对象 */
    delete vertexShader;
    vertexShader = nullptr;
    delete fragmentShader;
    fragmentShader = nullptr;

    return true;
}

/**
 * @brief 创建纹理，可以显式的指定纹理单元编号，返回纹理单元编号，创建失败则返回的是
 * 
 * @param uniformName 
 * @param textureUnit 如果不指定或者小于0，则会自动分配纹理单元编号
 * @return int -1，创建失败，>0，创建成功
 */
int ShaderBase::createTexture(const QString uniformName, int textureUnit)
{
    GLint maxTextureUnits = 0;
    glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
    if(textureUnit > maxTextureUnits) 
    {
        SPDLOG_ERROR("纹理单元编号:{}已超过支持的最大单元数目:{}", textureUnit, maxTextureUnits);
        return -1;
    }
    /* 制定了单元编号，先查重 */
    int texUnit = textureUnit;
    if(texUnit >= 0)
    {
        for(auto it = m_mapTexture.begin(); it != m_mapTexture.end(); ++it) 
        {
            if(it.key() == texUnit) 
            {
                if(it->first == uniformName) 
                {
                    SPDLOG_WARN("纹理单元编号:{}，名称:{} 已存在", texUnit, uniformName.toStdString());
                    return texUnit;
                } else 
                {
                    /* 删除该纹理单元 */
                    SPDLOG_WARN("纹理单元编号:{}，名称:{} 已存在，将被替换为名称:{}", texUnit, it->first.toStdString(), uniformName.toStdString());
                    if(it->second != nullptr) 
                    {
                        it->second->destroy();
                        delete it->second;
                        it->second = nullptr;
                    }
                    m_mapTexture.erase(it); // 删除该纹理单元
                    break; // 退出循环
                }
            }
        }
    }else 
    {
        /* 没有分配编号，手动分配 */
        texUnit = 0;
        for(int i = 0; i < maxTextureUnits; i++)
        {
            if(!m_mapTexture.contains(i))
            {
                texUnit = i;
                break;
            }
        }
    }

    QOpenGLTexture* texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
    /* 设置纹理参数 */
    texture->setWrapMode(QOpenGLTexture::Repeat); // S轴环绕方式
    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); // 纹理缩小过滤方式：三线性过滤
    texture->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
    

    printGLerror("ShaderBase::createTextureFile");
    /* 将纹理存储到数组中 */
    m_mapTexture.insert(texUnit, QPair<QString, QOpenGLTexture*>(uniformName, texture));

    return texUnit;
}

// /* 初始化纹理，直接传入图片 */
// bool ShaderBase::createTexture(const QImage &image, const QString uniformName)
// {
//     if(image.isNull()) {
//         SPDLOG_WARN("Texture Image is null");
//         return false;
//     }
//     QOpenGLTexture* texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
//     texture->setSize(image.width(), image.height(), 1); // 设置纹理大小
//     texture->setFormat(QOpenGLTexture::RGBA8_UNorm); // 设置纹理格式
//     texture->allocateStorage(); // 分配纹理存储空间
//     texture->setData(QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.convertToFormat(QImage::Format_RGBA8888).bits()); // 设置纹理数据
//     /* 设置纹理参数 */
//     texture->setWrapMode(QOpenGLTexture::Repeat); // S轴环绕方式
//     texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear); // 纹理缩小过滤方式
//     texture->setMagnificationFilter(QOpenGLTexture::Linear); // 纹理放大过滤方式
    
    
//     texture->generateMipMaps(); // 生成多级渐远纹理

//     printGLerror("ShaderBase::createTextureImage");
//     /* 将纹理存储到数组中 */
//     m_listTexture.push_back(QPair<QString, QOpenGLTexture*>(uniformName, texture));

//     return true;
// }

/* 清空纹理 */
void ShaderBase::clearTexture()
{
    for(auto& it : m_mapTexture) 
    {
        if(it.second != nullptr) 
        {
            it.second->destroy();
            delete it.second;
            it.second = nullptr;
        }
    }
    m_mapTexture.clear();
}

/* 使用着色器 */
void ShaderBase::useShader()
{
    if (m_shaderProgramObj == nullptr) {
        SPDLOG_ERROR("Shader program object is null");
        return;
    }
    /* 使用着色器程序 */
    m_shaderProgramObj->bind();
    /* 使用纹理 */
    if(m_mapTexture.size() > 0) 
    {
        for (auto it = m_mapTexture.begin(); it != m_mapTexture.end(); ++it) 
        {
            it->second->bind(it.key());
            /* 绑定纹理 */
            int location = m_shaderProgramObj->uniformLocation(it->first);
            if (location < 0) {
                SPDLOG_ERROR("Failed to get uniform location for texture: {}", it->first.toStdString());
                continue;
            }
            /* 设置纹理单元 */
            m_shaderProgramObj->setUniformValue(location, it.key()); 
        }
    }
}

/* 打印OpenGL错误 */
void ShaderBase::printGLerror(const std::string functionName)
{
    GLenum error = glGetError();
    if (error == GL_NO_ERROR) {
        return;
    }

    /* 打印出字符串 */
    std::string errorString;
    switch (error) 
    {
        case GL_INVALID_ENUM:
            errorString = "GL_INVALID_ENUM";
            break;
        case GL_INVALID_VALUE:
            errorString = "GL_INVALID_VALUE";
            break;
        case GL_INVALID_OPERATION:
            errorString = "GL_INVALID_OPERATION";
            break;
        case GL_STACK_OVERFLOW:
            SPDLOG_ERROR("GL_STACK_OVERFLOW");
            break;
        case GL_STACK_UNDERFLOW:
            SPDLOG_ERROR("GL_STACK_UNDERFLOW");
            break;
        case GL_OUT_OF_MEMORY:
            SPDLOG_ERROR("GL_OUT_OF_MEMORY");
            break;
        default:
            SPDLOG_ERROR("Unknown OpenGL error: 0x{:X}", error);
            break;
    }
    SPDLOG_ERROR("OpenGL error in {}: 0x{:X}, {}", functionName, error, errorString);
    
}

