#ifndef KEYMAP_H
#define KEYMAP_H
#include <QObject>
#include <QPointF>
#include <QVector>
#include <QRectF>
#include <QPair>
#include <QMetaEnum>
#include <QMultiHash>
#include <QJsonObject>

class KeyMap : public QObject
{    
    Q_OBJECT
public:
    enum KeyMapType {
        KMT_INVALID = -1,
        KMT_CLICK = 0,
        KMT_CLICK_TWICE,
        KMT_STEER_WHEEL,
        KMT_DRAG,
        KMT_MOUSE_MOVE
    };    
    Q_ENUM(KeyMapType)

    enum ActionType {
        AT_INVALID = -1,
        AT_KEY = 0,
        AT_MOUSE = 1,
    };
    Q_ENUM(ActionType)

    struct KeyNode {
        ActionType type = AT_INVALID;
        int key = Qt::Key_unknown;
        QPointF pos = QPointF(0, 0); // normal key
        QPointF extendPos = QPointF(0, 0); // for drag
        double extendOffset = 0.0;  // for steerWheel

        KeyNode(ActionType type = AT_INVALID,
                int key = Qt::Key_unknown,
                QPointF pos = QPointF(0, 0),
                QPointF extendPos = QPointF(0, 0),
                double extendOffset = 0.0)
            : type(type)
            , key(key)
            , pos(pos)
            , extendPos(extendPos)
            , extendOffset(extendOffset)
        {

        }
    };

    struct KeyMapNode {
        KeyMapType type = KMT_INVALID;
        union DATA {
            struct {
                KeyNode keyNode;
                bool switchMap = false;
            } click;
            struct {
                KeyNode keyNode;
            } clickTwice;
            struct {
                QPointF centerPos = {0.0, 0.0};
                KeyNode left, right, up, down;
            } steerWheel;
            struct {
                KeyNode keyNode;
            } drag;
            struct {
                QPointF startPos = {0.0, 0.0};
                int speedRatio = 1;
            } mouseMove;
            DATA() {}
            ~DATA() {}
        } data;
        KeyMapNode() {}
        ~KeyMapNode() {}
    };

    KeyMap(QObject *parent = Q_NULLPTR);
    virtual ~KeyMap();

    void loadKeyMap(const QString &json);
    const KeyMap::KeyMapNode& getKeyMapNode(int key);
    const KeyMap::KeyMapNode& getKeyMapNodeKey(int key);
    const KeyMap::KeyMapNode& getKeyMapNodeMouse(int key);
    bool isSwitchOnKeyboard();
    int getSwitchKey();

    bool isValidMouseMoveMap();
    bool isValidSteerWheelMap();
    const KeyMap::KeyMapNode& getMouseMoveMap();

    static const QString& getKeyMapPath();

private:
    // set up the reverse map from key/event event to keyMapNode
    void makeReverseMap();

    // safe check for base
    bool checkItemString(const QJsonObject& node, const QString& name);
    bool checkItemDouble(const QJsonObject& node, const QString& name);
    bool checkItemBool(const QJsonObject& node, const QString& name);
    bool checkItemObject(const QJsonObject& node, const QString& name);
    bool checkItemPos(const QJsonObject& node, const QString& name);

    // safe check for KeyMapNode
    bool checkForClick(const QJsonObject& node);
    bool checkForClickTwice(const QJsonObject& node);
    bool checkForSteerWhell(const QJsonObject& node);
    bool checkForDrag(const QJsonObject& node);

    // get keymap from json object
    QString getItemString(const QJsonObject& node, const QString& name);
    double getItemDouble(const QJsonObject& node, const QString& name);
    bool getItemBool(const QJsonObject& node, const QString& name);
    QJsonObject getItemObject(const QJsonObject& node, const QString& name);
    QPointF getItemPos(const QJsonObject& node, const QString& name);
    QPair<ActionType, int> getItemKey(const QJsonObject& node, const QString& name);
    KeyMapType getItemKeyMapType(const QJsonObject& node, const QString& name);

private:
    static QString s_keyMapPath;

    QVector<KeyMapNode> m_keyMapNodes;
    KeyNode m_switchKey = {AT_KEY, Qt::Key_QuoteLeft};

    // just for return
    KeyMapNode m_invalidNode;

    // steer wheel index
    int m_idxSteerWheel = -1;

    // mouse move index
    int m_idxMouseMove = -1;

    // mapping of key/mouse event name to index
    QMetaEnum m_metaEnumKey = QMetaEnum::fromType<Qt::Key>();
    QMetaEnum m_metaEnumMouseButtons = QMetaEnum::fromType<Qt::MouseButtons>();
    QMetaEnum m_metaEnumKeyMapType = QMetaEnum::fromType<KeyMap::KeyMapType>();
    // reverse map of key/mouse event
    QMultiHash<int, KeyMapNode*> m_rmapKey;
    QMultiHash<int, KeyMapNode*> m_rmapMouse;
};

#endif // KEYMAP_H
