#include "desktopbackground.h"

#include <QSGSimpleRectNode>
#include <QPainter>
#include <QtMath>
#include <QGSettings>
#include <QElapsedTimer>

//! 默认的最大虚化程度
#define MAX_BLUR_ALPHA  20
//! 背景文件获取失败时，默认加载的背景
#define DEFAULT_BACKGROUND   ":/images/default-background.jpg"

const QString backgroundSchemaUrl = "org.mate.background";
const QString fillStyleKeyName   = "pictureOptions";

QImage DesktopBackground::m_blurBackground;
QImage DesktopBackground::m_nonBlurBackground;
quint32 DesktopBackground::m_blurRadius = 0;

DesktopBackground::DesktopBackground(QQuickPaintedItem *parent)
    : QQuickPaintedItem(parent)
    , m_blurAlpha(0)
{
    QElapsedTimer timer;
    timer.start();
    setFlag(ItemHasContents);
    init();
    qDebug() << "The DesktopBackground Constructor took" << timer.elapsed() << "milliseconds";
}

DesktopBackground::~DesktopBackground()
{
    if (!m_blurBackground.isNull())
        m_blurBackground = QImage();
    if (!m_nonBlurBackground.isNull())
        m_nonBlurBackground = QImage();
    m_blurRadius = 0;
}

void DesktopBackground::init()
{
    connect(this, &DesktopBackground::blurAlphaChanged, this, &DesktopBackground::onBlurAlphaChanged);

    m_backgroundFile = getBackgroundFile();
    if(m_backgroundFile == ""){
        qWarning() << "未获取到背景图片!";
        m_backgroundFile = DEFAULT_BACKGROUND;
    }
    m_backgroundFillStyle = getBackgroundFillStyle();
    m_scalingFactor = getSystemScalingFactor();

    if (m_nonBlurBackground.isNull()) {
        QImage image(m_backgroundFile);
        m_nonBlurBackground = image;
        if (m_backgroundFillStyle == "centered" || m_backgroundFillStyle == "wallpaper") {
            // 居中 或 平铺
            m_nonBlurBackground = m_nonBlurBackground.scaled(m_nonBlurBackground.width() * m_scalingFactor,
                                                             m_nonBlurBackground.height() * m_scalingFactor);
        }
    }
}

void DesktopBackground::setBlurAlpha(qreal alpha)
{
    if(alpha < 0 || alpha > 1)
        return;
    m_blurAlpha = alpha;
    quint32 blur_radius = qRound(m_blurAlpha * MAX_BLUR_ALPHA);
    if (blur_radius != 0  && m_blurRadius != blur_radius)
    {
        m_blurRadius = qRound(m_blurAlpha * MAX_BLUR_ALPHA);
        emit blurAlphaChanged();
    }
}

QRect DesktopBackground::scaledRect(const QImage &image)
{
    qreal scaledAspectRatio = qreal(boundingRect().width()) / qreal(boundingRect().height());
    qreal width = image.width();
    qreal height = image.height();

    if ((width / height) == scaledAspectRatio) {
        return image.rect();
    }

    bool widthShorter = (width <= height);
    // scaledAspectRatio是一个必大于1的实数
    // 值为qMax(boundingRect().height(), boundingRect().width()) / qMax(boundingRect().width(), boundingRect().height())
    if (widthShorter) {
        scaledAspectRatio = qreal(boundingRect().height()) / qreal(boundingRect().width());
    }

    qreal shorterEdge = widthShorter ? width : height;
    qreal longerEdge = widthShorter ? height : width;

    while (shorterEdge > 1) {
        qint32 temp = qFloor(shorterEdge * scaledAspectRatio);
        if (temp <= longerEdge) {
            longerEdge = temp;
            break;
        }

        qint32 spacing = qRound(shorterEdge / 20);
        if (spacing <= 0) {
            spacing = 1;
        }
        shorterEdge -= spacing;
    }

    QSize scaledSize = image.size();
    if (shorterEdge > 1 && longerEdge > 1) {
        scaledSize.setWidth(widthShorter ? shorterEdge : longerEdge);
        scaledSize.setHeight(widthShorter ? longerEdge : shorterEdge);
    }

    qint32 offsetX = 0;
    qint32 offsetY = 0;
    if (image.width() > scaledSize.width()) {
        offsetX = (image.width() - scaledSize.width()) / 2;
    }

    if (image.height() > scaledSize.height()) {
        offsetY = (image.height() - scaledSize.height()) / 2;
    }

    QPoint offsetPoint = image.rect().topLeft();
    offsetPoint += QPoint(offsetX, offsetY);

    return QRect(offsetPoint, scaledSize);
}

QString DesktopBackground::getBackgroundFile()
{
    QUrl url;
    QString filePath;
    QString schemaUrl = "org.mate.background";
    QString keyName =   "pictureFilename";

    if (QGSettings::isSchemaInstalled(schemaUrl.toUtf8())) {
        QGSettings gsettings(schemaUrl.toUtf8());

        const QStringList keyList = gsettings.keys();
        if (keyList.contains(keyName)) {
            QString name = gsettings.get(keyName).toString();
            url = QUrl::fromLocalFile(name);
            filePath = url.toString().replace(QRegExp("^file:/"), "");
        } else {
            qWarning() << QString("未获取到Gesetting的键值(%1) value").arg(keyName);
            return "";
        }
    }

    return filePath;
}

QString DesktopBackground::getBackgroundFillStyle()
{
    QString fillStyle;
    if (QGSettings::isSchemaInstalled(backgroundSchemaUrl.toUtf8())) {
        QGSettings backgroundGsetting(backgroundSchemaUrl.toUtf8());
        const QStringList keyList = backgroundGsetting.keys();
        if (keyList.contains(fillStyleKeyName)) {
            fillStyle = backgroundGsetting.get(fillStyleKeyName).toString();
//            qDebug() << "MMMMMM fillStyle = " << fillStyle;
        } else {
            qWarning() << QString("未获取到Gesetting的键值(%1) value").arg(fillStyleKeyName);
            // default to scaled
            return "scaled";
        }
    }
    return fillStyle;
}

void DesktopBackground::updateBlurBackground()
{
    QElapsedTimer timer;
    timer.start();
    if (m_nonBlurBackground.isNull())
        return;

    if (m_blurRadius > 0) {
        // 重新模糊背景图片
        m_blurBackground = m_nonBlurBackground;
        bluredImage(m_blurAlpha * MAX_BLUR_ALPHA, m_blurBackground);
    }
    qDebug() << "The DesktopBackground blur took" << timer.elapsed() << "milliseconds";
//    m_background.save("/home/pangyi/backgroundImage.png", "PNG");
//    m_blurBackground.save("/home/pangyi/backgroundImage1.png", "PNG");
}

int DesktopBackground::edge(int x, int i, int w)
{
    // x 为中心位置, i 为偏移量, w 为图片的宽度/高度
    int inx = x + i;
    if(inx < 0 || inx >= w)
        return x - i;
    else
        return inx;
}

// 一维高斯函数
float DesktopBackground::gaussFunc1D(int x, float sigma)
{
    float A = 1.0f / (sigma * sqrt(2 * 3.1415f));
    float index = -1.0 * (x * x) / (2 * sigma * sigma);
    return A * qExp(index);
}

// 获取线性的权值空间
void DesktopBackground::getKernal(int radius, float* weight)
{
    float sigma = radius / 3.0f;
    float sum = 0.0f;
    // 获取权值空间weight[]
    for(int i = 0; i < 2 * radius + 1; i++)
    {
        weight[i] = gaussFunc1D(i - radius, sigma);
        sum += weight[i];
    }
    // 归一化
    for(int i = 0; i < 2 * radius + 1; i++)
    {
        weight[i] /= sum;
    }
}

// 获取最终的模糊图像
void DesktopBackground::bluredImage(int blur_radius, QImage &blurImage)
{
    // 原图为 originImg
    // 第一个方向处理的图像为tmpImg
    int originWidth = blurImage.width();
    int originHeight = blurImage.height();
    blurImage = blurImage.scaled(400, 300, Qt::KeepAspectRatio);
    QImage tmpImg = blurImage;
    // 经过第二次处理的最终结果bluredImage
    float weight[2 * blur_radius + 1];
    getKernal(blur_radius, weight);
    int height = blurImage.height();
    int width = blurImage.width();
    float rgb[3];
    unsigned char *p, *p1, *ptmp;
    // 在横向进行一次相加
    for(int y = 0; y < height; y++)
    {
        p = blurImage.scanLine(y);
        for(int x = 0; x < width; x++)
        {
            rgb[0] = rgb[1] = rgb[2] = 0.0f;
            for(int i = -blur_radius; i <= blur_radius; i++)
            {
                // 边界处理后的对应的权值矩阵实际值
                int inx = edge(x, i, width);
                p1 = p + inx * 4;
                rgb[0] += p1[0] * weight[blur_radius + i];
                rgb[1] += p1[1] * weight[blur_radius + i];
                rgb[2] += p1[2] * weight[blur_radius + i];
            }
            ptmp = tmpImg.scanLine(y) + 4 * x;
            ptmp[0] = rgb[0];
            ptmp[1] = rgb[1];
            ptmp[2] = rgb[2];
        }
    }
    // 在纵方向对第一次的结果重新进行一次
    for(int y = 0; y < height; y++)
    {
        for(int x = 0; x < width; x++)
        {
            rgb[0] = rgb[1] = rgb[2] = 0.0f;
            for(int i = -blur_radius; i <= blur_radius; i++)
            {
                int iny = edge(y, i, height);
                p = tmpImg.scanLine(iny);
                p1 = p + x * 4;
                rgb[0] += p1[0] * weight[blur_radius + i];
                rgb[1] += p1[1] * weight[blur_radius + i];
                rgb[2] += p1[2] * weight[blur_radius + i];
            }
            ptmp = blurImage.scanLine(y) + 4 * x;
            ptmp[0] = rgb[0];
            ptmp[1] = rgb[1];
            ptmp[2] = rgb[2];
        }
    }
    blurImage = blurImage.scaled(originWidth, originHeight, Qt::KeepAspectRatio);
}


double DesktopBackground::getSystemScalingFactor()
{
    double scalingFactor = 1.5;
    QString schemaUrl = "org.ukui.SettingsDaemon.plugins.xsettings";
    QString keyName   = "scalingFactor";

    if (QGSettings::isSchemaInstalled(schemaUrl.toUtf8())) {
        QGSettings gsettings(schemaUrl.toUtf8());

        const QStringList keyList = gsettings.keys();
        if (keyList.contains(keyName)) {
            scalingFactor = gsettings.get(keyName).toDouble();
//            qDebug() << "MMMMMM scalingFactor = " << scalingFactor;
        } else {
            qWarning() << QString("未获取到Gesetting的键值(%1) value").arg(keyName);
            // default
            return scalingFactor;
        }
    }

    return scalingFactor;
}

void DesktopBackground::onBlurAlphaChanged()
{
    if(m_nonBlurBackground.isNull())
        return;

    updateBlurBackground();
    update();
}

void DesktopBackground::paint(QPainter *painter)
{
    QImage tmpImage;
    if (qRound(m_blurAlpha * MAX_BLUR_ALPHA) > 0) {
        tmpImage = m_blurBackground;
    } else {
        tmpImage = m_nonBlurBackground;
    }
    if (m_backgroundFillStyle == "centered")    {
        //居中
        painter->drawImage((boundingRect().width() - tmpImage.width()) / 2,
                     (boundingRect().height() - tmpImage.height()) / 2,
                     tmpImage);
    } else if (m_backgroundFillStyle == "wallpaper") {
        //平铺
        int drewWidth = 0;
        int drewHeight = 0;
        while (drewHeight < boundingRect().height()) {
            // draw background on x-direction
            while (drewWidth < boundingRect().width()) {
                painter->drawImage(drewWidth, drewHeight, tmpImage);
                drewWidth += tmpImage.width();
            }
            // set drewWidth to 0 to start next line of drawing
            drewWidth = 0;
            // draw background on y-direction
            drewHeight += tmpImage.height();
        }
    } else if (m_backgroundFillStyle == "scaled") {
        //填充
        painter->drawImage(boundingRect(), tmpImage, scaledRect(tmpImage));
    } else if (m_backgroundFillStyle == "stretched") {
        //拉伸
        painter->drawImage(boundingRect(), tmpImage);
    }
    else {
        // default to scaled
        painter->drawImage(boundingRect(), tmpImage, scaledRect(tmpImage));
    }
}
