#ifndef oxygensimulator_h
#define oxygensimulator_h

//////////////////////////////////////////////////////////////////////////////
// oxygensimulator.h
// simulates event chain passed to the application
// -------------------
//
// SPDX-FileCopyrightText: 2010 Hugo Pereira Da Costa <hugo.pereira@free.fr>
//
// SPDX-License-Identifier: MIT
//////////////////////////////////////////////////////////////////////////////

#include "../oxygen.h"

#include <KLocalizedString>
#include <QAbstractButton>
#include <QTabBar>
#include <QTabWidget>
#include <QWidget>

#include <QBasicTimer>
#include <QEvent>
#include <QList>
#include <QObject>
#include <QTimerEvent>

namespace Oxygen
{
class Simulator : public QObject
{
    Q_OBJECT

public:
    //* constructor
    explicit Simulator(QObject *parent)
        : QObject(parent)
        , _previousPosition(-1, -1)
    {
    }

    //*@name high level interface
    //@{

    //* click on button
    void click(QWidget *receiver, int delay = -1);

    //* click on button
    void click(QWidget *, const QPoint &, int = -1);

    //* slide
    void slide(QWidget *receiver, const QPoint &delta, int delay = -1);

    //* select item
    void selectItem(QWidget *, int row, int column = 0, int = -1);

    //* select combobox item
    void selectComboBoxItem(QWidget *, int, int = -1);

    //* select menu item
    void selectMenuItem(QWidget *, int, int = -1);

    //* select tab in tabwidget
    void selectTab(QTabWidget *, int, int = -1);

    //* select tab in tabbar
    void selectTab(QTabBar *, int, int = -1);

    //* write sample text
    void writeSampleText(QWidget *widget, int delay = -1)
    {
        writeText(widget, i18n("This is a sample text"), delay);
    }

    //* write string
    void writeText(QWidget *, QString, int = -1);

    //* clear text
    void clearText(QWidget *, int = -1);

    //* delay
    void wait(int delay);

    //@}

    //* true if aborted
    bool aborted(void) const
    {
        return _aborted;
    }

    //* run stored events
    void run(void);

    //* gab mouse
    static bool grabMouse(void)
    {
        return _grabMouse;
    }

    //* mouse grab
    static void setGrabMouse(bool value)
    {
        _grabMouse = value;
    }

    //* default delay
    static void setDefaultDelay(int value)
    {
        _defaultDelay = value;
    }

Q_SIGNALS:

    //* emitted when simulator starts and stops
    void stateChanged(bool);

public Q_SLOTS:

    //* abort simulations
    void abort(void);

protected:
    //* timer event
    void timerEvent(QTimerEvent *) override;

private:
    //*@name low level interface
    //@{

    //* enter widget
    bool enter(QWidget *receiver, int delay = -1)
    {
        return enter(receiver, receiver->rect().center(), delay);
    }

    //* enter receiver
    bool enter(QWidget *, const QPoint &, int = -1);

    //* mouse click event
    void postMouseClickEvent(QWidget *widget)
    {
        postMouseClickEvent(widget, Qt::LeftButton, widget->rect().center());
    }

    //* mouse click event
    void postMouseClickEvent(QWidget *, Qt::MouseButton, const QPoint &);

    //* 'basic' event
    void postEvent(QWidget *, QEvent::Type) const;

    //* hover
    void postHoverEvent(QWidget *, QEvent::Type, const QPoint &, const QPoint &) const;

    //* mouse event
    void
    postMouseEvent(QWidget *, QEvent::Type, Qt::MouseButton, const QPoint &, Qt::MouseButtons = Qt::NoButton, Qt::KeyboardModifiers = Qt::NoModifier) const;

    //* key event
    void postKeyClickEvent(QWidget *, Qt::Key, QString, Qt::KeyboardModifiers = Qt::NoModifier) const;

    //* key event
    void postKeyModifiersEvent(QWidget *, QEvent::Type, Qt::KeyboardModifiers) const;

    //* key event
    void postKeyEvent(QWidget *, QEvent::Type, Qt::Key, QString, Qt::KeyboardModifiers = Qt::NoModifier) const;

    //* delay
    void postDelay(int);

    //* set focus to widget
    void setFocus(QWidget *);

    //* move cursor
    void moveCursor(const QPoint &, int steps = 10);
    //@}

    using WidgetPointer = WeakPointer<QWidget>;

    //* event
    class Event
    {
    public:
        enum Type { Wait, Click, Slide, SelectItem, SelectComboBoxItem, SelectMenuItem, SelectTab, WriteText, ClearText };

        //* constructor
        Event(Type type, QWidget *receiver, int delay = 0)
            : _type(type)
            , _receiver(receiver)
            , _delay(delay)
        {
        }

        Type _type;
        WidgetPointer _receiver;
        QPoint _position;
        QString _text;
        int _delay = 0;
    };

    //* process event
    void processEvent(const Event &);

    //* process Qt event
    void postQEvent(QWidget *, QEvent *) const;

    //* convert QChar to key
    Qt::Key toKey(QChar) const;

    //* list of events
    using EventList = QList<Event>;
    EventList _events;

    //* previous position in global coordinates
    /** this is needed to have proper handling of enter/leave/hover events */
    QPoint _previousPosition;

    //* previous widget
    WidgetPointer _previousWidget;

    //* basic timer, for wait
    QBasicTimer _timer;

    //* pending events timer
    QBasicTimer _pendingEventsTimer;

    //* pending event
    WidgetPointer _pendingWidget;
    QList<QEvent *> _pendingEvents;

    //* true when simulations must be aborted
    bool _aborted = false;

    //* true if simulations also grab mouse
    static bool _grabMouse;

    //* default delay
    static int _defaultDelay;
};
}

#endif
