/*
 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "input_device_impl.h"

#include "multimodal_input_connect_manager.h"
#include "bytrace_adapter.h"
#include "mmi_log.h"

#undef MMI_LOG_TAG
#define MMI_LOG_TAG "InputDeviceImpl"

namespace OHOS {
namespace MMI {
namespace {
constexpr std::string_view INPUT_DEV_CHANGE_ADD_DEV { "add" };
constexpr std::string_view INPUT_DEV_CHANGE_REMOVE_DEV { "remove" };
}

InputDeviceImpl& InputDeviceImpl::GetInstance()
{
    static InputDeviceImpl instance;
    return instance;
}

int32_t InputDeviceImpl::RegisterDevListener(const std::string &type, InputDevListenerPtr listener)
{
    CHKPR(listener, RET_ERR);
    MMI_HILOGI("Register listener of change of input devices");

    bool needStartServer = false;
    {
        std::lock_guard<std::mutex> guard(devListenerMutex_);
        auto iter = devListener_.find(type);
        if (iter == devListener_.end()) {
            MMI_HILOGE("Type of listener (%{public}s) is not supported", type.c_str());
            return RET_ERR;
        }

        auto &listeners = iter->second;
        bool isNew = std::all_of(listeners.cbegin(), listeners.cend(),
            [listener](InputDevListenerPtr tListener) {
                return (tListener != listener);
            });
        if (isNew) {
            listeners.push_back(listener);
            needStartServer = listeners.size();
        }
    }

    if (needStartServer) {
        auto ret = StartListeningToServer();
        if (ret != RET_OK) {
            MMI_HILOGE("StartListeningToServer fail, error:%{public}d", ret);
            std::lock_guard<std::mutex> guard(devListenerMutex_);
            auto iter = devListener_.find(type);
            if (iter != devListener_.end()) {
                iter->second.remove(listener);
            }
            return ret;
        }
    }

    MMI_HILOGI("Succeed to register listener of change of input devices");
    return RET_OK;
}

int32_t InputDeviceImpl::UnregisterDevListener(const std::string &type, InputDevListenerPtr listener)
{
    bool needStopServer = false;
    {
        std::lock_guard<std::mutex> guard(devListenerMutex_);
        auto iter = devListener_.find(type);
        if (iter == devListener_.end()) {
            MMI_HILOGE("Type of listener (%{public}s) is not supported", type.c_str());
            return RET_ERR;
        }

        auto &listeners = iter->second;
        if (listener == nullptr) {
            MMI_HILOGI("Unregister all listeners of change of input devices");
            needStopServer = !listeners.empty();
            listeners.clear();
        } else {
            MMI_HILOGI("Unregister listener of change of input devices");
            size_t oldSize = listeners.size();
            listeners.remove_if([listener](const auto &item) {
                return (item == listener);
            });
            needStopServer = (oldSize > 0) && listeners.empty();
        }
    }

    if (needStopServer) {
        StopListeningToServer();
    }
    return RET_OK;
}

void InputDeviceImpl::OnDevListener(int32_t deviceId, const std::string &type)
{
    CALL_DEBUG_ENTER;
    int32_t keyboardType = 0;
    MULTIMODAL_INPUT_CONNECT_MGR->GetKeyboardType(deviceId, keyboardType);
    MMI_HILOGI("Change(%{public}s) of input device(%{public}d), keyboardType:%{public}d",
        type.c_str(), deviceId, keyboardType);

    std::vector<InputDevListenerPtr> listenersToNotify;
    {
        std::lock_guard<std::mutex> guard(devListenerMutex_);
        auto iter = devListener_.find(CHANGED_TYPE);
        if (iter == devListener_.end()) {
            MMI_HILOGE("Find change failed");
            return;
        }
        listenersToNotify.assign(iter->second.begin(), iter->second.end());
    }

    BytraceAdapter::StartDevListener(type, deviceId);

    for (const auto &item : listenersToNotify) {
        if (type == INPUT_DEV_CHANGE_ADD_DEV) {
            item->OnDeviceAdded(deviceId, type);
        } else if (type == INPUT_DEV_CHANGE_REMOVE_DEV) {
            item->OnDeviceRemoved(deviceId, type);
        }
    }
    BytraceAdapter::StopDevListener();
}

int32_t InputDeviceImpl::GetInputDeviceIds(FunInputDevIds callback)
{
    CALL_DEBUG_ENTER;
    CHKPR(callback, RET_ERR);
    std::vector<int32_t> ids;
    if (MULTIMODAL_INPUT_CONNECT_MGR->GetDeviceIds(ids) != RET_OK) {
        MMI_HILOGE("GetInputDeviceIds failed");
        return RET_ERR;
    }
    callback(ids);
    return RET_OK;
}

int32_t InputDeviceImpl::GetInputDevice(int32_t deviceId, FunInputDevInfo callback)
{
    CALL_DEBUG_ENTER;
    CHKPR(callback, RET_ERR);
    std::shared_ptr<InputDevice> inputDevice = std::make_shared<InputDevice>();
    if (MULTIMODAL_INPUT_CONNECT_MGR->GetDevice(deviceId, inputDevice) != RET_OK) {
        MMI_HILOGE("GetDevice failed");
        return RET_ERR;
    }
    callback(inputDevice);
    return RET_OK;
}

int32_t InputDeviceImpl::SupportKeys(int32_t deviceId, std::vector<int32_t> keyCodes, FunInputDevKeys callback)
{
    CALL_DEBUG_ENTER;
    CHKPR(callback, RET_ERR);
    std::vector<bool> keystroke;
    if (MULTIMODAL_INPUT_CONNECT_MGR->SupportKeys(deviceId, keyCodes, keystroke) != RET_OK) {
        MMI_HILOGE("SupportKeys failed");
        return RET_ERR;
    }
    callback(keystroke);
    return RET_OK;
}

int32_t InputDeviceImpl::GetKeyboardType(int32_t deviceId, FunKeyboardTypes callback)
{
    CALL_DEBUG_ENTER;
    CHKPR(callback, RET_ERR);
    int32_t keyboardType = 0;
    if (MULTIMODAL_INPUT_CONNECT_MGR->GetKeyboardType(deviceId, keyboardType) != RET_OK) {
        MMI_HILOGE("GetKeyboardType failed");
        return RET_ERR;
    }
    callback(keyboardType);
    return RET_OK;
}

int32_t InputDeviceImpl::SetKeyboardRepeatDelay(int32_t delay)
{
    CALL_DEBUG_ENTER;
    if (MULTIMODAL_INPUT_CONNECT_MGR->SetKeyboardRepeatDelay(delay) != RET_OK) {
        MMI_HILOGE("SetKeyboardRepeatDelay failed");
        return RET_ERR;
    }
    return RET_OK;
}

int32_t InputDeviceImpl::SetKeyboardRepeatRate(int32_t rate)
{
    CALL_DEBUG_ENTER;
    if (MULTIMODAL_INPUT_CONNECT_MGR->SetKeyboardRepeatRate(rate) != RET_OK) {
        MMI_HILOGE("SetKeyboardRepeatRate failed");
        return RET_ERR;
    }
    return RET_OK;
}

int32_t InputDeviceImpl::GetKeyboardRepeatDelay(std::function<void(int32_t)> callback)
{
    CALL_DEBUG_ENTER;
    CHKPR(callback, RET_ERR);
    int32_t repeatDelay = 0;
    if (MULTIMODAL_INPUT_CONNECT_MGR->GetKeyboardRepeatDelay(repeatDelay) != RET_OK) {
        MMI_HILOGE("GetKeyboardRepeatDelay failed");
        return RET_ERR;
    }
    callback(repeatDelay);
    return RET_OK;
}

int32_t InputDeviceImpl::GetKeyboardRepeatRate(std::function<void(int32_t)> callback)
{
    CALL_DEBUG_ENTER;
    int32_t repeatRate = 0;
    if (MULTIMODAL_INPUT_CONNECT_MGR->GetKeyboardRepeatRate(repeatRate) != RET_OK) {
        MMI_HILOGE("GetKeyboardRepeatRate failed");
        return RET_ERR;
    }
    callback(repeatRate);
    return RET_OK;
}

int32_t InputDeviceImpl::GetUserData()
{
    return userData_;
}

int32_t InputDeviceImpl::RegisterInputdevice(int32_t deviceId, bool enable, std::function<void(int32_t)> callback)
{
    CALL_DEBUG_ENTER;
    CHKPR(callback, RET_ERR);
    int32_t _id;
    {
        std::lock_guard<std::mutex> guard(inputDeviceMutex_);
        _id = operationIndex_++;
        inputdeviceList_[_id] = callback;
    }
    int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->SetInputDeviceEnabled(deviceId, enable, _id);
    if (ret != RET_OK) {
        MMI_HILOGE("Failed to register");
        return ret;
    }
    return RET_OK;
}

void InputDeviceImpl::OnSetInputDeviceAck(int32_t index, int32_t result)
{
    CALL_DEBUG_ENTER;
    std::function<void(int32_t)> callback;
    {
        std::lock_guard<std::mutex> guard(inputDeviceMutex_);
        auto iter = inputdeviceList_.find(index);
        if (iter == inputdeviceList_.end()) {
            MMI_HILOGE("Find index failed");
            return;
        }
        callback = std::move(iter->second);
        inputdeviceList_.erase(iter);
    }
    callback(result);
}

void InputDeviceImpl::OnConnected()
{
    bool shouldStartServer = false;
    {
        std::lock_guard<std::mutex> guard(devListenerMutex_);
        auto iter = devListener_.find(CHANGED_TYPE);
        shouldStartServer = (iter != devListener_.end()) && !iter->second.empty();
    }

    if (!shouldStartServer) {
        return;
    }

    auto ret = StartListeningToServer();
    if (ret != RET_OK) {
        MMI_HILOGE("StartListeningToServer fail, error:%{public}d", ret);
    }
}

void InputDeviceImpl::OnDisconnected()
{
    MMI_HILOGI("Disconnected from server");
    isListeningProcess_.store(false);
}

int32_t InputDeviceImpl::StartListeningToServer()
{
    if (isListeningProcess_.load()) {
        return RET_OK;
    }
    MMI_HILOGI("Start monitoring changes of input devices");
    int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->RegisterDevListener();
    if (ret != RET_OK) {
        MMI_HILOGE("RegisterDevListener to server fail, error:%{public}d", ret);
        return ret;
    }
    isListeningProcess_.store(true);
    return RET_OK;
}

void InputDeviceImpl::StopListeningToServer()
{
    if (!isListeningProcess_.load()) {
        return;
    }
    MMI_HILOGI("Stop monitoring changes of input devices");
    auto ret = MULTIMODAL_INPUT_CONNECT_MGR->UnregisterDevListener();
    if (ret != RET_OK) {
        MMI_HILOGE("UnregisterDevListener from server fail, error:%{public}d", ret);
    }
    isListeningProcess_.store(false);
}
} // namespace MMI
} // namespace OHOS