/*
 * Copyright (c) KylinSoft Co., Ltd. 2025.All rights reserved.
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 */
#include "inputmodemanager.h"

#include "fcitx-utils/dbus/bus.h"
#include "fcitx-utils/dbus/message.h"
#include "fcitx-utils/dbus/objectvtable.h"
#include "dbus_public.h"

namespace fcitx {

static const char KeyboardManagerServiceName[] =
    "org.fcitx.Fcitx5.KeyboardStatus";
static const char KeyboardManagerInterfaceName[] =
    "org.fcitx.Fcitx5.KeyboardStatus";

InputModeManager::InputModeManager(Instance *instance)
    : instance_(instance), bus_(new dbus::Bus(dbus::BusType::System)),
      watcher_(new dbus::ServiceWatcher(*bus_)) {
    bus_->attachEventLoop(&instance->eventLoop());

    reloadConfig();
    updateInputModeStrategy();
    updateShowHideStrategy();
    connectKeyboardSignals();
}

void InputModeManager::updateInputModeStrategy() {
    switch (inputModeConfig_.inputModeStrategySelect.value()) {
    case InputMethodModeStrategy::AlwaysOnPhysicalKeyboard: {
        FCITX_INFO() << "updateInputModeStrategy : AlwaysOnPhysicalKeyboard";
        instance_->setInputMethodModeStrategy(
            InputMethodModeStrategy::AlwaysOnPhysicalKeyboard);
        instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard);
        break;
    }
    case InputMethodModeStrategy::AlwaysOnScreenKeyboard: {
        FCITX_INFO() << "updateInputModeStrategy : AlwaysOnScreenKeyboard";

        instance_->setInputMethodModeStrategy(
            InputMethodModeStrategy::AlwaysOnScreenKeyboard);
        instance_->setInputMethodMode(InputMethodMode::OnScreenKeyboard);
        break;
    }
    case InputMethodModeStrategy::OnScreenKeyboardWhenKeyboardIsDisconnect: {
        FCITX_INFO() << "updateInputModeStrategy : "
                        "OnScreenKeyboardWhenKeyboardIsDisconnect";
        instance_->setInputMethodModeStrategy(
            InputMethodModeStrategy::OnScreenKeyboardWhenKeyboardIsDisconnect);
        updateInputMode();
        break;
    }
    default:
        FCITX_INFO() << "please reset input mode!";
        break;
    }
}

void InputModeManager::updateShowHideStrategy() {
    switch (inputModeConfig_.showHideStrategySelect.value()) {
    case VirtualKeyboardShowHideStrategy::AutoShowAndHide: {
        FCITX_INFO() << "VirtualKeyboardShowHideStrategy : "
                        "AutoShowAndHide";
        instance_->setVirtualKeyboardAutoShow(true);
        instance_->setVirtualKeyboardAutoHide(true);
        break;
    }
    case VirtualKeyboardShowHideStrategy::DoNotAutoShowAndHide: {
        FCITX_INFO() << "VirtualKeyboardShowHideStrategy : "
                        "DoNotAutoShowAndHide";
        instance_->setVirtualKeyboardAutoShow(false);
        instance_->setVirtualKeyboardAutoHide(false);
        break;
    }
    default:
        FCITX_INFO() << "please reset show hide strategy!";
        break;
    }
}

void InputModeManager::updateInputMode() {
    int num = callDBusToGetKeyboardStatus();
    if (num > 0) {
        FCITX_INFO() << "update input mode : OnPhysicalKeyboard";
        instance_->setInputMethodMode(InputMethodMode::OnPhysicalKeyboard);
    } else {
        FCITX_INFO() << "update input mode : OnScreenKeyboard";
        instance_->setInputMethodMode(InputMethodMode::OnScreenKeyboard);
    }
}

void InputModeManager::connectKeyboardSignals() {
    entry_ = watcher_->watchService(
        KeyboardManagerServiceName,
        [this](const std::string &service, const std::string &,
               const std::string &newName) {
            FCITX_INFO() << "KeyboardManager service name : " << service
                         << ", new name : " << newName;
            if (newName.empty()) {
                return;
            }

            if (instance_->inputMethodModeStrategy() !=
                InputMethodModeStrategy::
                    OnScreenKeyboardWhenKeyboardIsDisconnect) {
                return;
            }

            updateInputMode();
        });

    slot_ = bus_->addMatch(
        dbus::MatchRule(KeyboardManagerServiceName, "/",
                        KeyboardManagerInterfaceName, "keyboardStatusChanged"),
        [this](dbus::Message &message) {
            FCITX_UNUSED(message);

            FCITX_INFO() << "keyboardStatusChanged ";
            if (instance_->inputMethodModeStrategy() !=
                InputMethodModeStrategy::
                    OnScreenKeyboardWhenKeyboardIsDisconnect) {
                FCITX_INFO() << "keyboardStatusChanged,but current "
                                "inputMethodModeStrategy is not "
                                "OnScreenKeyboardWhenKeyboardIsDisconnect.";
                return true;
            }

            updateInputMode();
            return true;
        });
}

int InputModeManager::callDBusToGetKeyboardStatus() {
    auto msg =
        bus_->createMethodCall(KeyboardManagerServiceName, "/",
                               KeyboardManagerInterfaceName, "getKeyboardNum");
    if (msg.isError()) {
        FCITX_INFO() << "message is error, keyboardNum set default value : 1";
        return keyboardNum;
    }

    auto replay = msg.call(0);
    if (replay.isError()) {
        FCITX_INFO() << "reply is error, keyboardNum set default value : 1";
        return keyboardNum;
    }

    replay >> keyboardNum;

    FCITX_INFO() << "keyboardNum : " << keyboardNum;

    return keyboardNum;
}

const Configuration *InputModeManager::getConfig() const {
    return &inputModeConfig_;
}

void InputModeManager::reloadConfig() {
    readAsIni(inputModeConfig_, "conf/inputmode.conf");
}

void InputModeManager::setConfig(const RawConfig &config) {
    inputModeConfig_.load(config, true);
    safeSaveAsIni(inputModeConfig_, "conf/inputmode.conf");
    updateInputModeStrategy();
    updateShowHideStrategy();
}

class InputModeFactory : public AddonFactory {
    AddonInstance *create(AddonManager *manager) override {
        return new InputModeManager(manager->instance());
    }
};
} // namespace fcitx

FCITX_ADDON_FACTORY_V2(inputmodemanager, fcitx::InputModeFactory);
