// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once


#include "common/clipboardmode.h"
#include "common/command.h"
#include "common/clientsocket.h"
#include "gui/clipboardbrowser.h"
#include "gui/notificationbutton.h"
#include "gui/notification.h"

#include <QList>
#include <QMetaObject>
#include <QObject>
#include <QPersistentModelIndex>
#include <QPoint>
#include <QRect>
#include <QVariant>
#include <QVector>

class ClipboardBrowser;
class MainWindow;
class QEventLoop;
class QPersistentModelIndex;
class QPixmap;

struct NamedValue {
    NamedValue() {}
    NamedValue(const QString &name, const QVariant &value) : name(name), value(value) {}
    QString name;
    QVariant value;
};

struct VariantMapList {
    QVector<QVariantMap> items;
};

struct NamedValueList {
    QList<NamedValue> items;
};

struct NotificationButtonList {
    QList<NotificationButton> items;
};

struct KeyboardModifierList {
    Qt::KeyboardModifiers items;
};

struct ItemSelection {
    QPointer<ClipboardBrowser> browser;
    QList<QPersistentModelIndex> indexes;
};

struct MessageData {
    QString title;
    QString message;
    int timeoutMs = -1;
    QString icon;
    QString notificationId;
    NotificationButtonList buttons;
    Notification::Urgency urgency = Notification::Urgency::Default;
    Notification::Persistency persistency = Notification::Persistency::Default;
};

Q_DECLARE_METATYPE(NamedValueList)
Q_DECLARE_METATYPE(NotificationButtonList)
Q_DECLARE_METATYPE(VariantMapList)
Q_DECLARE_METATYPE(KeyboardModifierList)
Q_DECLARE_METATYPE(MessageData)

QDataStream &operator<<(QDataStream &out, const NotificationButtonList &list);
QDataStream &operator>>(QDataStream &in, NotificationButtonList &list);
QDataStream &operator<<(QDataStream &out, const NamedValueList &list);
QDataStream &operator>>(QDataStream &in, NamedValueList &list);
QDataStream &operator<<(QDataStream &out, const VariantMapList &list);
QDataStream &operator>>(QDataStream &in, VariantMapList &list);
QDataStream &operator<<(QDataStream &out, ClipboardMode mode);
QDataStream &operator>>(QDataStream &in, ClipboardMode &mode);
QDataStream &operator<<(QDataStream &out, KeyboardModifierList value);
QDataStream &operator>>(QDataStream &in, KeyboardModifierList &value);
QDataStream &operator<<(QDataStream &out, const MessageData &value);
QDataStream &operator>>(QDataStream &in, MessageData &value);

class ScriptableProxy final : public QObject
{
    Q_OBJECT

public:
    explicit ScriptableProxy(MainWindow* mainWindow, QObject *parent = nullptr);

    void callFunction(const QByteArray &serializedFunctionCall);

    int actionId() const { return m_actionId; }
    ClientSocketId clientSocketId() const { return m_clientSocketId; }
    void setClientSocketId(ClientSocketId id) { m_clientSocketId = id; }

    void setFunctionCallReturnValue(const QByteArray &bytes);
    void setInputDialogResult(const QByteArray &bytes);

    void safeDeleteLater();

    void disconnectClient();
    void abortEvaluation();

public slots:
    QVariantMap getActionData(int id);
    void setActionData(int id, const QVariantMap &data);

    void exit();

    void close();
    bool showWindow();
    bool showWindowAt(QRect rect);
    bool pasteToCurrentWindow();
    bool copyFromCurrentWindow();

    bool focusPrevious();

    bool isMonitoringEnabled();
    bool isMainWindowVisible();
    bool isMainWindowFocused();
    bool preview(const QVariant &arg);
    void disableMonitoring(bool arg1);
    void setClipboard(const QVariantMap &data, ClipboardMode mode);
    bool registerClipboardProviderAction(int actionId, ClipboardMode mode);

    QString renameTab(const QString &arg1, const QString &arg2);

    QString removeTab(const QString &arg1);

    QString tabIcon(const QString &tabName);
    void setTabIcon(const QString &tabName, const QString &icon);

    QStringList unloadTabs(const QStringList &tabs);
    void forceUnloadTabs(const QStringList &tabs);

    bool showBrowser(const QString &tabName);
    bool showBrowserAt(const QString &tabName, QRect rect);

    void action(const QVariantMap &arg1, const Command &arg2);

    void runInternalAction(const QVariantMap &data, const QString &command);

    void showMessage(const MessageData &messageData);

    QString playSound(const QString &filePath, float volume);

    QVariantMap nextItem(const QString &tabName, int where);
    void browserMoveToClipboard(const QString &tabName, int row);
    void browserSetCurrent(const QString &tabName, int arg1);
    QString browserRemoveRows(const QString &tabName, QVector<int> rows);
    void browserMoveSelected(int targetRow, const QString &tabName);

    void browserEditRow(const QString &tabName, int arg1, const QString &format);
    void browserEditNew(const QString &tabName, const QString &format, const QByteArray &content, bool changeClipboard);

    QStringList tabs();
    bool toggleVisible();
    bool toggleMenu(const QString &tabName, int maxItemCount, QPoint position);
    bool toggleCurrentMenu();
    int findTabIndex(const QString &arg1);

    int menuItems(const VariantMapList &items);

    void openActionDialog(const QVariantMap &arg1);

    bool loadTab(const QString &fileName);
    bool saveTab(const QString &tabName, const QString &fileName);

    bool importData(const QString &fileName);
    bool exportData(const QString &fileName);

    QVariant config(const QVariantList &nameValue);
    QString configDescription();
    QVariant toggleConfig(const QString &optionName);

    int browserLength(const QString &tabName);
    bool browserOpenEditor(
        const QString &tabName, int row, const QString &format, const QByteArray &content, bool changeClipboard);

    QString browserInsert(const QString &tabName, int row, const VariantMapList &items);
    QString browserChange(const QString &tabName, int row, const VariantMapList &items);

    QByteArray browserItemData(const QString &tabName, int arg1, const QString &arg2);
    QVariantMap browserItemData(const QString &tabName, int arg1);

    void setCurrentTab(const QString &tabName);

    QString tab(const QString &tabName);

    int currentItem(const QString &tabName);
    bool selectItems(const QString &tabName, const QVector<int> &rows);

    QVector<int> selectedItems(const QString &tabName);
    QString selectedTab();

    QVariantMap selectedItemData(int selectedIndex, const QString &tabName);
    bool setSelectedItemData(int selectedIndex, const QVariantMap &data, const QString &tabName);

    VariantMapList selectedItemsData(const QString &tabName);
    void setSelectedItemsData(const VariantMapList &dataList, const QString &tabName);

    int createSelection(const QString &tabName);
    int selectionCopy(int id);
    void destroySelection(int id);
    void selectionRemoveAll(int id);
    void selectionSelectRemovable(int id);
    void selectionInvert(int id);
    void selectionSelectAll(int id);
    void selectionSelect(int id, const QVariant &maybeRe, const QString &mimeFormat);
    void selectionDeselectIndexes(int id, const QVector<int> &indexes);
    void selectionDeselectSelection(int id, int toDeselectId);
    void selectionGetCurrent(int id);
    int selectionGetSize(int id);
    QString selectionGetTabName(int id);
    QVector<int> selectionGetRows(int id);
    QVariantMap selectionGetItemIndex(int id, int index);
    void selectionSetItemIndex(int id, int index, const QVariantMap &item);
    QVariantList selectionGetItemsData(int id);
    void selectionSetItemsData(int id, const QVariantList &dataList);
    QVariantList selectionGetItemsFormat(int id, const QString &format);
    void selectionSetItemsFormat(int id, const QString &mime, const QVariant &value);
    void selectionMove(int id, int row);
    void selectionSort(int id, const QVector<int> &indexes);

    QString testSelected();

    QVariant callPlugin(const QVariantList &arguments);

    void serverLog(const QString &text);

    QString currentWindowTitle();

    int inputDialog(const NamedValueList &values);

    void setSelectedItemsData(const QString &mime, const QVariant &value, const QString &tabName);

    void filter(const QString &text);
    QString filter();

    QVector<Command> commands();
    void setCommands(const QVector<Command> &commands);
    void addCommands(const QVector<Command> &commands);

    QByteArray screenshot(const QString &format, const QString &screenName, bool select);

    QStringList screenNames();

    KeyboardModifierList queryKeyboardModifiers();
    QPoint pointerPosition();
    void setPointerPosition(int x, int y);

    QString iconColor();
    bool setIconColor(const QString &name);

    QString iconTag();
    void setIconTag(const QString &tag);

    QString iconTagColor();
    bool setIconTagColor(const QString &name);

    void setClipboardData(const QVariantMap &data);
    void setTitle(const QString &title);
    void setTitleForData(const QVariantMap &data);
    void saveData(const QString &tab, const QVariantMap &data, ClipboardMode mode);
    void showDataNotification(const QVariantMap &data);

    bool enableMenuItem(int actionId, int currentRun, int menuItemMatchCommandIndex, const QVariantMap &menuItem);

    QVariantMap setDisplayData(int actionId, const QVariantMap &displayData);

    QVector<Command> automaticCommands();
    QVector<Command> displayCommands();
    QVector<Command> scriptCommands();

    bool openUrls(const QStringList &urls);

    QString loadTheme(const QString &path);

    QByteArray getClipboardData(const QString &mime, ClipboardMode mode);
    bool hasClipboardFormat(const QString &mime, ClipboardMode mode);

    QStringList styles();

    QString stats();

    void setScriptOverrides(const QVector<int> &overrides);

signals:
    void functionCallFinished(int functionCallId, const QVariant &returnValue);
    void inputDialogFinished(int dialogId, const NamedValueList &result);
    void sendMessage(const QByteArray &message, int messageCode);
    void abortEvaluationRequest();
    void actionIdChanged(int actionId);
    void clipboardProviderRegistered(ClientSocketId clientId, ClipboardMode mode);

private:
    ClipboardBrowser *fetchBrowser(const QString &tabName);
    ClipboardBrowser *fetchExistingBrowser(const QString &tabName);

    ClipboardBrowser *selectedBrowser();

    QVariantMap itemData(const QString &tabName, int i);
    QByteArray itemData(const QString &tabName, int i, const QString &mime);

    void setItemsData(
        ClipboardBrowser *c, const QList<QPersistentModelIndex> &indexes, const QString &mime, const QVariant &value);

    template<typename T>
    T getSelectionData(const QString &mime);

    QPersistentModelIndex currentIndex(ClipboardBrowser *c = nullptr);
    QList<QPersistentModelIndex> selectedIndexes(ClipboardBrowser *c = nullptr);

    ClipboardBrowser *browserForIndexes(const QList<QPersistentModelIndex> &indexes) const;

    QVariant waitForFunctionCallFinished(int functionId);

    QByteArray callFunctionHelper(const QByteArray &serializedFunctionCall);

    bool getSelectionData();

    MainWindow* m_wnd;
    QVariantMap m_actionData;
    int m_actionId = -1;

    int m_lastFunctionCallId = -1;
    int m_lastInputDialogId = -1;

    int m_functionCallStack = 0;
    bool m_shouldBeDeleted = false;

    int m_lastSelectionId = -1;
    QMap<int, ItemSelection> m_selections;

    bool m_disconnected = false;
    ClientSocketId m_clientSocketId = 0;
};

void setClipboardMonitorRunning(bool running);
bool isClipboardMonitorRunning();
