/*****************************************************************************
 *
 * Testmanager - Graphical Automation and Visualisation Tool
 *
 * Copyright (C) 2019  Florian Pose <fp@igh.de>
 *
 * This file is part of Testmanager.
 *
 * Testmanager 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.
 *
 * Testmanager 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 Testmanager. If not, see <http://www.gnu.org/licenses/>.
 *
 ****************************************************************************/

#include "SourceDelegate.h"

#include "DataNode.h"
#include "DataModel.h"
#include "TooltipMatrix.h"

#include <QHelpEvent>
#include <QApplication>
#include <QDesktopWidget>
#include <QAbstractItemView>

#include <QHBoxLayout>
#include <QLabel>

#include <QDebug>

/****************************************************************************/

class VariableToolTip:
    public QFrame
{
    public:
        VariableToolTip(QWidget *w);
        ~VariableToolTip();

        static void show(const QPoint &, QWidget *, DataNode *);
        static void hide();

        void update(const QPoint &, QWidget *, DataNode *);

    private:
        static VariableToolTip *instance;
        QVBoxLayout verticalLayout;
        QHBoxLayout horizontalLayout;
        QLabel labelIcon;
        QLabel labelText;
        TooltipMatrix matrix;
        DataNode *dataNode;

        bool eventFilter(QObject *, QEvent *) override;
        void hideTip();
        void placeTip(const QPoint &pos, QWidget *w);
        void setDataNode(DataNode *);
        bool changed(QWidget *, DataNode *);
};

/****************************************************************************/

VariableToolTip *VariableToolTip::instance = 0;

/****************************************************************************/

VariableToolTip::VariableToolTip(
        QWidget *w
        ):
    QFrame(w, Qt::ToolTip | Qt::BypassGraphicsProxyWidget),
    verticalLayout(this),
    horizontalLayout(),
    labelIcon(this),
    labelText(this),
    matrix(this),
    dataNode(nullptr)
{
    delete instance;
    instance = this;
    qApp->installEventFilter(this);
    setMouseTracking(true);
    setFrameShape(QFrame::Box);

    verticalLayout.setContentsMargins(0, 0, 0, 0);
    verticalLayout.addLayout(&horizontalLayout);

    horizontalLayout.setContentsMargins(4, 4, 4, 4);
    horizontalLayout.addWidget(&labelIcon);
    horizontalLayout.addWidget(&labelText, 1);

    verticalLayout.addWidget(&matrix);
}

/****************************************************************************/

VariableToolTip::~VariableToolTip()
{
    if (instance == this) {
        instance = nullptr;
    }
}

/****************************************************************************/

void VariableToolTip::show(const QPoint &pos, QWidget *w, DataNode *dataNode)
{
    if (not instance or instance->changed(w, dataNode)) {
        new VariableToolTip(w);
    }

    instance->update(pos, w, dataNode);
}

/****************************************************************************/

void VariableToolTip::hide()
{
    if (instance) {
        instance->hideTip();
    }
}

/****************************************************************************/

void VariableToolTip::update(const QPoint &pos, QWidget *w, DataNode *node)
{
    if (not isVisible()) {
        showNormal();
    }

    placeTip(pos, w);
    setDataNode(node);
}

/****************************************************************************/

bool VariableToolTip::eventFilter(QObject *, QEvent *e)
{
    switch (e->type()) {
        case QEvent::Leave:
        case QEvent::WindowActivate:
        case QEvent::WindowDeactivate:
        case QEvent::FocusIn:
        case QEvent::FocusOut:
        case QEvent::Close:
        case QEvent::MouseButtonPress:
        case QEvent::MouseButtonRelease:
        case QEvent::MouseButtonDblClick:
        case QEvent::Wheel:
            hideTip();
            break;

        default:
            break;
    }

    return false;
}

/****************************************************************************/

void VariableToolTip::hideTip()
{
    instance = nullptr;
    deleteLater();
}

/****************************************************************************/

void VariableToolTip::placeTip(const QPoint &pos, QWidget *w)
{
    QRect screen = QApplication::desktop()->availableGeometry(w);

    QPoint p = pos;
    p += QPoint(2, 16);
    if (p.x() + this->width() > screen.x() + screen.width())
        p.rx() -= 4 + this->width();
    if (p.y() + this->height() > screen.y() + screen.height())
        p.ry() -= 24 + this->height();
    if (p.y() < screen.y())
        p.setY(screen.y());
    if (p.x() + this->width() > screen.x() + screen.width())
        p.setX(screen.x() + screen.width() - this->width());
    if (p.x() < screen.x())
        p.setX(screen.x());
    if (p.y() + this->height() > screen.y() + screen.height())
        p.setY(screen.y() + screen.height() - this->height());
    this->move(p);
}

/****************************************************************************/

void VariableToolTip::setDataNode(DataNode *node)
{
    if (dataNode == node) {
        return;
    }

    dataNode = node;
    labelIcon.setPixmap(dataNode->getIcon().pixmap(QSize(48, 48)));
    labelText.setText(dataNode->nodeData(Qt::ToolTipRole, 0).toString());

    if (not node->getVariable()) {
        return;
    }

    double period(0.0);

    if (dataNode->getPeriod() > 0.0
            and dataNode->getSubscriptionMode()
            != DataNode::SubscriptionEvent) {
        period = 0.2;
    }

    matrix.setVariable(node->getVariable(), period);
}

/****************************************************************************/

bool VariableToolTip::changed(QWidget *w, DataNode *node)
{
    return parent() != w or dataNode != node;
}

/*****************************************************************************
 * SourceDelegate
 ****************************************************************************/

SourceDelegate::SourceDelegate(DataModel *dataModel):
    QStyledItemDelegate(),
    dataModel(dataModel)
{
}

/****************************************************************************/

bool SourceDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view,
        const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (event->type() == QEvent::ToolTip) {
        DataNode *dataNode(dataModel->getDataNode(index));
        if (dataNode->getVariable()) {
            VariableToolTip::show(event->globalPos(), view, dataNode);
        }
        else {
            VariableToolTip::hide();
        }
        return true;
    }

    return QStyledItemDelegate::helpEvent(event, view, option, index);
}

/****************************************************************************/
