#pragma once

#include <QObject>
#include <QRect>
#include <QString>

class QEvent;
class QTimer;
class QWidget;
class Tooltip;

// Shows and hides one element's tooltip (REQ-UI-TOOLTIP-TRIGGER, REQ-UI-TOOLTIP-DISMISS).
// Attached to the element as an event filter, so a plain QLabel or QPushButton needs no
// subclass of its own to carry a tooltip.
//
// It replaces QWidget::setToolTip throughout the UI: Qt's own tooltip times out, hides on
// the first mouse move, cannot be hovered, and cannot be brought up by a click.
class TooltipTrigger : public QObject
{
    Q_OBJECT

public:
    // What brings the tooltip up. An element whose click already does something keeps to
    // HoverOnly, so one gesture never both acts and explains (REQ-UI-TOOLTIP-TRIGGER).
    enum class Trigger
    {
        HoverOnly,
        HoverAndClick
    };

    // Attaches a text tooltip, created and owned along with the trigger by the target.
    static void attachText(QWidget& target, const QString& text, Trigger trigger);
    // Attaches a tooltip built by the caller -- the item production tooltip, whose
    // content is drawn rather than written (REQ-UI-ITEM-TOOLTIP).
    static void attach(QWidget& target, Tooltip& tooltip, Trigger trigger);

protected:
    bool eventFilter(QObject* watched, QEvent* event) override;

private:
    TooltipTrigger(QWidget& target, Tooltip& tooltip, Trigger trigger);

    QRect getTargetGlobalRect() const;
    void  showTooltip();
    void  hideTooltip();
    // Keeps the tooltip where it was put in relation to its element, so an element that
    // moves takes its tooltip along (REQ-UI-TOOLTIP-DISMISS).
    void  followTarget();
    // Hides the tooltip once the pointer is on neither the element nor the tooltip
    // itself, which is the only thing that dismisses it (REQ-UI-TOOLTIP-DISMISS).
    void  hideUnlessPointerHeld();

    QWidget* m_target;
    Tooltip* m_tooltip;
    Trigger  m_trigger;
    // Held back so a pointer crossing the element on its way elsewhere does not flash the
    // tooltip. A click skips it (REQ-UI-TOOLTIP-TRIGGER).
    QTimer*  m_hoverTimer;
    // Runs only while the tooltip is up: it carries the tooltip along with an element
    // that moves and tests where the pointer is. Polled rather than read off events:
    // enter and leave arrive in an order that depends on which of the two widgets the
    // pointer crosses first and a popup window is not a reliable source of them, and a
    // move event reaches only the widget whose own position changed -- never a label
    // carried along by the button around it, which is the case that matters here.
    QTimer*  m_trackingTimer;
    // Where the element was when the tooltip was last placed, so the next tick can tell
    // how far it has travelled since. Only meaningful while the tooltip is up.
    QRect    m_targetGlobalRect;
};
