/*
 * Qt5-UKUI's Library
 *
 * Copyright (C) 2020, Tianjin KYLIN Information Technology Co., Ltd.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this library.  If not, see <https://www.gnu.org/licenses/>.
 *
 * Authors: Yue Lan <lanyue@kylinos.cn>
 *
 */

#include "highlight-effect.h"

#include <QAbstractItemView>
#include <QMenu>
#include <QStyleOption>

#include <QPixmap>
#include <QPainter>

#include <QImage>
#include <QtMath>

#include <QApplication>

#include <QDebug>

#define ColorDifference 10

QColor HighLightEffect::symbolic_color = QColor(31, 32, 34, 192);

void HighLightEffect::setSkipEffect(QWidget *w, bool skip)
{
    w->setProperty("skipHighlightIconEffect", skip);
}



bool HighLightEffect::isPixmapPureColor(const QPixmap &pixmap)
{
    QImage image = pixmap.toImage();

    QVector<QColor> vector;
    int total_red = 0;
    int total_green = 0;
    int total_blue = 0;
    bool pure = true;
    for (int y = 0; y < image.height(); ++y) {
        for (int x = 0; x < image.width(); ++x) {
            if (image.pixelColor(x, y).alphaF() > 0.3) {
                QColor color = image.pixelColor(x, y);
                vector << color;
                total_red += color.red();
                total_green += color.green();
                total_blue += color.blue();
                int dr = qAbs(color.red() - symbolic_color.red());
                int dg = qAbs(color.green() - symbolic_color.green());
                int db = qAbs(color.blue() - symbolic_color.blue());
                if (dr > ColorDifference || dg > ColorDifference || db > ColorDifference)
                    pure = false;
            }
        }
    }

    if (pure)
        return true;

    qreal squareRoot_red = 0;
    qreal squareRoot_green = 0;
    qreal squareRoot_blue = 0;
    qreal average_red = total_red / vector.count();
    qreal average_green = total_green / vector.count();
    qreal average_blue = total_blue / vector.count();
    for (QColor color : vector) {
        squareRoot_red += (color.red() - average_red) * (color.red() - average_red);
        squareRoot_green += (color.green() - average_green) * (color.green() - average_green);
        squareRoot_blue += (color.blue() - average_blue) * (color.blue() - average_blue);
    }

    qreal arithmeticSquareRoot_red = qSqrt(squareRoot_red / vector.count());
    qreal arithmeticSquareRoot_green = qSqrt(squareRoot_green / vector.count());
    qreal arithmeticSquareRoot_blue = qSqrt(squareRoot_blue / vector.count());
    return arithmeticSquareRoot_red < 2.0 && arithmeticSquareRoot_green < 2.0 && arithmeticSquareRoot_blue < 2.0;
}



bool HighLightEffect::setMenuIconHighlightEffect(QMenu *menu, HighLightMode hlmode, EffectMode mode)
{
    if (!menu)
        return false;

    menu->setProperty("useIconHighlightEffect", hlmode);
    menu->setProperty("iconHighlightEffectMode", mode);
    return true;
}

bool HighLightEffect::setViewItemIconHighlightEffect(QAbstractItemView *view, HighLightMode hlmode, EffectMode mode)
{
    if (!view)
        return false;

    view->viewport()->setProperty("useIconHighlightEffect", hlmode);
    view->viewport()->setProperty("iconHighlightEffectMode", mode);
    return true;
}

HighLightEffect::HighLightMode HighLightEffect::isWidgetIconUseHighlightEffect(const QWidget *w)
{
    if (w) {
        if (w->property("useIconHighlightEffect").isValid())
            return HighLightMode(w->property("useIconHighlightEffect").toInt());
    }
    return HighLightEffect::skipHighlight;
}

void HighLightEffect::setSymoblicColor(const QColor &color)
{
    qApp->setProperty("symbolicColor", color);
    symbolic_color = color;
}

void HighLightEffect::setWidgetIconFillSymbolicColor(QWidget *widget, bool fill)
{
    widget->setProperty("fillIconSymbolicColor", fill);
}

const QColor HighLightEffect::getCurrentSymbolicColor()
{
    QIcon symbolic = QIcon::fromTheme("window-new-symbolic");
    QPixmap pix = symbolic.pixmap(QSize(16, 16));
    QImage img = pix.toImage();
    for (int x = 0; x < img.width(); x++) {
        for (int y = 0; y < img.height(); y++) {
            QColor color = img.pixelColor(x, y);
            if (color.alpha() > 0) {
                symbolic_color = color;
                return color;
            }
        }
    }
    return symbolic_color;
}

const QColor HighLightEffect::defaultStyleDark()
{
    auto windowText = qApp->palette().windowText().color();
    qreal h, s, v;
    windowText.getHsvF(&h, &s, &v);
    return QColor::fromHsvF(h, s*0.85, v, 0.7);
}

QPixmap HighLightEffect::generatePixmap(const QPixmap &pixmap, const QStyleOption *option, const QWidget *widget, bool force, EffectMode mode)
{
    if (pixmap.isNull())
        return pixmap;
    if (!(option->state & QStyle::State_Enabled))
        return pixmap;
    if (widget && !widget->isEnabled())
        return pixmap;

    QPixmap target = pixmap;
    bool isPurePixmap = isPixmapPureColor(pixmap);
    if (force) {
        if (!isPurePixmap)
            return pixmap;

        QPainter p(&target);
        p.setRenderHint(QPainter::Antialiasing);
        p.setRenderHint(QPainter::SmoothPixmapTransform);
        p.setCompositionMode(QPainter::CompositionMode_SourceIn);
        if (option->state & QStyle::State_MouseOver ||
                option->state & QStyle::State_Selected ||
                option->state & QStyle::State_On ||
                option->state & QStyle::State_Sunken) {
            p.fillRect(target.rect(), option->palette.highlightedText());
        } else {
                p.fillRect(target.rect(), mode ? option->palette.text() : defaultStyleDark());
        }
        return target;
    }



    if (!widget)
        return pixmap;

    if (widget->property("skipHighlightIconEffect").isValid()) {
        bool skipEffect = widget->property("skipHighlightIconEffect").toBool();
        if (skipEffect)
            return pixmap;
    }

    if (widget->property("iconHighlightEffectMode").isValid()) {
        mode = EffectMode(widget->property("iconHighlightEffectMode").toBool());
    }

    HighLightMode hlmode = isWidgetIconUseHighlightEffect(widget);

    if (hlmode == skipHighlight) {
        return pixmap;
    } else if (hlmode == HighlightEffect) {
        bool fillIconSymbolicColor = false;
        if (widget->property("fillIconSymbolicColor").isValid()) {
            fillIconSymbolicColor = widget->property("fillIconSymbolicColor").toBool();
        }

        bool isEnable = option->state.testFlag(QStyle::State_Enabled);
        bool overOrDown =  option->state.testFlag(QStyle::State_MouseOver) ||
                option->state.testFlag(QStyle::State_Sunken) ||
                option->state.testFlag(QStyle::State_On) ||
                option->state.testFlag(QStyle::State_Selected);

        if (qobject_cast<const QAbstractItemView *>(widget)) {
            if (!option->state.testFlag(QStyle::State_Selected))
                overOrDown = false;
        }

        if (isEnable && overOrDown) {
            if (fillIconSymbolicColor) {
                target = filledSymbolicColoredPixmap(pixmap, option->palette.highlightedText().color());
            }

            if (!isPurePixmap)
                return target;

            QPainter p(&target);
            p.setRenderHint(QPainter::Antialiasing);
            p.setRenderHint(QPainter::SmoothPixmapTransform);
            p.setCompositionMode(QPainter::CompositionMode_SourceIn);
            p.fillRect(target.rect(), option->palette.highlightedText());
            return target;
        } else {
            QPixmap target = pixmap;
            if (fillIconSymbolicColor) {
                target = filledSymbolicColoredPixmap(pixmap, option->palette.highlightedText().color());
            }
            if (!isPurePixmap)
                return target;

            QPainter p(&target);
            p.setRenderHint(QPainter::Antialiasing);
            p.setRenderHint(QPainter::SmoothPixmapTransform);
            p.setCompositionMode(QPainter::CompositionMode_SourceIn);
            p.fillRect(target.rect(), mode ? option->palette.text() : defaultStyleDark());
            return target;
        }
    } else if (hlmode == ordinaryHighLight) {
        return ordinaryGeneratePixmap(pixmap, option, widget, mode);
    } else if (hlmode == hoverHighLight) {
        return hoverGeneratePixmap(pixmap, option, widget);
    } else if (hlmode == defaultHighLight) {
        return bothOrdinaryAndHoverGeneratePixmap(pixmap, option, widget, mode);
    } else if (hlmode == filledSymbolicColorHighLight) {
        if (isPurePixmap) {
            return bothOrdinaryAndHoverGeneratePixmap(pixmap, option, widget, mode);
        } else {
            return filledSymbolicColoredGeneratePixmap(pixmap, option, widget, mode);
        }
    } else {
        return pixmap;
    }

    return pixmap;
}

QPixmap HighLightEffect::ordinaryGeneratePixmap(const QPixmap &pixmap, const QStyleOption *option, const QWidget *widget, EffectMode mode)
{
    if (pixmap.isNull())
        return pixmap;
    if (!isPixmapPureColor(pixmap) || !(option->state & QStyle::State_Enabled))
        return pixmap;

    QPixmap target = pixmap;
    QColor color;
    if (widget && widget->property("setIconHighlightEffectDefaultColor").isValid() && widget->property("setIconHighlightEffectDefaultColor").canConvert<QColor>()) {
        color = widget->property("setIconHighlightEffectDefaultColor").value<QColor>();
    }

    if (widget && widget->property("iconHighlightEffectMode").isValid()) {
        mode = EffectMode(widget->property("iconHighlightEffectMode").toBool());
    }

    QPainter p(&target);
    p.setRenderHint(QPainter::Antialiasing);
    p.setRenderHint(QPainter::SmoothPixmapTransform);
    p.setCompositionMode(QPainter::CompositionMode_SourceIn);
    p.fillRect(target.rect(), color.isValid() ? color : (mode ? option->palette.text() : defaultStyleDark()));
    return target;
}

QPixmap HighLightEffect::hoverGeneratePixmap(const QPixmap &pixmap, const QStyleOption *option, const QWidget *widget, EffectMode mode)
{
    if (pixmap.isNull())
        return pixmap;
    if (!isPixmapPureColor(pixmap) || !(option->state & QStyle::State_Enabled))
        return pixmap;

    QPixmap target = pixmap;
    QColor color;
    if (widget && widget->property("setIconHighlightEffectHoverColor").isValid() && widget->property("setIconHighlightEffectHoverColor").canConvert<QColor>()) {
        color = widget->property("setIconHighlightEffectHoverColor").value<QColor>();
    }

    if (widget && widget->property("iconHighlightEffectMode").isValid()) {
        mode = EffectMode(widget->property("iconHighlightEffectMode").toBool());
    }

    bool overOrDown =  option->state.testFlag(QStyle::State_MouseOver) ||
            option->state.testFlag(QStyle::State_Sunken) ||
            option->state.testFlag(QStyle::State_On) ||
            option->state.testFlag(QStyle::State_Selected);
    if (qobject_cast<const QAbstractItemView *>(widget)) {
        if (!option->state.testFlag(QStyle::State_Selected))
            overOrDown = false;
    }

    QPainter p(&target);
    if (overOrDown) {
        p.setRenderHint(QPainter::Antialiasing);
        p.setRenderHint(QPainter::SmoothPixmapTransform);
        p.setCompositionMode(QPainter::CompositionMode_SourceIn);
        p.fillRect(target.rect(), color.isValid() ? color : option->palette.highlightedText());
    }
    return target;
}

QPixmap HighLightEffect::bothOrdinaryAndHoverGeneratePixmap(const QPixmap &pixmap, const QStyleOption *option, const QWidget *widget, EffectMode mode)
{
    if (pixmap.isNull())
        return pixmap;
    if (!isPixmapPureColor(pixmap) || !(option->state & QStyle::State_Enabled))
        return pixmap;

    QPixmap target = pixmap;
    QColor defaultColor, hoverColor;
    if (widget && widget->property("setIconHighlightEffectDefaultColor").isValid() && widget->property("setIconHighlightEffectDefaultColor").canConvert<QColor>()) {
        defaultColor = widget->property("setIconHighlightEffectDefaultColor").value<QColor>();
    }
    if (widget && widget->property("setIconHighlightEffectHoverColor").isValid() && widget->property("setIconHighlightEffectHoverColor").canConvert<QColor>()) {
        hoverColor = widget->property("setIconHighlightEffectHoverColor").value<QColor>();
    }

    if (widget && widget->property("iconHighlightEffectMode").isValid()) {
        mode = EffectMode(widget->property("iconHighlightEffectMode").toBool());
    }

    bool overOrDown =  option->state.testFlag(QStyle::State_MouseOver) ||
            option->state.testFlag(QStyle::State_Sunken) ||
            option->state.testFlag(QStyle::State_On) ||
            option->state.testFlag(QStyle::State_Selected);
    if (qobject_cast<const QAbstractItemView *>(widget)) {
        if (!option->state.testFlag(QStyle::State_Selected))
            overOrDown = false;
    }

    QPainter p(&target);
    if (overOrDown) {
        p.setRenderHint(QPainter::Antialiasing);
        p.setRenderHint(QPainter::SmoothPixmapTransform);
        p.setCompositionMode(QPainter::CompositionMode_SourceIn);
        p.fillRect(target.rect(), hoverColor.isValid() ? hoverColor : option->palette.highlightedText());
    } else {
        p.setRenderHint(QPainter::Antialiasing);
        p.setRenderHint(QPainter::SmoothPixmapTransform);
        p.setCompositionMode(QPainter::CompositionMode_SourceIn);
        p.fillRect(target.rect(), defaultColor.isValid() ? defaultColor : (mode ? option->palette.text() : defaultStyleDark()));
    }
    return target;
}

QPixmap HighLightEffect::filledSymbolicColoredGeneratePixmap(const QPixmap &pixmap, const QStyleOption *option, const QWidget *widget, EffectMode mode)
{
    if (pixmap.isNull())
        return pixmap;
    if (isPixmapPureColor(pixmap))
        return bothOrdinaryAndHoverGeneratePixmap(pixmap, option, widget, mode);

    QPixmap target = pixmap;
    QColor defaultColor, hoverColor;
    if (widget && widget->property("setIconHighlightEffectDefaultColor").isValid() && widget->property("setIconHighlightEffectDefaultColor").canConvert<QColor>()) {
        defaultColor = widget->property("setIconHighlightEffectDefaultColor").value<QColor>();
    }
    if (widget && widget->property("setIconHighlightEffectHoverColor").isValid() && widget->property("setIconHighlightEffectHoverColor").canConvert<QColor>()) {
        hoverColor = widget->property("setIconHighlightEffectHoverColor").value<QColor>();
    }

    if (widget && widget->property("iconHighlightEffectMode").isValid()) {
        mode = EffectMode(widget->property("iconHighlightEffectMode").toBool());
    }
    bool isEnable = option->state.testFlag(QStyle::State_Enabled);
    bool overOrDown =  option->state.testFlag(QStyle::State_MouseOver) ||
            option->state.testFlag(QStyle::State_Sunken) ||
            option->state.testFlag(QStyle::State_On) ||
            option->state.testFlag(QStyle::State_Selected);
    if (qobject_cast<const QAbstractItemView *>(widget)) {
        if (!option->state.testFlag(QStyle::State_Selected))
            overOrDown = false;
    }

    if (isEnable && overOrDown) {
        return filledSymbolicColoredPixmap(target, hoverColor.isValid() ? hoverColor : option->palette.color(QPalette::HighlightedText));
    } else {
        return filledSymbolicColoredPixmap(target, defaultColor.isValid() ? defaultColor : (mode ? option->palette.color(QPalette::Text) : defaultStyleDark()));
    }

    return target;
}

HighLightEffect::HighLightEffect(QObject *parent) : QObject(parent)
{

}

QPixmap HighLightEffect::filledSymbolicColoredPixmap(const QPixmap &source, const QColor &baseColor)
{
    if (source.isNull())
        return source;
    QImage img = source.toImage();
    for (int x = 0; x < img.width(); x++) {
        for (int y = 0; y < img.height(); y++) {
            auto color = img.pixelColor(x, y);
            if (color.alpha() > 0.3) {
                if (qAbs(color.red() - symbolic_color.red()) < 10 && qAbs(color.green() - symbolic_color.green()) < 10
                        && qAbs(color.blue() - symbolic_color.blue()) < 10) {
                    color.setRed(baseColor.red());
                    color.setGreen(baseColor.green());
                    color.setBlue(baseColor.blue());
                    img.setPixelColor(x, y, color);
                }
            }
        }
    }
    return QPixmap::fromImage(img);
}
