#include "BaseHeaderView.h"
#include "utils/Utils.h"
#include <DApplication>
#include <DGuiApplicationHelper>
#include <DPalette>
#include <DStyle>
#include <DStyleHelper>

#include <QDebug>
#include <QPaintEvent>
#include <QPainter>
#include <QtMath>
#include <QModelIndex>
#include <QPainterPath>

#if DTK_VERSION >= DTK_VERSION_CHECK(5, 5, 10, 0)
    #include <DPaletteHelper>
#else
    #include <DApplicationHelper>
#endif

static const int kSpacingMargin = 4;
static const QSize kDropDownSize {11, 10};

BaseHeaderView::BaseHeaderView(Qt::Orientation orientation, QWidget *parent)
    : DHeaderView(orientation, parent)
{
    installEventFilter(this);
    viewport()->setAutoFillBackground(false);
}

QSize BaseHeaderView::sizeHint() const
{
    return QSize(width(), 36 + m_spacing);
}

int BaseHeaderView::sectionSizeHint(int logicalIndex) const
{
    return DHeaderView::sectionSizeHint(logicalIndex);
}

void BaseHeaderView::paintEvent(QPaintEvent *event)
{
    QPainter painter(viewport());
    painter.save();
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setOpacity(0.03);

    DPalette::ColorGroup cg;
    cg = DPalette::Active;
#if DTK_VERSION >= DTK_VERSION_CHECK(5, 5, 10, 0)
    DPalette palette = DPaletteHelper::instance()->palette(this);
#else
    DPalette palette = DApplicationHelper::instance()->palette(this);
#endif

    auto *style = dynamic_cast<DStyle *>(DApplication::style());

    QStyleOption opt;
    opt.initFrom(this);
    QBrush bgBrush(palette.color(cg, DPalette::BrightText));
    QBrush hlBrush = Qt::transparent;
    QRect hlRect;

    // background
    if ((opt.state & DStyle::State_Enabled) && m_hover != -1) {
        auto baseColor = bgBrush.color();
        if (m_pressed >= 0) {
            auto actColor = palette.color(cg, DPalette::Highlight);
            actColor.setAlphaF(0.1);
            auto newColor = style->adjustColor(baseColor, 0, 0, -20, 0, 0, 20, 0);
            hlBrush = style->blendColor(newColor, actColor);
        } else {
            hlBrush = style->adjustColor(baseColor, 0, 0, -10);
        }
        auto spos = sectionPosition(m_hover);
        auto sw = sectionSize(m_hover);
        hlRect = {
            m_hover > 0 ? spos + 1 - offset() : spos - offset(),
            0,
            m_hover > 0 ? sw - 1 : sw,
            height()};
    }

    QStyleOptionHeader option;
    initStyleOption(&option);
    auto radius = style->pixelMetric(DStyle::PM_FrameRadius, &option);

    QRect rect = viewport()->rect();
    QRectF clipRect(rect.x(), rect.y(), rect.width(), rect.height() * 2);
    QRectF subRect(rect.x(), rect.y() + rect.height(), rect.width(), rect.height());
    QPainterPath clipPath, subPath;
    clipPath.addRoundedRect(clipRect, radius, radius);
    subPath.addRect(subRect);
    clipPath = clipPath.subtracted(subPath);

    painter.fillPath(clipPath, bgBrush);
    QPainterPath hlPath;
    hlPath.addRect(hlRect);
    hlPath = hlPath.intersected(clipPath);
    painter.fillPath(hlPath, hlBrush);

    DHeaderView::paintEvent(event);
    painter.restore();

    // draw focus
    if (hasFocus()) {
        QStyleOptionFocusRect o;
        o.QStyleOption::operator=(option);
        QRect focusRect {rect.x() - offset(), rect.y(), length() - sectionPosition(0), rect.height()};
        o.rect = style->visualRect(layoutDirection(), rect, focusRect);
        style->drawPrimitive(DStyle::PE_FrameFocusRect, &o, &painter);
    }
}

void BaseHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
{
    painter->save();
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setOpacity(1);

    DPalette::ColorGroup cg;
    QStyleOption opt;
    opt.initFrom(this);
    if (!(opt.state & DStyle::State_Enabled)) {
        cg = DPalette::Disabled;
    } else {
        cg = DPalette::Active;
    }

#if DTK_VERSION >= DTK_VERSION_CHECK(5, 5, 10, 0)
    DPaletteHelper *dAppHelper = DPaletteHelper::instance();
#else
    DApplicationHelper *dAppHelper = DApplicationHelper::instance();
#endif
    DPalette palette = dAppHelper->palette(this);

    DStyle *style = dynamic_cast<DStyle *>(DApplication::style());

    QStyleOptionHeader option;
    initStyleOption(&option);
    int margin = style->pixelMetric(DStyle::PM_ContentsMargins, &option);

    // title
    QRect contentRect(rect.x(), rect.y(), rect.width(), rect.height() - m_spacing);
    QRect hSpacingRect(rect.x(), contentRect.height(), rect.width(),
                       rect.height() - contentRect.height());

    QBrush contentBrush(palette.color(cg, DPalette::Base));
    // horizontal spacing
    QBrush hSpacingBrush(palette.color(cg, DPalette::FrameBorder));

    QBrush vSpacingBrush(palette.color(cg, DPalette::FrameBorder));
    QRectF vSpacingRect(rect.x(), rect.y() + kSpacingMargin, m_spacing,
                        rect.height() - kSpacingMargin * 2);
    QBrush clearBrush(palette.color(cg, DPalette::Window));

    painter->fillRect(hSpacingRect, clearBrush);
    painter->fillRect(hSpacingRect, hSpacingBrush);

    if (visualIndex(logicalIndex) > 0) {
        painter->fillRect(vSpacingRect, clearBrush);
        painter->fillRect(vSpacingRect, vSpacingBrush);
    }

    QPen foreground;
    auto type = DGuiApplicationHelper::instance()->themeType();
    foreground.setColor(palette.color(cg, DPalette::Text));
    if (opt.state == QStyle::State_Enabled && m_pressed == logicalIndex) {
        foreground = opt.palette.highlight().color();
    } else if (opt.state == QStyle::State_Enabled && m_hover == logicalIndex) {
        foreground = style->adjustColor(foreground.color(), 0, 0, type == DGuiApplicationHelper::DarkType ? 20 : -50);
    }

    QRect textRect;
    if (sortIndicatorSection() == logicalIndex) {
        textRect = {
            contentRect.x() + margin,
            contentRect.y(),
            contentRect.width() - margin * 3 - kDropDownSize.width(),
            contentRect.height()};
    } else {
        textRect = {
            contentRect.x() + margin,
            contentRect.y(),
            contentRect.width() - margin,
            contentRect.height()};
    }
    QString title = model()->headerData(logicalIndex, orientation(), Qt::DisplayRole).toString();
    int align = model()->headerData(logicalIndex, orientation(), Qt::TextAlignmentRole).toInt();
    // foreground
    painter->setPen(foreground);
    QString objName = model()->objectName();
    if (0 == logicalIndex && objName == "ID_UserDataBackupSelectWidget") { // 绘制全选checkbox
        const int checkboxWidth = 20;
        const int checkboxHight = 20;
        const int xOffset = 10;
        const int yOffset = 10;
        static QPixmap uncheckedNormalLightPix = Utils::hidpiPixmap(":/resources/icons/UnChecked-Normal-Light.svg", QSize(checkboxWidth, checkboxHight));
        static QPixmap checkedNormalLightPix = Utils::hidpiPixmap(":/resources/icons/Checked-Normal-Light.svg", QSize(checkboxWidth, checkboxHight));
        static QPixmap mixCheckedNormalLightPix = Utils::hidpiPixmap(":/resources/icons/Mixed-Normal-Light.svg", QSize(checkboxWidth, checkboxHight));
        int x = textRect.x() + xOffset;
        if (m_checkAll == Qt::Checked) {
            painter->drawPixmap(x, textRect.y() + yOffset, checkboxWidth, checkboxHight, checkedNormalLightPix);
        } else if (m_checkAll == Qt::PartiallyChecked){
            painter->drawPixmap(x, textRect.y() + yOffset, checkboxWidth, checkboxHight, mixCheckedNormalLightPix);
        } else {
            painter->drawPixmap(x, textRect.y() + yOffset, checkboxWidth, checkboxHight, uncheckedNormalLightPix);
        }
        painter->drawText(textRect.x() + 35, textRect.y() + 25, title); // 调整 Name 的偏移量
    } else {
        if (1 == logicalIndex && objName == "ID_UserDataBackupSelectWidget") {
            textRect.setX(textRect.x() - 5);
            textRect.setY(textRect.y() + 5);
            painter->drawText(textRect, align, title);
        } else {
            painter->drawText(textRect, align, title);
        }
    }

    // sort indicator
    if (isSortIndicatorShown() && logicalIndex == sortIndicatorSection()) {
        QRect sortIndicator(textRect.x() + textRect.width() + margin,
                            textRect.y() + qCeil((textRect.height() - kDropDownSize.height()) / 2.),
                            kDropDownSize.width(), kDropDownSize.height());
        option.rect = sortIndicator;
        if (sortIndicatorOrder() == Qt::DescendingOrder) {
            style->drawPrimitive(DStyle::PE_IndicatorArrowDown, &option, painter, this);
        } else if (sortIndicatorOrder() == Qt::AscendingOrder) {
            style->drawPrimitive(DStyle::PE_IndicatorArrowUp, &option, painter, this);
        }
    }

    painter->restore();
}

void BaseHeaderView::setCheckAllState(Qt::CheckState checkState)
{
    if (m_checkAll != checkState) {
        m_checkAll = checkState;
        update();
    }
}

void BaseHeaderView::mousePressEvent(QMouseEvent *e)
{
    // 处理全选checkbox, Qt::PartiallyChecked 是通过setCheckAllState里去更新
    if (e->button() == Qt::LeftButton && e->type() == QEvent::MouseButtonPress) {
        QRect rect = viewport()->rect();
        QRectF subRect(rect.x() + 20, rect.y() + 10, 20, 20);
        QPoint point = e->pos();
        if (subRect.contains(point)) {
            if (m_checkAll == Qt::Unchecked || m_checkAll == Qt::PartiallyChecked) {
                m_checkAll = Qt::Checked;
            } else {
                m_checkAll = Qt::Unchecked;
            }

            Q_EMIT checkAllChanged(m_checkAll);
        }
    }

    return DHeaderView::mousePressEvent(e);
}

bool BaseHeaderView::eventFilter(QObject *obj, QEvent *ev)
{
    if (ev->type() == QEvent::KeyPress) {
        QKeyEvent *kev = dynamic_cast<QKeyEvent *>(ev);
        if (kev->key() == Qt::Key_Space ||
                kev->key() == Qt::Key_Up ||
                kev->key() == Qt::Key_Down) {
            return true;
        }
    }
    return DHeaderView::eventFilter(obj, ev);
}

bool BaseHeaderView::viewportEvent(QEvent *event)
{
    // #ref: QTreeView::event
    switch (event->type()) {
    case QEvent::HoverEnter: {
        auto *he = dynamic_cast<QHoverEvent *>(event);
        m_hover = logicalIndexAt(he->pos());
        if (m_hover != -1)
            updateSection(m_hover);
        break;
    }
    case QEvent::Leave:
    case QEvent::HoverLeave: {
        if (m_hover != -1)
            updateSection(m_hover);
        m_hover = -1;
        break;
    }
    case QEvent::HoverMove: {
        auto *he = dynamic_cast<QHoverEvent *>(event);
        int oldHover = m_hover;
        m_hover = logicalIndexAt(he->pos());
        if (m_hover != oldHover) {
            if (oldHover != -1)
                updateSection(oldHover);
            if (m_hover != -1)
                updateSection(m_hover);
        }
        break;
    }
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
    case QEvent::MouseButtonPress: {
        auto *mev = dynamic_cast<QMouseEvent *>(event);
        m_pressed = logicalIndexAt(mev->pos());
        m_pressed = (mev->button() == Qt::LeftButton && (mev->type() == QEvent::MouseButtonPress || mev->type() == QEvent::MouseButtonDblClick)) ? m_pressed : -1;
        update();
        break;
    }
    default:
        break;
    }
    return DHeaderView::viewportEvent(event);
}
