/*
 * Copyright (c) 2024-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 "adapter/ios/capability/editing/text_input_client_handler.h"
#include "adapter/ios/capability/editing/text_input_connection_impl.h"
#include "core/common/container.h"
#include "core/common/ime/input_method_manager.h"
#include "core/common/ime/text_input_connection.h"
#include "core/components_ng/pattern/text_field/text_field_manager.h"
#include "core/pipeline_ng/pipeline_context.h"

namespace OHOS::Ace {
std::unique_ptr<InputMethodManager> InputMethodManager::instance_ = nullptr;
std::mutex InputMethodManager::mtx_;

InputMethodManager* InputMethodManager::GetInstance()
{
    if (instance_ == nullptr) {
        std::lock_guard<std::mutex> lock(mtx_);
        if (instance_ == nullptr) {
            instance_.reset(new InputMethodManager);
        }
    }
    return instance_.get();
}

void InputMethodManager::OnFocusNodeChange(const RefPtr<NG::FrameNode>& curFocusNode, FocusReason focusReason)
{
    ManageFocusNode(curFocusNode, focusReason);
}

void InputMethodManager::ManageFocusNode(
    const RefPtr<NG::FrameNode>& curFocusNode, FocusReason focusReason, bool saveKeyboard)
{
    if (!curFocusNode) {
        TAG_LOGE(AceLogTag::ACE_KEYBOARD, "CurFocusNode Not Exist");
        return;
    }
    curFocusNode_ = curFocusNode;
    auto pattern = curFocusNode->GetPattern();
    if (pattern) {
        pattern->OnFocusNodeChange(focusReason);
    }
    if (saveKeyboard) {
        TAG_LOGI(AceLogTag::ACE_KEYBOARD, "No Need To Close Keyboard");
        return;
    }
    CloseKeyboard(curFocusNode);
}

void InputMethodManager::ProcessKeyboardInWindowScene(const RefPtr<NG::FrameNode>& curFocusNode) {}

void InputMethodManager::ProcessKeyboard(const RefPtr<NG::FrameNode>& curFocusNode) {}

void InputMethodManager::SetWindowFocus(bool windowFocus) {}

bool InputMethodManager::NeedSoftKeyboard() const
{
    return false;
}

void InputMethodManager::CloseKeyboard(bool disableNeedToRequestKeyboard)
{
    auto currentFocusNode = curFocusNode_.Upgrade();
    CHECK_NULL_VOID(currentFocusNode);
    auto pipeline = currentFocusNode->GetContext();
    CHECK_NULL_VOID(pipeline);
    auto textFieldManager = pipeline->GetTextFieldManager();
    CHECK_NULL_VOID(textFieldManager);
    if (disableNeedToRequestKeyboard) {
        textFieldManager->SetNeedToRequestKeyboard(false);
    }
    auto container = Container::Current();
    CHECK_NULL_VOID(container);
    auto instanceId = container->GetInstanceId();
    auto connection = Platform::TextInputClientHandler::GetInstance().GetCurrentConnection();
    CHECK_NULL_VOID(connection);
    connection->Close(instanceId);
}

void InputMethodManager::CloseKeyboard(const RefPtr<NG::FrameNode>& focusNode)
{
    auto curPattern = focusNode->GetPattern<NG::Pattern>();
    CHECK_NULL_VOID(curPattern);
    ACE_LAYOUT_SCOPED_TRACE("CloseKeyboard[id:%d]", focusNode->GetId());
    bool isNeedKeyBoard = curPattern->NeedSoftKeyboard();
    if (!isNeedKeyBoard) {
        TAG_LOGI(AceLogTag::ACE_KEYBOARD, "FrameNode(%{public}s/%{public}d) notNeedSoftKeyboard.",
            focusNode->GetTag().c_str(), focusNode->GetId());
        CloseKeyboard();
    }
}

void InputMethodManager::CloseKeyboardInPipelineDestroy() {}

void InputMethodManager::HideKeyboardAcrossProcesses() {}

void InputMethodManager::ProcessModalPageScene() {}

void InputMethodManager::ShowKeyboard(bool isFocusViewChanged, int32_t instanceId)
{
    auto connection = Platform::TextInputClientHandler::GetInstance().GetCurrentConnection();
    CHECK_NULL_VOID(connection);
    connection->Show(isFocusViewChanged, instanceId);
}

void InputMethodManager::SetEditingState(const TextEditingValue& value, int32_t instanceId, bool needFireChangeEvent)
{
    auto connection = Platform::TextInputClientHandler::GetInstance().GetCurrentConnection();
    CHECK_NULL_VOID(connection);
    connection->SetEditingState(value, instanceId, needFireChangeEvent);
}

void InputMethodManager::CloseKeyboard(int32_t instanceId)
{
    auto connection = Platform::TextInputClientHandler::GetInstance().GetCurrentConnection();
    CHECK_NULL_VOID(connection);
    connection->Close(instanceId);
}

void InputMethodManager::Attach(const WeakPtr<TextInputClient>& client, const TextInputConfiguration& config,
    const RefPtr<TaskExecutor>& taskExecutor, int32_t instanceId)
{
    auto connection = AceType::MakeRefPtr<Platform::TextInputConnectionImpl>(client, taskExecutor, config);
    Platform::TextInputClientHandler::GetInstance().SetCurrentConnection(connection);
}

void InputMethodManager::FinishComposing(int32_t instanceId)
{
    auto connection = Platform::TextInputClientHandler::GetInstance().GetCurrentConnection();
    CHECK_NULL_VOID(connection);
    connection->FinishComposing(instanceId);
}
} // namespace OHOS::Ace