/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <QApplication>
#include <QDebug>
#include <QKeyEvent>
#include <QLinearGradient>
#include <QPainter>
#include <QStyleOptionViewItem>

#include "MvQTreeView.h"

#include "MvQKeyProfileModel.h"
#include "MvQTheme.h"
#include "MvWindef.h"
#include "MvQMethods.h"

//#include <iostream>


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

MvQTreeViewDelegate::MvQTreeViewDelegate(QTreeView* parent) :
    QStyledItemDelegate(parent),
    view_(parent)
{
    Q_ASSERT(view_);
    setColours();
}

void MvQTreeViewDelegate::setColours()
{
    QString group = "treeview";
    // "block" here means and item with children

    textPen_    = MvQTheme::pen(group, "text");
    subTextPen_ = MvQTheme::pen(group, "subtext");

    selectTextPen_    = MvQTheme::pen(group, "select_text");
    selectSubTextPen_ = MvQTheme::pen(group, "select_text");
    selectBrush_      = MvQTheme::brush(group, "select_brush");
    selectBrushBlock_ = selectBrush_;

    blockBrush_   = MvQTheme::brush(group, "block_brush");
    blockTextPen_ = MvQTheme::pen(group, "block_text");

    errorSelectPen_   = MvQTheme::pen(group, "error_select_pen");
    errorSelectBrush_ = MvQTheme::brush(group, "error_select_brush");
    errorBrush_       = MvQTheme::brush(group, "error_brush");
    borderPen_        = MvQTheme::pen(group, "border_pen");
}

void MvQTreeViewDelegate::setStyleId(int /*id*/)
{
}

void MvQTreeViewDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option,
                                const QModelIndex& index) const
{
    QStyleOptionViewItem vopt(option);
    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,
    // hence it has branch controls.
    bool hasChild = index.model()->hasChildren(index);
    if (!hasChild && index.column() > 0) {
        hasChild = index.model()->hasChildren(index.model()->index(index.row(), 0, index.parent()));
    }

    bool selected = option.state & QStyle::State_Selected;
    bool hasError = index.data(MvQKeyProfileModel::ErrorRole).toBool();

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

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

    //For normal items 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);
    }

    //Bg and border
    if (hasChild) {
        if (selected) {
            QRect selectRect;
            selectRect = bgRect.adjusted(0, 1, 0, 0);
            painter->fillRect(selectRect, selectBrushBlock_);
            //            painter->setPen(selectPen_);
            //            painter->drawLine(selectRect.topLeft(), selectRect.topRight());
            //            painter->drawLine(selectRect.bottomLeft(), selectRect.bottomRight());
        }
        else {
            QRect fullRect = bgRect;
            //painter->fillRect(fullRect, blockBrush_);
            //            painter->setPen(selectPen_);
            // painter->setPen(blockBorderPen_);
            fullRect.adjust(0, 1, 0, 0);
            //            painter->drawLine(fullRect.topLeft(), fullRect.topRight());
            //            painter->drawLine(fullRect.bottomLeft(), fullRect.bottomRight());
            //            if (index.column() == 0)
            //                painter->drawLine(fullRect.topLeft(), fullRect.bottomLeft());
        }
    }
    else {
        if (selected) {
            //The selection rect
            QRect selectRect;
            selectRect = bgRect.adjusted(0, 1, 0, 0);

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

            if (hasError) {
                painter->fillRect(selectRect, errorSelectBrush_);
                //painter->setPen(errorSelectPen_);
            }
            else {
                painter->fillRect(selectRect, selectBrush_);
                //painter->setPen(selectPen_);
            }
            //            painter->drawLine(selectRect.topLeft(), selectRect.topRight());
            //            painter->drawLine(selectRect.bottomLeft(), selectRect.bottomRight());
        }
        else {
            if (hasError) {
                painter->fillRect(bgRect, errorBrush_);
            }
            else {
                auto bg = index.data(Qt::BackgroundRole).value<QColor>();
                if (bg.isValid()) {
                    painter->fillRect(bgRect, bg);
                }
            }
        }
    }

    //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_);
    if (index.column() != 0)
        painter->drawLine(bgRect.topLeft(), bgRect.topRight());
    else {
        painter->drawLine(QPoint(0, bgRect.y()), bgRect.topRight());
    }

    //Display text
    QString text    = index.data(Qt::DisplayRole).toString();
    bool useSubText = index.data(MvQKeyProfileModel::SubTextRole).toBool();
    if (text.isEmpty() == false) {
        QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt, widget);
        if (hasChild)
            textRect.adjust(3, 0, 3, 0);

        QFont f = view_->font();
        QFontMetrics fm(f);

#ifndef METVIEW_ON_WINDOWS
        textRect.setWidth(MvQ::textWidth(fm, text) + 2);
#else
        textRect.setWidth(MvQ::textWidth(fm, text) * 2);
#endif

        if (textRect.right() + 1 > option.rect.right()) {
            painter->setClipRect(option.rect.adjusted(-option.rect.left(), 0, 0, 0));
        }

        QPen fg;
        if (option.state & QStyle::State_Selected) {
            fg = (useSubText) ? selectSubTextPen_ : selectTextPen_;
        }
        else {
            //            if (hasChild) {
            //                fg = blockTextPen_;
            //            } else {
            fg = (useSubText) ? subTextPen_ : textPen_;
            //}
        }

        painter->setFont(f);
        painter->setPen(fg);
        painter->drawText(textRect.adjusted(2, 0, 0, 0), Qt::AlignLeft | Qt::AlignVCenter, text);
    }

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

QSize MvQTreeViewDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QSize size = QStyledItemDelegate::sizeHint(option, index);
    size += QSize(0, 1);
    return size;
}

MvQTreeView::MvQTreeView(QWidget* parent, bool useDelegate) :
    QTreeView(parent)
{
    activatedByKeyNavigation_ = false;
    columnToDrag_             = 0;

    if (useDelegate) {
        //!!!!We need to do it because:
        //The background colour between the view's left border and the nodes cannot be
        //controlled by delegates or stylesheets. It always takes the QPalette::Highlight
        //colour from the palette. Here we set this to transparent so that Qt could leave
        //this area empty and we will fill it appropriately in our delegate.
        QPalette pal = palette();
        pal.setColor(QPalette::Highlight, QColor(128, 128, 128, 0));  //Qt::transparent);
        setPalette(pal);

        delegate_ = new MvQTreeViewDelegate(this);
        setItemDelegate(delegate_);
    }
}

void MvQTreeView::setStyleId(int id)
{
    if (delegate_) {
        delegate_->setStyleId(id);
    }
}

void MvQTreeView::keyReleaseEvent(QKeyEvent* event)
{
    if (activatedByKeyNavigation_) {
        if (event->isAutoRepeat() == false &&
            (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down ||
             event->key() == Qt::Key_PageUp || event->key() == Qt::Key_PageDown ||
             event->key() == Qt::Key_Home || event->key() == Qt::Key_End)) {
            emit activated(currentIndex());
        }
    }
    else {
        QTreeView::keyReleaseEvent(event);
    }
}

// reimplement virtual function from QTreeView - called when the selection is changed
void MvQTreeView::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
    QTreeView::selectionChanged(selected, deselected);

    QModelIndexList lst = selectedIndexes();

    if (lst.count() > 0)
        Q_EMIT selectionChanged(lst[0]);
}

void MvQTreeView::keyboardSearch(const QString& /*search*/)
{
}

bool MvQTreeView::dragEnabled()
{
    return (dragDropMode() != DropOnly && dragDropMode() != NoDragDrop);
}

bool MvQTreeView::dropEnabled()
{
    return (dragDropMode() != DragOnly && dragDropMode() != NoDragDrop);
}

//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 MvQTreeView::drawBranches(QPainter* painter, const QRect& rect, const QModelIndex& index) const
{
    if (delegate_) {
        if (index.model()->hasChildren(index) && 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

            if (selectionModel()->rowIntersectsSelection(index.row(), index.parent())) {
                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>());
                //painter->fillRect(rect.adjusted(0,1,0,0)
            }

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