/*
 * Copyright (c) 2022-2024 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 "dinput_sa_manager.h"

#include "iservice_registry.h"
#include "system_ability_definition.h"

#include "constants_dinput.h"
#include "dinput_errcode.h"
#include "dinput_log.h"

namespace OHOS {
namespace DistributedHardware {
namespace DistributedInput {
IMPLEMENT_SINGLE_INSTANCE(DInputSAManager);
const uint32_t DINPUT_CLIENT_HANDLER_MSG_DELAY_TIME = 100; // million seconds
void DInputSAManager::SystemAbilityListener::OnRemoveSystemAbility(int32_t systemAbilityId, const std::string &deviceId)
{
    if (systemAbilityId == DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID) {
        DInputSAManager::GetInstance().dInputSourceSAOnline_.store(false);
        {
            std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sourceMutex_);
            DInputSAManager::GetInstance().dInputSourceProxy_ = nullptr;
        }
        {
            std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().handlerMutex_);
            if (DInputSAManager::GetInstance().eventHandler_ != nullptr) {
                DHLOGI("SendEvent DINPUT_CLIENT_CLEAR_SOURCE_CALLBACK_REGISTER_MSG");
                AppExecFwk::InnerEvent::Pointer msgEvent =
                    AppExecFwk::InnerEvent::Get(DINPUT_CLIENT_CLEAR_SOURCE_CALLBACK_REGISTER_MSG, systemAbilityId);
                DInputSAManager::GetInstance().eventHandler_->SendEvent(msgEvent, DINPUT_CLIENT_HANDLER_MSG_DELAY_TIME,
                    AppExecFwk::EventQueue::Priority::IMMEDIATE);
            }
        }
    }

    if (systemAbilityId == DISTRIBUTED_HARDWARE_INPUT_SINK_SA_ID) {
        DInputSAManager::GetInstance().dInputSinkSAOnline_.store(false);
        {
            std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sinkMutex_);
            DInputSAManager::GetInstance().dInputSinkProxy_ = nullptr;
        }
        {
            std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().handlerMutex_);
            if (DInputSAManager::GetInstance().eventHandler_ != nullptr) {
                DHLOGI("SendEvent DINPUT_CLIENT_CLEAR_SINK_CALLBACK_REGISTER_MSG");
                AppExecFwk::InnerEvent::Pointer msgEvent =
                    AppExecFwk::InnerEvent::Get(DINPUT_CLIENT_CLEAR_SINK_CALLBACK_REGISTER_MSG, systemAbilityId);
                DInputSAManager::GetInstance().eventHandler_->SendEvent(msgEvent, DINPUT_CLIENT_HANDLER_MSG_DELAY_TIME,
                    AppExecFwk::EventQueue::Priority::IMMEDIATE);
            }
        }
    }
    DHLOGI("sa %{public}d is removed.", systemAbilityId);
}

void DInputSAManager::SystemAbilityListener::OnAddSystemAbility(int32_t systemAbilityId, const std::string &deviceId)
{
    if (systemAbilityId == DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID) {
        DInputSAManager::GetInstance().dInputSourceSAOnline_.store(true);
        std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().handlerMutex_);
        if (DInputSAManager::GetInstance().eventHandler_ != nullptr) {
            DHLOGI("SendEvent DINPUT_CLIENT_CHECK_SOURCE_CALLBACK_REGISTER_MSG");
            AppExecFwk::InnerEvent::Pointer msgEvent =
                AppExecFwk::InnerEvent::Get(DINPUT_CLIENT_CHECK_SOURCE_CALLBACK_REGISTER_MSG, systemAbilityId);
            DInputSAManager::GetInstance().eventHandler_->SendEvent(msgEvent,
                DINPUT_CLIENT_HANDLER_MSG_DELAY_TIME, AppExecFwk::EventQueue::Priority::IMMEDIATE);
        }
    }

    if (systemAbilityId == DISTRIBUTED_HARDWARE_INPUT_SINK_SA_ID) {
        DInputSAManager::GetInstance().dInputSinkSAOnline_.store(true);
        std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().handlerMutex_);
        if (DInputSAManager::GetInstance().eventHandler_ != nullptr) {
            DHLOGI("SendEvent DINPUT_CLIENT_CHECK_SINK_CALLBACK_REGISTER_MSG");
            AppExecFwk::InnerEvent::Pointer msgEvent =
                AppExecFwk::InnerEvent::Get(DINPUT_CLIENT_CHECK_SINK_CALLBACK_REGISTER_MSG, systemAbilityId);
            DInputSAManager::GetInstance().eventHandler_->SendEvent(msgEvent,
                DINPUT_CLIENT_HANDLER_MSG_DELAY_TIME, AppExecFwk::EventQueue::Priority::IMMEDIATE);
        }
    }
    DHLOGI("sa %{public}d is added.", systemAbilityId);
}

void DInputSAManager::Init()
{
    saListenerCallback = new(std::nothrow) SystemAbilityListener();
    sptr<ISystemAbilityManager> systemAbilityManager =
        SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();

    if (!systemAbilityManager) {
        DHLOGE("get system ability manager failed.");
        return;
    }

    if (!isSubscribeSrcSAChangeListener_.load()) {
        DHLOGI("try subscribe source sa change listener, saId:%{public}d", DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID);
        int32_t ret = systemAbilityManager->SubscribeSystemAbility(DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID,
            saListenerCallback);
        if (ret != DH_SUCCESS) {
            DHLOGE("subscribe source sa change failed: %{public}d", ret);
            return;
        }
        isSubscribeSrcSAChangeListener_.store(true);
    }

    if (!isSubscribeSinkSAChangeListener_.load()) {
        DHLOGI("try subscribe sink sa change listener, saId:%{public}d", DISTRIBUTED_HARDWARE_INPUT_SINK_SA_ID);
        int32_t ret = systemAbilityManager->SubscribeSystemAbility(DISTRIBUTED_HARDWARE_INPUT_SINK_SA_ID,
            saListenerCallback);
        if (ret != DH_SUCCESS) {
            DHLOGE("subscribe sink sa change failed: %{public}d", ret);
            return;
        }
        isSubscribeSinkSAChangeListener_.store(true);
    }
}

void DInputSAManager::RegisterEventHandler(std::shared_ptr<AppExecFwk::EventHandler> handler)
{
    std::lock_guard<std::mutex> lock(handlerMutex_);
    eventHandler_ = handler;
}

bool DInputSAManager::GetDInputSourceProxy()
{
    if (!isSubscribeSrcSAChangeListener_.load()) {
        std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sourceMutex_);
        if (!isSubscribeSrcSAChangeListener_.load()) {
            sptr<ISystemAbilityManager> systemAbilityManager =
                SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
            if (!systemAbilityManager) {
                DHLOGE("get system ability manager failed.");
                return false;
            }

            DHLOGI("try subscribe source sa change listener, saId:%{public}d", DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID);
            int32_t ret = systemAbilityManager->SubscribeSystemAbility(DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID,
                saListenerCallback);
            if (ret != DH_SUCCESS) {
                DHLOGE("subscribe source sa change failed: %{public}d", ret);
                return false;
            }
            isSubscribeSrcSAChangeListener_.store(true);
        }
    }

    if (dInputSourceSAOnline_.load() && !dInputSourceProxy_) {
        std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sourceMutex_);
        if (dInputSourceProxy_ != nullptr) {
            DHLOGI("dinput source proxy has already got.");
            return true;
        }
        sptr<ISystemAbilityManager> systemAbilityManager =
            SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
        if (!systemAbilityManager) {
            DHLOGE("get system ability manager failed.");
            return false;
        }

        DHLOGI("try get sa: %{public}d", DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID);
        sptr<IRemoteObject> remoteObject = systemAbilityManager->GetSystemAbility(
            DISTRIBUTED_HARDWARE_INPUT_SOURCE_SA_ID);
        if (!remoteObject) {
            return false;
        }

        dInputSourceProxy_ = iface_cast<IDistributedSourceInput>(remoteObject);

        if ((!dInputSourceProxy_) || (!dInputSourceProxy_->AsObject())) {
            return false;
        }
    }
    std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sourceMutex_);
    return dInputSourceProxy_ != nullptr;
}

bool DInputSAManager::HasDInputSourceProxy()
{
    std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sourceMutex_);
    return dInputSourceProxy_ != nullptr;
}

bool DInputSAManager::SetDInputSourceProxy(const sptr<IRemoteObject> &remoteObject)
{
    std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sourceMutex_);
    dInputSourceProxy_ = iface_cast<IDistributedSourceInput>(remoteObject);

    if ((!dInputSourceProxy_) || (!dInputSourceProxy_->AsObject())) {
        DHLOGE("Failed to get dinput source proxy.");
        return false;
    }
    return true;
}

bool DInputSAManager::GetDInputSinkProxy()
{
    if (!isSubscribeSinkSAChangeListener_.load()) {
        std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sinkMutex_);
        if (!isSubscribeSinkSAChangeListener_.load()) {
            sptr<ISystemAbilityManager> systemAbilityManager =
                SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
            if (!systemAbilityManager) {
                DHLOGE("get system ability manager failed.");
                return false;
            }

            DHLOGI("try subscribe sink sa change listener, sa id: %{public}d", DISTRIBUTED_HARDWARE_INPUT_SINK_SA_ID);
            int32_t ret = systemAbilityManager->SubscribeSystemAbility(DISTRIBUTED_HARDWARE_INPUT_SINK_SA_ID,
                saListenerCallback);
            if (ret != DH_SUCCESS) {
                DHLOGE("subscribe sink sa change failed: %{public}d", ret);
                return false;
            }
            isSubscribeSinkSAChangeListener_.store(true);
        }
    }

    if (dInputSinkSAOnline_.load() && !dInputSinkProxy_) {
        std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sinkMutex_);
        if (dInputSinkProxy_ != nullptr) {
            DHLOGI("dinput sink proxy has already got.");
            return true;
        }
        sptr<ISystemAbilityManager> systemAbilityManager =
            SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
        if (!systemAbilityManager) {
            DHLOGE("get system ability manager failed.");
            return false;
        }

        sptr<IRemoteObject> remoteObject = systemAbilityManager->GetSystemAbility(
            DISTRIBUTED_HARDWARE_INPUT_SINK_SA_ID);
        if (!remoteObject) {
            return false;
        }

        dInputSinkProxy_ = iface_cast<IDistributedSinkInput>(remoteObject);

        if ((!dInputSinkProxy_) || (!dInputSinkProxy_->AsObject())) {
            return false;
        }
    }
    std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sinkMutex_);
    return dInputSinkProxy_ != nullptr;
}

bool DInputSAManager::HasDInputSinkProxy()
{
    std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sinkMutex_);
    return dInputSinkProxy_ != nullptr;
}

bool DInputSAManager::SetDInputSinkProxy(const sptr<IRemoteObject> &remoteObject)
{
    std::lock_guard<std::mutex> lock(DInputSAManager::GetInstance().sinkMutex_);
    dInputSinkProxy_ = iface_cast<IDistributedSinkInput>(remoteObject);

    if ((!dInputSinkProxy_) || (!dInputSinkProxy_->AsObject())) {
        DHLOGE("Failed to get dinput sink proxy.");
        return false;
    }
    return true;
}

int32_t DInputSAManager::RestoreRegisterListenerAndCallback()
{
    DHLOGI("Restore RegisterPublisherListener");
    if (!DInputSAManager::GetInstance().GetDInputSourceProxy()) {
        DHLOGE("RestoreRegisterSimulationEventListener proxy error, client fail");
        return ERR_DH_INPUT_CLIENT_GET_SOURCE_PROXY_FAIL;
    }

    int32_t result = DH_SUCCESS;
    {
        std::lock_guard<std::mutex> lock(simEventListenerCacheMtx_);
        for (const auto& listener : simEventListenerCache_) {
            if (listener == nullptr) {
                DHLOGE("simEventListenerCache_ is nullptr");
                continue;
            }
            int32_t ret = DInputSAManager::GetInstance().dInputSourceProxy_->RegisterSimulationEventListener(listener);
            if (ret != DH_SUCCESS) {
                result = ret;
                DHLOGE("SA execute RegisterSimulationEventListener fail, ret = %{public}d", ret);
            }
        }
    }
    {
        DHLOGI("Restore RegisterSessionStateCb");
        std::lock_guard<std::mutex> lock(sessionStateCbCacheMtx_);
        for (const auto& callback : sessionStateCbCache_) {
            if (callback == nullptr) {
                DHLOGE("sessionStateCbCache_ is nullptr");
                continue;
            }
            int32_t ret = DInputSAManager::GetInstance().dInputSourceProxy_->RegisterSessionStateCb(callback);
            if (ret != DH_SUCCESS) {
                result = ret;
                DHLOGE("SA execute RegisterSessionStateCb fail, ret = %{public}d", ret);
            }
        }
    }
    return result;
}

void DInputSAManager::AddSimEventListenerToCache(sptr<ISimulationEventListener> listener)
{
    std::lock_guard<std::mutex> simEventListenerLock(simEventListenerCacheMtx_);
    if (listener != nullptr) {
        simEventListenerCache_.insert(listener);
    }
}

void DInputSAManager::RemoveSimEventListenerFromCache(sptr<ISimulationEventListener> listener)
{
    std::lock_guard<std::mutex> simEventListenerLock(simEventListenerCacheMtx_);
    if (listener != nullptr) {
        simEventListenerCache_.erase(listener);
    }
}

void DInputSAManager::AddSessionStateCbToCache(sptr<ISessionStateCallback> callback)
{
    std::lock_guard<std::mutex> sessionStateCbLock(sessionStateCbCacheMtx_);
    if (callback != nullptr) {
        sessionStateCbCache_.insert(callback);
    }
}

void DInputSAManager::RemoveSessionStateCbFromCache()
{
    std::lock_guard<std::mutex> sessionStateCbLock(sessionStateCbCacheMtx_);
    sessionStateCbCache_.clear();
}
} // namespace DistributedInput
} // namespace DistributedHardware
} // namespace OHOS