/*
 * libkysdk-qtwidgets's Library
 *
 * Copyright (C) 2023, KylinSoft 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: Zhen Sun <sunzhen1@kylinos.cn>
 *
 */

#include "kcolorcombobox.h"
#include "themeController.h"
#include <QAbstractItemView>
#include <QCompleter>
#include <QDebug>
#include <QListView>
#include <QProxyStyle>
#include <QResizeEvent>
#include <QStyledItemDelegate>
#include <QVariant>
#include <qstylepainter.h>

namespace kdk
{

const static QSize defaultPopupItemSize(20, 20);
static QSize g_size;

class KComboStyle : public QProxyStyle
{
public:
    KComboStyle()
    {
    }
    ~KComboStyle()
    {
    }

    QSize sizeFromContents(QStyle::ContentsType type,
                           const QStyleOption *option,
                           const QSize &contentsSize,
                           const QWidget *widget = nullptr) const override;
};

class KColorComboBoxDelegate : public QStyledItemDelegate
{
    Q_OBJECT

public:
    enum ItemRoles {
        ColorRole = Qt::UserRole + 1
    };

    KColorComboBoxDelegate(QObject *parent = nullptr, KColorComboBox *combo = nullptr);
    void paint(QPainter *painter,
               const QStyleOptionViewItem &option, const QModelIndex &index) const override;
    QSize sizeHint(const QStyleOptionViewItem &option,
                   const QModelIndex &index) const override;

private:
    KColorComboBox *m_combo;
};

class KColorComboBoxPrivate : public QObject, public ThemeController
{
    Q_OBJECT
    Q_DECLARE_PUBLIC(KColorComboBox)

public:
    KColorComboBoxPrivate(KColorComboBox *parent);
    void updateList();

public Q_SLOTS:
    void slotActivated(int);
    void slotHighlighted(int);
    void slotCurrentIndexChanged(int);

private:
    KColorComboBox *q_ptr;
    KColorComboBox::ComboType m_comboType;
    QList<QColor> m_colorList;
    int m_borderRadius;
    QColor m_currentColor;
    QSize m_popupItemSize;
};

KColorComboBox::KColorComboBox(QWidget *parent)
    : QComboBox(parent)
    , d_ptr(new KColorComboBoxPrivate(this))
{
    Q_D(KColorComboBox);
    setItemDelegate(new KColorComboBoxDelegate(this, this));
    KComboStyle *style = new KComboStyle();
    setStyle(style);
    view()->setFixedWidth(d->m_popupItemSize.width());
    connect(this, SIGNAL(activated(int)), d, SLOT(slotActivated(int)));
    connect(this, SIGNAL(highlighted(int)), d, SLOT(slotHighlighted(int)));
    connect(this, SIGNAL(currentIndexChanged(int)), d, SLOT(slotCurrentIndexChanged(int)));
}

void KColorComboBox::setColorList(const QList<QColor> &list)
{
    Q_D(KColorComboBox);
    d->m_colorList = list;
    d->updateList();
}

QList<QColor> KColorComboBox::colorList()
{
    Q_D(KColorComboBox);
    return d->m_colorList;
}

void KColorComboBox::addColor(const QColor &color)
{
    Q_D(KColorComboBox);
    d->m_colorList.append(color);
    addItem(QString());
    setItemData(d->m_colorList.count() - 1, d->m_colorList.back(), KColorComboBoxDelegate::ColorRole);
    update();
}

void KColorComboBox::setComboType(const KColorComboBox::ComboType &type)
{
    Q_D(KColorComboBox);
    d->m_comboType = type;
    update();
}

KColorComboBox::ComboType KColorComboBox::comboType()
{
    Q_D(KColorComboBox);
    return d->m_comboType;
}

void KColorComboBox::setPopupItemSize(const QSize &size)
{
    Q_D(KColorComboBox);

    d->m_popupItemSize = size.expandedTo(this->size());
    g_size = d->m_popupItemSize;

    if (QListView *listView = qobject_cast<QListView *>(view())) {
        listView->setFixedWidth(d->m_popupItemSize.width());
        listView->setGridSize(d->m_popupItemSize);
    }

    view()->updateGeometry();
    update();
}

QSize KColorComboBox::popupItemSzie()
{
    Q_D(KColorComboBox);
    return d->m_popupItemSize;
}

void KColorComboBox::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)
    Q_D(KColorComboBox);
    QStylePainter painter(this);
    painter.setPen(Qt::NoPen);

    QStyleOptionComboBox opt;
    initStyleOption(&opt);

    QRect frame = this->rect();
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setPen(Qt::transparent);
    if (d->m_currentColor.isValid())
        painter.setBrush(d->m_currentColor);
    if (d->m_comboType == KColorComboBox::Circle) {
        painter.drawEllipse(frame.adjusted(1, 1, -1, -1));
    } else {
        painter.drawRoundedRect(frame.adjusted(1, 1, -1, -1), d->m_borderRadius, d->m_borderRadius);
    }
}

void KColorComboBox::resizeEvent(QResizeEvent *event)
{
    setPopupItemSize(event->size());
    QComboBox::resizeEvent(event);
}

KColorComboBoxPrivate::KColorComboBoxPrivate(KColorComboBox *parent)
    : q_ptr(parent)
    , m_comboType(KColorComboBox::Circle)
    , m_popupItemSize(defaultPopupItemSize)
{
    setParent(parent);
    m_borderRadius = ThemeController::getRadiusFromDT("kradius-min");
    if (m_borderRadius == -1)
        m_borderRadius = 4;
}

void KColorComboBoxPrivate::updateList()
{
    Q_Q(KColorComboBox);
    while (q->count()) {
        q->removeItem(0);
    }
    for (int i = 0; i < m_colorList.count(); ++i) {
        q->addItem(QString());
        q->setItemData(i, m_colorList[i], KColorComboBoxDelegate::ColorRole);
    }
    q->update();
}

void KColorComboBoxPrivate::slotActivated(int index)
{
    Q_Q(KColorComboBox);

    m_currentColor = m_colorList[index];
    q->update();
    Q_EMIT q->activated(m_currentColor);
}

void KColorComboBoxPrivate::slotHighlighted(int index)
{
    Q_Q(KColorComboBox);

    auto color = m_colorList[index];
    q->update();
    Q_EMIT q->highlighted(color);
}

void KColorComboBoxPrivate::slotCurrentIndexChanged(int index)
{
    Q_Q(KColorComboBox);

    m_currentColor = m_colorList[index];
    q->update();
    Q_EMIT q->currentColorChanged(m_currentColor);
}

KColorComboBoxDelegate::KColorComboBoxDelegate(QObject *parent, KColorComboBox *combo)
    : QStyledItemDelegate(parent)
    , m_combo(combo)
{
}

void KColorComboBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    auto variant = index.data(ItemRoles::ColorRole);
    auto color = variant.value<QColor>();
    auto paintRect = option.rect.adjusted(5, 5, -5, -5);

    switch (m_combo->comboType()) {
    case KColorComboBox::Circle: {
        if (color.isValid()) {
            painter->save();
            painter->setRenderHint(QPainter::Antialiasing);
            painter->setPen(Qt::NoPen);
            painter->setBrush(color);
            painter->drawEllipse(paintRect);
            painter->restore();
        }
        if (option.state & (QStyle::State_MouseOver | QStyle::State_Selected)) {
            painter->save();
            painter->setRenderHint(QPainter::Antialiasing);
            painter->setPen(Qt::NoPen);
            painter->setBrush(Qt::white);
            QRect subRect(paintRect.top(), paintRect.left(), paintRect.width() / 2, paintRect.height() / 2);
            subRect.moveCenter(paintRect.center());
            painter->drawEllipse(subRect);
            painter->restore();
        }
        break;
    }
    case KColorComboBox::RoundedRect: {
        if (color.isValid()) {
            painter->save();
            painter->setRenderHint(QPainter::Antialiasing);
            if (option.state & (QStyle::State_MouseOver | QStyle::State_Selected)) {
                QPen pen;
                pen.setWidth(2);
                pen.setBrush(Qt::white);
                painter->setBrush(color);
                painter->setPen(pen);
            } else {
                painter->setBrush(color);
                painter->setPen(Qt::NoPen);
            }
            int radius = ThemeController::getRadiusFromDT("kradius-min");
            if (radius == -1)
                radius = 4;
            painter->drawRoundedRect(paintRect, radius, radius);
            painter->restore();
        }
        break;
    }
    default:
        break;
    }
}

QSize KColorComboBoxDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    return m_combo->popupItemSzie();
}

QSize KComboStyle::sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &contentsSize, const QWidget *widget) const
{
    if (type == QStyle::CT_ComboBox)
        return g_size;

    return QProxyStyle::sizeFromContents(type, option, contentsSize, widget);
}

}
#include "kcolorcombobox.moc"
#include "moc_kcolorcombobox.cpp"
