/* -*- Mode: C++; indent-tabs-mode: nil; tab-width: 4 -*-
 * -*- coding: utf-8 -*-
 *
 * Copyright (C) 2022 KylinSoft Co., Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: sundagao <sundagao@kylinos.cn>
 */
#include "input-device-manager.h"
#include "input-device-helper.h"
#include "input-device-function.h"
#define TEST_PRINT
using namespace InputDeviceHelper;

extern const DeviceFunctionMap deviceFuncMap;
bool InputDeviceManager::s_mousePresent = false;

InputDeviceManager::InputDeviceManager(QObject *parent) : QObject(parent)
{
    m_timer = new QTimer(this);
}

InputDeviceManager::~InputDeviceManager()
{
    disconnectAll();
    clearUpDeviceList();
}

void InputDeviceManager::start()
{
    connect(m_timer, &QTimer::timeout, this, &InputDeviceManager::managerStart);
    m_timer->start();
}

void InputDeviceManager::stop()
{
    disconnectAll();
    clearUpDeviceList();
}

bool InputDeviceManager::mousePresent()
{
    return s_mousePresent;
}

void InputDeviceManager::setMousePresent(bool value)
{
    s_mousePresent = value;
}

bool InputDeviceManager::initDeviceFactor()
{
    //初始化设备factor,获取设备列表
    m_deviceFactor = InputDeviceFactorManager::createDeviceFactor(this);
    if (!m_deviceFactor) {
        return false;
    }
    m_deviceFactor->initInputDevices();
    //光标改变
    connect(m_inputGsettings, &InputGsettings::mouseChanged, m_deviceFactor, &InputDeviceFactor::onCursorChanged);
    return true;
}

void InputDeviceManager::eliminateSpecialDevice()
{
    for (InputDevice *touchpad : m_touchpadList) {
        QVariant touchProduct = touchpad->getProductId();
        USD_LOG(LOG_DEBUG,"touchProduct : %d", touchProduct.toInt());
        for (InputDevice* mouse : m_mouseList) {
            QVariant mouseProduct = mouse->getProductId();
            USD_LOG(LOG_DEBUG,"mouseProduct : %d", mouseProduct.toInt());
            if (touchProduct == mouseProduct) {
                m_mouseList.removeAll(mouse);
            }
        }
    }
}

//清空设备列表
void InputDeviceManager::clearUpDeviceList()
{
    qDeleteAll(m_mouseList);
    m_mouseList.clear();
    qDeleteAll(m_touchpadList);
    m_touchpadList.clear();
    qDeleteAll(m_keyboardList);
    m_keyboardList.clear();
}

void InputDeviceManager::connctGsettings()
{
    //gsettings 改变
    connect(m_inputGsettings,&InputGsettings::mouseChanged,this,&InputDeviceManager::onMouseChanged);
    connect(m_inputGsettings,&InputGsettings::touchpadChanged,this,&InputDeviceManager::onTouchpadChanged);
    connect(m_inputGsettings,&InputGsettings::keyboardChanged,this,&InputDeviceManager::onKeyboardChanged);

}

void InputDeviceManager::disconnectAll()
{
    //断开监听
    disconnect(m_inputGsettings,&InputGsettings::mouseChanged,this,&InputDeviceManager::onMouseChanged);
    disconnect(m_inputGsettings,&InputGsettings::touchpadChanged,this,&InputDeviceManager::onTouchpadChanged);
    disconnect(m_inputGsettings,&InputGsettings::keyboardChanged,this,&InputDeviceManager::onKeyboardChanged);

}

void InputDeviceManager::classifyDevice(InputDevice *device)
{
    DeviceType type = device->getDeviceType();
    switch (type) {
    case DeviceType::IN_MOUSE:
        m_mouseList.append(device);
        break;
    case DeviceType::IN_TOUCHPAD:
        m_touchpadList.append(device);
        break;
    case DeviceType::IN_KEYBOARD:
        m_keyboardList.append(device);
        break;
    case DeviceType::IN_TOUCHSCREEN:
        /*
         *
         *
         */
        break;
    default:
        break;
    }
}

bool InputDeviceManager::deleteDevice(QVariant deviceId)
{
    for (InputDevice* device : m_mouseList) {
        if (deviceId == device->getDeviceId()) {
            m_mouseList.removeOne(device);
            return true;
        }
    }
    for (InputDevice* device : m_touchpadList) {
        if (deviceId == device->getDeviceId()) {
            m_touchpadList.removeOne(device);
            return true;
        }
    }
    for (InputDevice* device : m_keyboardList) {
        if (deviceId == device->getDeviceId()) {
            m_keyboardList.removeOne(device);
            return true;
        }
    }
    return false;
}

bool InputDeviceManager::existMouse()
{
    return m_mouseList.count();
}

void InputDeviceManager::testPrintDeviceList()
{
    for (InputDevice* device : m_mouseList) {
        USD_LOG(LOG_DEBUG,"mouse device name : %s - device id %s",device->getDeviceName().toLatin1().data(),device->getDeviceId().toString().toLatin1().data());
    }
    for (InputDevice* device : m_touchpadList) {
        USD_LOG(LOG_DEBUG,"touchpad device name : %s - device id %s",device->getDeviceName().toLatin1().data(),device->getDeviceId().toString().toLatin1().data());
    }
    for (InputDevice* device : m_keyboardList) {
        USD_LOG(LOG_DEBUG,"keyboardr device name : %s - device id %s",device->getDeviceName().toLatin1().data(),device->getDeviceId().toString().toLatin1().data());
    }
}

#define device_function(list, key, value) \
    auto func = deviceFuncMap.value(key); \
    if (func) { \
        for (InputDevice* device : list) { \
            func(value, device); \
        } \
    }

#define restore_touchpad_status(prama) \
    for (InputDevice* device : m_touchpadList) {\
        device->setEnable(prama); \
    }

void InputDeviceManager::restoreTouchpadStatus()
{
    const QVariant& value = m_inputGsettings->getGsettingsValue(KEY_TOUCHPAD_ENABLED, DeviceType::IN_TOUCHPAD);
    restore_touchpad_status(value);
}

void InputDeviceManager::onMouseChanged(const QString& key, QVariant value)
{
    USD_LOG(LOG_DEBUG,"mouse property %s is changed",key.toLatin1().data());
    if (key == QStringLiteral(KEY_MOUSE_LOCATE_POINTER)) {
        InputDeviceFunction::setLocatePointer(value);
    } else if (key == QStringLiteral(CURSOR_SIZE)) {

        // wayland 环境下，GTK 光标大小, 通过设置org.gnome.desktop.interface cursor-size生效，X环境下设置此值不生效。
        // X 环境下通过Xsettings设置光标大小， 后续光标大小设置，可考虑放入XSettings, 前提是Xsettings 需要兼容X/wayland。
        QByteArray gnomeSchemaId("org.gnome.desktop.interface");
        if (QGSettings::isSchemaInstalled(gnomeSchemaId)) {
            QGSettings(gnomeSchemaId).set(key, value);
        }

    } else {
        device_function(m_mouseList, key, value);
    }
}

void InputDeviceManager::onTouchpadChanged(const QString &key, QVariant value)
{
    USD_LOG(LOG_DEBUG,"touchpad property %s is changed",key.toLatin1().data());
    device_function(m_touchpadList, key, value);
}

void InputDeviceManager::onKeyboardChanged(const QString &key, QVariant value)
{
    USD_LOG(LOG_DEBUG,"keyboard property %s is changed",key.toLatin1().data());
    device_function(m_keyboardList, key, value);
}

//slots
void InputDeviceManager::deviceAdd(InputDevice* device)
{
    classifyDevice(device);
    eliminateSpecialDevice();
    setMousePresent(existMouse());
    restoreTouchpadStatus();

#ifdef TEST_PRINT
    testPrintDeviceList();
#endif
}

void InputDeviceManager::deviceRemove(QVariant deviceId)
{
    deleteDevice(deviceId);
    setMousePresent(existMouse());
    restoreTouchpadStatus();
#ifdef TEST_PRINT
    testPrintDeviceList();
#endif
}

void InputDeviceManager::initLocatePointer()
{// 没有指针设备时，显示光标位置依然需要可用
    bool value = m_inputGsettings->getGsettingsValue(KEY_MOUSE_LOCATE_POINTER).toBool();
    InputDeviceFunction::setLocatePointer(value);
}

void InputDeviceManager::initCursorSize()
{
    onMouseChanged(CURSOR_SIZE, m_inputGsettings->getGsettingsValue(CURSOR_SIZE));
}

void InputDeviceManager::managerStart()
{
    m_timer->stop();
    m_inputGsettings = InputGsettings::instance();
    m_inputGsettings->initGsettings();
    //gsettings init faild
    if (!m_inputGsettings->resultInitGsettings()) {
        USD_LOG(LOG_WARNING,"input gsettings init faild .");
        return;
    }
    //光标显示
    initLocatePointer();
    //GTK 光标大小
    initCursorSize();
    //初始化设备工场
    if (!initDeviceFactor()) {
        USD_LOG(LOG_WARNING,"init device factor faild .");
        return;
    }
    //剔除与触摸板Product ID 一致的mouse设备
    eliminateSpecialDevice();
    //gsettings 改变
    connctGsettings();

    //因为插入鼠标禁用触摸板原因，所以在初始化所有设备后，恢复触摸板状态
    restoreTouchpadStatus();

#ifdef TEST_PRINT
    testPrintDeviceList();
#endif
}
