/*
 * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 */
#ifndef _KEYBOARDSTATUSMANAGER_H_
#define _KEYBOARDSTATUSMANAGER_H_

#include <iostream>
#include <memory>

#include <QDBusAbstractAdaptor>
#include <QObject>
#include <QSocketNotifier>
#include <QTimer>

#include <libudev.h>

namespace fcitx {

/**
 * Obtain keyboard status and monitor signals of keyboard status changes.
 * 1. Provide an interface to retrieve keyboard status.
 * 2. Monitor the signal of keyboard status changes and then send out a signal.
 */
class KeyboardStatusMonitor : public QObject {
    Q_OBJECT
public:
    struct DeviceInfo {
        QString name;
        QString physPath;
        QString sysfsPath;
        QString eventCapabilities;
    };

    explicit KeyboardStatusMonitor(QObject *parent = nullptr);
    ~KeyboardStatusMonitor() override;

    /**
     * 获取键盘数量
     */
    int getKeyboardNum();

private:
    /**
     * 初始化定时器
     * 在检测到文件发生变化后被触发，进而更新键盘状态
     */
    void initSignalTimer();

    /**
     * 通过Udev监控输入设备的变化
     */
    void setupUdevMonitoring();

    /**
     * 通过文件描述符查询设备状态
     */
    void setupSocketNotifier(int fd);

    /**
     * 添加设备到列表中
     */
    void addDeviceToMap(const QString &id, const DeviceInfo &info);

    /**
     * 更新键盘设备状态
     */
    void updateProcInputDevices();

    void parseAllDeviceBlocks(const QList<QStringList> &blocks);
    QList<QStringList> splitIntoDeviceBlocks(const QString &content) const;
    const QString readFile(const QString &path) const;

    void parseDeviceLine(const QString &line, QString &outId,
                         DeviceInfo &outInfo) const;

    std::pair<QString, DeviceInfo>
    parseSingleDeviceBlock(const QStringList &block) const;

private:
    QMap<QString, QList<DeviceInfo>> m_deviceInfoMap;
    QTimer *m_signalMergingTimer = nullptr;
    struct udev *m_udev = nullptr;
    struct udev_monitor *m_monitor = nullptr;
    QSocketNotifier *m_notifier = nullptr;

private Q_SLOTS:
    void onFileChanged();

signals:
    void keyboardStatusChanged();
};

/**
 * As a dbus service, users can obtain keyboard numbers and listen for signals
 * indicating changes in keyboard status through this service
 */
class KeyboardStatusDBusService : public QObject {
    Q_OBJECT
    Q_CLASSINFO("D-Bus Interface", "org.fcitx.Fcitx5.KeyboardStatus")
public:
    KeyboardStatusDBusService(QObject *parent,
                              KeyboardStatusMonitor *keyboardStatusMonitor)
        : QObject(parent), m_keyboardStatusMonitor(keyboardStatusMonitor) {}

public Q_SLOTS:
    int getKeyboardNum() {
        std::cout << "func : " << __FUNCTION__ << std::endl;
        if (m_keyboardStatusMonitor == nullptr) {
            // 默认存在键盘，确保不会转换到屏幕键盘输入模式
            std::cerr << "m_keyboardStatusMonitor is nullptr,return.";
            return 1;
        }

        return m_keyboardStatusMonitor->getKeyboardNum();
    }

signals:
    void keyboardStatusChanged();

private:
    KeyboardStatusMonitor *m_keyboardStatusMonitor = nullptr;
};

/**
 * Manage keyboard status monitor and keyboard status dbus service
 */
class KeyboardStatusManager : public QObject {
public:
    explicit KeyboardStatusManager(QObject *parent = nullptr);
    ~KeyboardStatusManager() override;

private:
    void registerDBus();

private:
    KeyboardStatusMonitor *m_keyboardStatusMonitor = nullptr;
    KeyboardStatusDBusService *m_keyboardStatusDBusService = nullptr;
};
} // namespace fcitx
#endif