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

#include "common_timer_errors.h"
#include "ime_enabled_info_manager.h"
#include "ime_info_inquirer.h"
#include "inputmethod_message_handler.h"
#include "message.h"
namespace OHOS {
namespace MiscServices {
constexpr uint32_t TIMER_TASK_INTERNAL = 1 * 60 * 60 * 1000; // updated hourly
FullImeInfoManager::~FullImeInfoManager()
{
    timer_.Unregister(timerId_);
    timer_.Shutdown();
    fullImeInfos_.clear();
}

FullImeInfoManager::FullImeInfoManager()
{
    uint32_t ret = timer_.Setup();
    if (ret != Utils::TIMER_ERR_OK) {
        IMSA_HILOGE("failed to create timer!");
        return;
    }
    timerId_ = timer_.Register(
        []() {
            Message *msg = new (std::nothrow) Message(MessageID::MSG_ID_REGULAR_UPDATE_IME_INFO, nullptr);
            if (msg == nullptr) {
                IMSA_HILOGE("failed to create message!");
                return;
            }
            auto instance = MessageHandler::Instance();
            if (instance == nullptr) {
                delete msg;
                msg = nullptr;
                return;
            }
            instance->SendMessage(msg);
        },
        TIMER_TASK_INTERNAL, false);
}

FullImeInfoManager &FullImeInfoManager::GetInstance()
{
    static FullImeInfoManager instance;
    return instance;
}
// LCOV_EXCL_START
int32_t FullImeInfoManager::RegularInit()
{
    std::vector<std::pair<int32_t, std::vector<FullImeInfo>>> fullImeInfos;
    auto ret = ImeInfoInquirer::GetInstance().QueryFullImeInfo(fullImeInfos);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGW("failed to QueryFullImeInfo, ret:%{public}d", ret);
        return ret;
    }
    std::lock_guard<std::mutex> lock(lock_);
    fullImeInfos_.clear();
    for (const auto &infos : fullImeInfos) {
        fullImeInfos_.insert_or_assign(infos.first, infos.second);
    }
    return ErrorCode::NO_ERROR;
}

int32_t FullImeInfoManager::Switch(int32_t userId, uint64_t displayId)
{
    std::vector<FullImeInfo> infos;
    auto ret = AddUser(userId, infos);
    // data in global table remains foreground user of default display
    if (displayId == ImfCommonConst::DEFAULT_DISPLAY_ID) {
        ImeEnabledInfoManager::GetInstance().Switch(userId, infos);
    }
    return ret;
}

int32_t FullImeInfoManager::Update()
{
    {
        std::lock_guard<std::mutex> lock(lock_);
        fullImeInfos_.clear();
    }
    IMSA_HILOGI("run in.");
    std::vector<std::pair<int32_t, std::vector<FullImeInfo>>> fullImeInfos;
    auto ret = ImeInfoInquirer::GetInstance().QueryFullImeInfo(fullImeInfos);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGW("failed to QueryFullImeInfo, ret:%{public}d", ret);
        return ret;
    }
    {
        std::lock_guard<std::mutex> lock(lock_);
        for (const auto &infos : fullImeInfos) {
            fullImeInfos_.insert_or_assign(infos.first, infos.second);
        }
    }
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_STOP
int32_t FullImeInfoManager::Delete(int32_t userId)
{
    {
        std::lock_guard<std::mutex> lock(lock_);
        fullImeInfos_.erase(userId);
    }
    ImeEnabledInfoManager::GetInstance().Delete(userId);
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_START
int32_t FullImeInfoManager::Add(int32_t userId, const std::string &bundleName)
{
    FullImeInfo info;
    auto ret = AddPackage(userId, bundleName, info);
    if (ret != ErrorCode::NO_ERROR) {
        return ret;
    }
    ImeEnabledInfoManager::GetInstance().Add(userId, info);
    return ErrorCode::NO_ERROR;
}

int32_t FullImeInfoManager::Delete(int32_t userId, const std::string &bundleName)
{
    auto ret = DeletePackage(userId, bundleName);
    ImeEnabledInfoManager::GetInstance().Delete(userId, bundleName);
    return ret;
}

int32_t FullImeInfoManager::Update(int32_t userId, const std::string &bundleName)
{
    FullImeInfo info;
    auto ret = ImeInfoInquirer::GetInstance().GetFullImeInfo(userId, bundleName, info);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to GetFullImeInfo failed, userId:%{public}d, bundleName:%{public}s, ret:%{public}d",
            userId, bundleName.c_str(), ret);
        return ErrorCode::ERROR_PACKAGE_MANAGER;
    }
    std::lock_guard<std::mutex> lock(lock_);
    auto it = fullImeInfos_.find(userId);
    if (it == fullImeInfos_.end()) {
        fullImeInfos_.insert({ userId, { info } });
        return ErrorCode::NO_ERROR;
    }
    auto iter = std::find_if(it->second.begin(), it->second.end(),
        [&bundleName](const FullImeInfo &info) { return bundleName == info.prop.name; });
    if (iter != it->second.end()) {
        it->second.erase(iter);
    }
    it->second.push_back(info);
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_STOP
int32_t FullImeInfoManager::Get(int32_t userId, std::vector<Property> &props, bool containSysSpecialIme)
{
    props = {};
    {
        std::lock_guard<std::mutex> lock(lock_);
        auto it = fullImeInfos_.find(userId);
        if (it == fullImeInfos_.end()) {
            return ErrorCode::ERROR_IMSA_GET_IME_INFO_FAILED;
        }
        for (const auto &fullImeInfo : it->second) {
            if (!containSysSpecialIme && fullImeInfo.isSystemSpecialIme) {
                continue;
            }
            props.push_back(fullImeInfo.prop);
        }
    }
    auto ret = ImeEnabledInfoManager::GetInstance().GetEnabledStates(userId, props);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("get enabled status failed:%{public}d!", ret);
        return ErrorCode::ERROR_ENABLE_IME;
    }
    IMSA_HILOGD("get success:%{public}d/%{public}zu!", containSysSpecialIme, props.size());
    return ErrorCode::NO_ERROR;
}

bool FullImeInfoManager::Get(int32_t userId, const std::string &bundleName, FullImeInfo &fullImeInfo)
{
    {
        std::lock_guard<std::mutex> lock(lock_);
        auto it = fullImeInfos_.find(userId);
        if (it == fullImeInfos_.end()) {
            IMSA_HILOGD("user %{public}d info", userId);
            return false;
        }
        auto iter = std::find_if(it->second.begin(), it->second.end(),
            [&bundleName](const FullImeInfo &info) { return bundleName == info.prop.name; });
        if (iter == it->second.end()) {
            IMSA_HILOGD("ime: %{public}s not in cache", bundleName.c_str());
            return false;
        }
        fullImeInfo = *iter;
    }
    auto ret =
        ImeEnabledInfoManager::GetInstance().GetEnabledState(userId, fullImeInfo.prop.name, fullImeInfo.prop.status);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("[%{public}d, %{public}s] get enabled status failed:%{public}d!", userId, bundleName.c_str(), ret);
    }
    return true;
}

bool FullImeInfoManager::Has(int32_t userId, const std::string &bundleName)
{
    std::lock_guard<std::mutex> lock(lock_);
    auto it = fullImeInfos_.find(userId);
    if (it == fullImeInfos_.end()) {
        return false;
    }
    auto iter = std::find_if(it->second.begin(), it->second.end(),
        [&bundleName](const FullImeInfo &info) { return bundleName == info.prop.name; });
    return iter != it->second.end();
}

std::string FullImeInfoManager::Get(int32_t userId, uint32_t tokenId)
{
    std::lock_guard<std::mutex> lock(lock_);
    auto it = fullImeInfos_.find(userId);
    if (it == fullImeInfos_.end()) {
        return "";
    }
    auto iter = std::find_if(
        it->second.begin(), it->second.end(), [&tokenId](const FullImeInfo &info) { return tokenId == info.tokenId; });
    if (iter == it->second.end()) {
        return "";
    }
    return (*iter).prop.name;
}
// LCOV_EXCL_START
int32_t FullImeInfoManager::Init()
{
    std::map<int32_t, std::vector<FullImeInfo>> fullImeInfos;
    auto ret = Init(fullImeInfos);
    if (ret != ErrorCode::NO_ERROR) {
        return ret;
    }
    ImeEnabledInfoManager::GetInstance().Init(fullImeInfos);
    return ErrorCode::NO_ERROR;
}

int32_t FullImeInfoManager::Init(std::map<int32_t, std::vector<FullImeInfo>> &fullImeInfos)
{
    {
        std::lock_guard<std::mutex> lock(lock_);
        if (!fullImeInfos_.empty()) {
            fullImeInfos = fullImeInfos_;
            return ErrorCode::NO_ERROR;
        }
    }
    std::vector<std::pair<int32_t, std::vector<FullImeInfo>>> imeInfos;
    auto ret = ImeInfoInquirer::GetInstance().QueryFullImeInfo(imeInfos);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGW("failed to QueryFullImeInfo, ret:%{public}d", ret);
        return ret;
    }
    std::lock_guard<std::mutex> lock(lock_);
    fullImeInfos_.clear();
    for (const auto &infos : imeInfos) {
        fullImeInfos_.insert_or_assign(infos.first, infos.second);
    }
    fullImeInfos = fullImeInfos_;
    return ErrorCode::NO_ERROR;
}

int32_t FullImeInfoManager::AddUser(int32_t userId, std::vector<FullImeInfo> &infos)
{
    {
        std::lock_guard<std::mutex> lock(lock_);
        auto it = fullImeInfos_.find(userId);
        if (it != fullImeInfos_.end()) {
            infos = it->second;
            return ErrorCode::NO_ERROR;
        }
    }
    auto ret = ImeInfoInquirer::GetInstance().QueryFullImeInfo(userId, infos);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to QueryFullImeInfo, userId:%{public}d, ret:%{public}d", userId, ret);
        return ret;
    }
    std::lock_guard<std::mutex> lock(lock_);
    fullImeInfos_.insert_or_assign(userId, infos);
    return ErrorCode::NO_ERROR;
}

int32_t FullImeInfoManager::AddPackage(int32_t userId, const std::string &bundleName, FullImeInfo &info)
{
    auto ret = ImeInfoInquirer::GetInstance().GetFullImeInfo(userId, bundleName, info);
    if (ret != ErrorCode::NO_ERROR) {
        IMSA_HILOGE("failed to GetFullImeInfo failed, userId:%{public}d, bundleName:%{public}s, ret:%{public}d",
                    userId, bundleName.c_str(), ret);
        return ErrorCode::ERROR_PACKAGE_MANAGER;
    }
    std::lock_guard<std::mutex> lock(lock_);
    auto it = fullImeInfos_.find(userId);
    if (it == fullImeInfos_.end()) {
        fullImeInfos_.insert({ userId, { info } });
        return ErrorCode::NO_ERROR;
    }
    auto iter = std::find_if(it->second.begin(), it->second.end(),
                             [&bundleName](const FullImeInfo &info) { return bundleName == info.prop.name; });
    if (iter != it->second.end()) {
        it->second.erase(iter);
    }
    it->second.push_back(info);
    return ErrorCode::NO_ERROR;
}
// LCOV_EXCL_STOP
int32_t FullImeInfoManager::DeletePackage(int32_t userId, const std::string &bundleName)
{
    std::lock_guard<std::mutex> lock(lock_);
    auto it = fullImeInfos_.find(userId);
    if (it == fullImeInfos_.end()) {
        return ErrorCode::NO_ERROR;
    }
    auto iter = std::find_if(it->second.begin(), it->second.end(),
        [&bundleName](const FullImeInfo &info) { return bundleName == info.prop.name; });
    if (iter == it->second.end()) {
        return ErrorCode::NO_ERROR;
    }
    it->second.erase(iter);
    if (it->second.empty()) {
        fullImeInfos_.erase(it->first);
    }
    return ErrorCode::NO_ERROR;
}
} // namespace MiscServices
} // namespace OHOS