//  Copyright © 2017  B. Clausius <barcc@gmx.de>
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

// some code in class ModelSelectionItemDelegate is derived from class QCommonStyle,
// part of the QtWidgets module of the Qt Toolkit (v5.6.1):
// Original Copyright: (C) 2015 The Qt Company Ltd.
// Original license: General Public License version 2.1 or version 3
//                   as published by the Free Software Foundation

#ifndef QTUI_P_H
#define QTUI_P_H

#include <QtGui/QPainter>
#include <QtGui/QTextLayout>
#include <QtWidgets/QStyledItemDelegate>
#include <QtWidgets/QListWidget>


static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth)
{
    qreal height = 0;
    qreal widthUsed = 0;
    textLayout.beginLayout();
    while (true) {
        QTextLine line = textLayout.createLine();
        if (!line.isValid())
            break;
        line.setLineWidth(lineWidth);
        line.setPosition(QPointF(0, height));
        height += line.height();
        widthUsed = qMax(widthUsed, line.naturalTextWidth());
    }
    textLayout.endLayout();
    return QSizeF(widthUsed, height);
}

class ModelSelectionItemDelegate : public QStyledItemDelegate
{
    public:
        ModelSelectionItemDelegate() : QStyledItemDelegate() {}
        //~ModelSelectionItemDelegate();
        
        QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const {
            return QSize(128, 128);
        }
        void _draw_text_background(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const {
            QPalette::ColorGroup cg = option->widget->isEnabled()
                                      ? QPalette::Normal : QPalette::Disabled;
            if (cg == QPalette::Normal && !(option->state & QStyle::State_Active))
                cg = QPalette::Inactive;
                
            QPalette::ColorRole crole = option->state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Dark;
            QColor color(option->palette.brush(cg, crole).color());
            color = color.darker(180);
            color.setAlpha(160);
            p->fillRect(rect, color);
        }
        
        void _draw_text(QPainter *p, const QStyleOptionViewItem *option, const QRect &rect) const {
            const int textMargin = option->widget->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, option->widget) + 1;
            
            QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
            const bool wrapText = option->features & QStyleOptionViewItem::WrapText;
            QTextOption textOption;
            textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
            textOption.setTextDirection(option->direction);
            textOption.setAlignment(QStyle::visualAlignment(option->direction, option->displayAlignment));
            QTextLayout textLayout(option->text, option->font);
            textLayout.setTextOption(textOption);
            
            QSizeF minsize(viewItemTextLayout(textLayout, textRect.width()));
            
            QString elidedText;
            qreal height = 0;
            qreal width = 0;
            int elidedIndex = -1;
            const int lineCount = textLayout.lineCount();
            for (int j = 0; j < lineCount; ++j) {
                const QTextLine line = textLayout.lineAt(j);
                if (j + 1 <= lineCount - 1) {
                    const QTextLine nextLine = textLayout.lineAt(j + 1);
                    if ((nextLine.y() + nextLine.height()) > textRect.height()) {
                        int start = line.textStart();
                        int length = line.textLength() + nextLine.textLength();
                        const QFontMetrics fontmetrics(option->font);
                        elidedText = fontmetrics.elidedText(textLayout.text().mid(start, length), option->textElideMode, textRect.width());
                        height += line.height();
                        width = textRect.width();
                        elidedIndex = j;
                        break;
                    }
                }
                if (line.naturalTextWidth() > textRect.width()) {
                    int start = line.textStart();
                    int length = line.textLength();
                    const QFontMetrics fontmetrics(option->font);
                    elidedText = fontmetrics.elidedText(textLayout.text().mid(start, length), option->textElideMode, textRect.width());
                    height += line.height();
                    width = textRect.width();
                    elidedIndex = j;
                    break;
                }
                width = qMax<qreal>(width, line.width());
                height += line.height();
            }
            
            const QRect layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment,
                                                         QSize(int(width), int(height)), textRect);
            _draw_text_background(p, option, QRect(layoutRect.x()+layoutRect.width()/2-minsize.width()/2-2, layoutRect.y(),
                                                    minsize.width()+5, layoutRect.height()+1));
            const QPointF position = layoutRect.topLeft();
            for (int i = 0; i < lineCount; ++i) {
                const QTextLine line = textLayout.lineAt(i);
                if (i == elidedIndex) {
                    qreal x = position.x() + line.x();
                    qreal y = position.y() + line.y() + line.ascent();
                    p->save();
                    p->setFont(option->font);
                    p->drawText(QPointF(x, y), elidedText);
                    p->restore();
                    break;
                }
                line.draw(p, position);
            }
        }
        
        
        void paint(QPainter *p, const QStyleOptionViewItem &opt, const QModelIndex &index) const {
            QStyleOptionViewItem option = opt;
            initStyleOption(&option, index);
            
            p->save();
            p->setClipRect(option.rect);
            
            QRect iconRect = option.rect.adjusted(3, 4, -3, -6);
            QRect textRect = option.rect.adjusted(5, 6, -5, -8);
            
            // draw the background
            option.widget->style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, p, option.widget);
            
            // draw the icon
            QIcon::Mode mode = QIcon::Normal;
            if (!(option.state & QStyle::State_Enabled))
                mode = QIcon::Disabled;
            else if (option.state & QStyle::State_Selected)
                mode = QIcon::Selected;
            QIcon::State state = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off;
            option.icon.paint(p, iconRect, option.decorationAlignment, mode, state);
            
            // draw the text
            if (!option.text.isEmpty()) {
                QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
                                      ? QPalette::Normal : QPalette::Disabled;
                if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
                    cg = QPalette::Inactive;
                    
                if (option.state & QStyle::State_Selected) {
                    p->setPen(option.palette.color(cg, QPalette::HighlightedText));
                } else {
                    p->setPen(option.palette.color(cg, QPalette::BrightText));
                }
                _draw_text(p, &option, textRect);
            }
            
            // draw the focus rect
            if (option.state & QStyle::State_HasFocus) {
                QStyleOptionFocusRect o;
                o.QStyleOption::operator=(option);
                o.rect = QRect(option.rect);
                o.state |= QStyle::State_KeyboardFocusChange;
                o.state |= QStyle::State_Item;
                QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
                              ? QPalette::Normal : QPalette::Disabled;
                o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
                                             ? QPalette::Highlight : QPalette::Window);
                option.widget->style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, p, option.widget);
            }
            
            p->restore();
        }
};

class ModelSelectionWidget : public QListWidget
{
public:
    ModelSelectionWidget(QWidget *p) : QListWidget(p) {}
    
protected:
    void mouseDoubleClickEvent(QMouseEvent *event) {
        event->ignore();
    }
    QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, const QEvent *event = Q_NULLPTR) const {
        if (dynamic_cast<const QMouseEvent*>(event)) {
            return QItemSelectionModel::NoUpdate;
        } else
            return QListWidget::selectionCommand(index, event);
    }
};

#endif // QTUI_P_H

