/*
 * Copyright 2009- ECMWF.
 *
 * This software is licensed under the terms of the Apache Licence version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
 * In applying this licence, ECMWF does not waive the privileges and immunities
 * granted to it by virtue of its status as an intergovernmental organisation
 * nor does it submit to any jurisdiction.
 */

#include "VariableView.hpp"

#include <QApplication>
#include <QDebug>
#include <QHeaderView>
#include <QImageReader>
#include <QItemSelectionModel>
#include <QPainter>

#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    #include <QRegExp>
#else
    #include <QtCore5Compat/QRegExp>
#endif

#include "IconProvider.hpp"
#include "VParam.hpp"
#include "VariableModel.hpp"
#include "ViewerUtil.hpp"

//========================================================
//
// VariableViewDelegate
//
//========================================================

VariableDelegate::VariableDelegate(QTreeView* parent) : QStyledItemDelegate(parent), view_(parent) {
    selectPen_         = QPen(QColor(8, 117, 182));
    selectBrush_       = QBrush(QColor(65, 139, 212));
    selectBrushBlock_  = QBrush(QColor(48, 102, 178));
    borderPen_         = QPen(QColor(230, 230, 230));
    genVarPixId_       = IconProvider::add(":/viewer/genvar.svg", "genvar");
    shadowGenVarPixId_ = IconProvider::add(":/viewer/genvar_shadow.svg", "genvar_shadow");
    mirrorVarPixId_    = IconProvider::add(":/viewer/mirrorvar.svg", "mirrorvar");
}

void VariableDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QStyleOptionViewItem vopt(option);
#else
    QStyleOptionViewItemV4 vopt(option);
#endif

    initStyleOption(&vopt, index);

    const QStyle* style   = vopt.widget ? vopt.widget->style() : QApplication::style();
    const QWidget* widget = vopt.widget;

    // This indicates that the item is a parent item (node or server),
    //  hence it has branch controls.
    bool hasChild = !index.parent().isValid();

    bool selected = option.state & QStyle::State_Selected;

    // Save painter state
    painter->save();

    // The background rect
    QRect bgRect = option.rect;

    // For variables in the first column we want to extend the item
    // rect to the left for the background painting.
    if (index.column() == 0 && !hasChild) {
        bgRect.setX(0);
    }

    // Paint item highlight!! It is taken from the UserRole
    auto highCol = index.data(Qt::UserRole).value<QColor>();
    if (highCol.isValid()) {
        painter->fillRect(bgRect.adjusted(1, 1, -1, -1), highCol);
    }
    // otherwise paint item background!!
    else if (!selected) {
        // Paint the item background
        auto bg = index.data(Qt::BackgroundRole).value<QColor>();
        if (bg.isValid()) {
            painter->fillRect(bgRect, bg);
        }
        // alternating row colour?
        else {}
    }

    // Paint selection. This should be transparent.
    if (selected) {
        // The selection rect
        QRect selectRect;
        if (hasChild) {
            selectRect = bgRect.adjusted(0, 1, 0, -1);
            painter->fillRect(selectRect, selectBrushBlock_);
        }
        else {
            selectRect = bgRect.adjusted(0, 1, 0, -1);

            // For the first column we extend the selection
            // rect to left edge.
            if (index.column() == 0) {
                selectRect.setX(0);
            }
            painter->fillRect(selectRect, selectBrush_);
        }
    }

    // Render the horizontal border for rows. We only render the top border line.
    // With this technique we miss the bottom border line of the last row!!!
    painter->setPen(borderPen_);
    painter->drawLine(bgRect.topLeft(), bgRect.topRight());

    if (index.column() == 0 && !hasChild) {
        painter->drawLine(bgRect.topRight(), bgRect.bottomRight());
    }

    // Display text
    QString text   = index.data(Qt::DisplayRole).toString();
    QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt, widget);
    QFont f;
    QFontMetrics fm(f);
    textRect.setWidth(ViewerUtil::textWidth(fm, text));

    if (index.column() == 0 && !hasChild) {
        bool hasLock = false;
        QPixmap lockPix;
        QRect lockRect;
        bool hasGen    = false;
        bool hasMirror = false;
        QPixmap genPix;
        QPixmap mirrorPix;
        QRect genRect;
        QRect mirrorRect;

        textRect.setLeft(option.rect.x() - 17);
        bool locked = index.data(VariableModel::ReadOnlyRole).toBool();
        if (locked) {
            hasLock = true;
            lockPix = IconProvider::lockPixmap(textRect.height() - 6);

            lockRect = QRect(textRect.left() - 4 - lockPix.width(),
                             textRect.top() + (textRect.height() - lockPix.height()) / 2,
                             lockPix.width(),
                             lockPix.height());
        }

        if (bool gen = index.data(VariableModel::GenVarRole).toBool(); gen && genVarPixId_ >= 0) {
            int pixId = genVarPixId_;
            if (index.data(VariableModel::ShadowRole).toBool()) {
                pixId = shadowGenVarPixId_;
            }
            if (pixId >= 0) {
                hasGen = true;

                genPix  = IconProvider::pixmap(pixId, textRect.height() - 4);
                genRect = QRect(textRect.left(),
                                textRect.top() + (textRect.height() - genPix.height()) / 2,
                                genPix.width(),
                                genPix.height());

                textRect.moveLeft(genRect.right() + 4);
            }
        }

        if (bool mirror = index.data(VariableModel::MirrorRole).toBool(); mirror && mirrorVarPixId_ >= 0) {
            int pixId = mirrorVarPixId_;
            if (pixId >= 0) {
                hasMirror = true;

                mirrorPix  = IconProvider::pixmap(pixId, textRect.height() - 4);
                mirrorRect = QRect(textRect.left(),
                                   textRect.top() + (textRect.height() - mirrorPix.height()) / 2,
                                   mirrorPix.width(),
                                   mirrorPix.height());

                textRect.moveLeft(mirrorRect.right() + 4);
            }
        }

        if (textRect.right() + 1 > option.rect.right()) {
            painter->setClipRect(option.rect.adjusted(-option.rect.left(), 0, 0, 0));
        }
        QColor fg;
        if (option.state & QStyle::State_Selected) {
            fg = Qt::white;
        }
        else {
            fg = index.data(Qt::ForegroundRole).value<QColor>();
            if (!fg.isValid())
                fg = Qt::black;
        }

        painter->setPen(fg);

        painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text);

        if (hasLock)
            painter->drawPixmap(lockRect, lockPix);

        if (hasGen)
            painter->drawPixmap(genRect, genPix);

        if (hasMirror) {
            painter->drawPixmap(mirrorRect, mirrorPix);
        }
    }
    else if (index.column() == 0) {
        auto fg = index.data(Qt::ForegroundRole).value<QColor>();
        if (!fg.isValid())
            fg = Qt::black;
        painter->setPen(fg);

        QRegExp rx("^(.+)\\s(\\S+)$");
        // QRegExp rx("inherited from (\\S+) (\\S+)");
        if (rx.indexIn(text) > -1 && rx.captureCount() == 2) {
            QFont f;
            f.setPointSize(f.pointSize() - 1);
            QFontMetrics fm(f);
            QString txt1 = rx.cap(1);
            textRect.setWidth(ViewerUtil::textWidth(fm, txt1));
            painter->setFont(f);
            painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, txt1);
            textRect.setLeft(textRect.right() + ViewerUtil::textWidth(fm, "D"));
            text = rx.cap(2);
        }

        QFont fBold;
        fBold.setPointSize(fBold.pointSize() - 1);
        fBold.setBold(true);
        QFontMetrics fmBold(fBold);
        textRect.setWidth(ViewerUtil::textWidth(fmBold, text + "a"));
        painter->setFont(fBold);

        painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text);
    }
    else if (index.column() == 1) {
        textRect.adjust(2, 0, 2, 0);

        QColor fg;
        if (selected) {
            fg = Qt::white;
        }
        else {
            fg = index.data(Qt::ForegroundRole).value<QColor>();
            if (!fg.isValid())
                fg = Qt::black;
        }

        painter->setPen(fg);
        painter->drawText(textRect, Qt::AlignLeft | Qt::AlignVCenter, text);
    }

    // Restore painter state
    painter->restore();
}

QSize VariableDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
    QSize size = QStyledItemDelegate::sizeHint(option, index);

    size += QSize(0, 2);

    // We need to starcth the second column to the right edge of the viewport
    if (index.column() == 1) {
        int w1 = view_->header()->sectionSize(0);
        int w  = view_->viewport()->size().width();
        if (w1 + size.width() < w)
            size.setWidth(w - w1 + 1);
    }
    return size;
}

//========================================================
//
// VariableView
//
//========================================================

VariableView::VariableView(QWidget* parent) : TreeView(parent) {
    setProperty("var", "1");

    delegate_ = new VariableDelegate(this);
    setItemDelegate(delegate_);

    setRootIsDecorated(true);
    setAllColumnsShowFocus(true);
    setUniformRowHeights(true);
    // setAlternatingRowColors(true);
    setSortingEnabled(true);

    header()->setSortIndicator(0, Qt::AscendingOrder);

    // Context menu
    setContextMenuPolicy(Qt::ActionsContextMenu);
}

// We do not want to render the branches for the variable items. We only
// render the branch for the group items (nodes or server).
void VariableView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const {
    if (!index.parent().isValid() && index.column() == 0) {
        // We need to fill the branch area here. We cannot do it in the delegate
        // because when the delegate is called the branch control is already
        // rendered, so the delegate would just overpaint it!!!

        if (selectionModel()->rowIntersectsSelection(index.row(), QModelIndex())) {
            painter->fillRect(rect.adjusted(0, 1, 0, -1), delegate_->selectBrushBlock_);
        }
        else {
            painter->fillRect(rect.adjusted(0, 1, 0, 0), index.data(Qt::BackgroundRole).value<QColor>());
        }

        // Draw the branch with the default method
        QTreeView::drawBranches(painter, rect, index);
    }
}
