#pragma once
#include <QObject>
#include <QPointF>
#include <QRect>
#include <QRegion>
#include <QColor>
#include <QString>
#include <QVector>
#include <QImage>
#include <QTimer>
#include <QElapsedTimer>

class QPainter;

// Draws ink strokes DIRECTLY into the e-paper framebuffer (captured via
// FbCapture) and refreshes each segment with an explicit EPFramebuffer swap,
// which renders solid/single-pass — the reMarkable fast-pen path. No Qt scene
// graph in the ink loop. Driven by PenDevice signals. Also owns the top-bar
// button visuals/hit-testing so both finger and stylus can drive them.
class InkEngine : public QObject {
    Q_OBJECT
public:
    explicit InkEngine(QObject *parent = nullptr);

    void setInkColor(const QColor &c) { m_color = c; }
    void setWidthRange(qreal minPx, qreal maxPx) { m_minWidth = minPx; m_maxWidth = maxPx; }
    void setPenType(int t) { m_penType = t; }

    // QML registers each top-bar button's geometry + label (single source of
    // truth): used to exclude ink, redraw on clear, and detect stylus taps.
    Q_INVOKABLE void registerButton(int x, int y, int w, int h, const QString &text);
    // Show a button's pressed/normal state directly in the framebuffer (single
    // clean swap, no scene flashing). Called from QML on finger press.
    Q_INVOKABLE void flashButton(int x, int y, int w, int h, const QString &text, bool pressed);
    // Run a button's action: Clear here, Back via the backRequested() signal.
    Q_INVOKABLE void activateButton(const QString &text);

    // Finger-wipe erase (finger touches arrive via QML; the stylus does not).
    // Erases ink along the finger path; a tap (no movement) erases nothing.
    Q_INVOKABLE void eraseStart(qreal x, qreal y);
    Q_INVOKABLE void eraseMove(qreal x, qreal y);
    Q_INVOKABLE void eraseEnd();

    // Two-finger vertical scroll. dyScreen = centroid movement since last call
    // (natural: content follows the fingers). Grows the canvas downward as needed.
    Q_INVOKABLE void panBy(qreal dyScreen);
    // Called when the scroll gesture ends: one full-refresh pass to clear the
    // ghosting that the fast per-step swaps leave on the color panel.
    Q_INVOKABLE void panEnd();

signals:
    void backRequested();

public slots:
    void strokeStart(QPointF p);
    void strokeMove(QPointF p, qreal pressure, qreal tiltX, qreal tiltY, bool eraser);
    void strokeEnd();
    void clearPage();

private:
    struct ButtonDef { QRect rect; QString text; };

    void pushRect(const QRect &r);          // fast (Pen waveform) — ink strokes
    void fullRefresh();                     // ghostControl() de-ghost (scroll/clear)
    void paintButton(QPainter &p, const QRect &rect, const QString &text, bool pressed) const;
    int buttonAt(const QPointF &p) const; // index into m_buttons, or -1

    void ensureCanvas();                    // lazily create the virtual canvas
    void growIfNeeded(int canvasBottomY);   // extend the canvas downward
    void blitRegion(const QRect &screenRect, bool full = false); // canvas -> framebuffer
    void renderAll(bool full = false);      // re-blit the whole viewport (on pan)

    QPointF m_last;
    bool m_drawing = false;
    QColor m_color = Qt::black;
    qreal m_minWidth = 2.0;     // hairline (px)
    qreal m_maxWidth = 14.0;    // size's max (px), from DPI + size category
    int m_penType = 2;          // reMarkable tool id (21 = Calligraphy)
    QRegion m_uiMask;           // button regions excluded from ink/clear
    QVector<ButtonDef> m_buttons;
    int m_btnDown = -1;         // button under an in-progress stylus tap, or -1
    QPointF m_erasePress;       // finger-wipe erase state
    QPointF m_eraseLast;
    bool m_erasing = false;     // true once movement passed the tap threshold

    QImage m_canvas;            // virtual document (>= screen; grows downward)
    int m_panY = 0;             // top of the viewport within the canvas
    int m_screenW = 0, m_screenH = 0;
    int m_fullSm = 4;           // screen mode for full updates (env CHATTER_FULL_SM)
    int m_flashGray = 0;        // de-ghost flash level 0=black..255 (env CHATTER_FLASH_GRAY)
    QTimer m_deghostTimer;      // debounce: de-ghost once ~1s after scrolling stops
    bool m_scrolled = false;    // any scrolling since last clear? -> Clear de-ghosts

    QElapsedTimer m_timer;
    qint64 m_lastT = 0;
};
