/*
 * Copyright (c) 2022-2023 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 "updater_ui_facade.h"
#include <thread>
#include "component/text_label_adapter.h"
#include "updater_event.h"
#include "updater_ui_config.h"
#include "updater_ui_env.h"
#include "updater_ui_tools.h"

namespace Updater {
constexpr int FULL_PERCENT_PROGRESS = 100;
static float g_currentPercent = 0.0;

UpdaterUiFacade::UpdaterUiFacade()
    : strategies_ {UpdaterUiConfig::GetStrategy()}, pgMgr_ {PageManager::GetInstance()}, mode_ {""}
{
}

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

__attribute__((weak)) void InitUeventThread()
{
    LOG(INFO) << "no need init uevent thread";
}

void UpdaterUiFacade::InitEnv() const
{
    InitUeventThread();
    UpdaterUiEnv::Init();
    UpdaterEvent::Subscribe(UPDATER_POWER_VOLUME_UP_EVENT, OnKeyUpEvent);
    UpdaterEvent::Subscribe(UPDATER_POWER_VOLUME_DOWN_EVENT, OnKeyDownEvent);
}

[[nodiscard]] bool UpdaterUiFacade::SetMode(std::string mode)
{
    if (mode == mode_) {
        return true;
    }
    mode_ = mode;
    SetLogoProgress();
    return true;
}

std::string UpdaterUiFacade::GetMode() const
{
    return mode_;
}

std::pair<bool, UpdaterUiFacade::StrategyMap::const_iterator> UpdaterUiFacade::CheckMode() const
{
    auto it = strategies_.find(mode_);
    if (it == strategies_.end()) {
        LOG(ERROR) << "mode has not a strategy for it " << mode_;
        return {false, strategies_.cend()};
    }
    return {true, it};
}

void UpdaterUiFacade::ShowLog(const std::string &tag, bool isClear) const
{
    if (auto [res, it] = CheckMode(); res) {
        ShowMsg(it->second.labelLogId, tag, isClear);
    }
}

void UpdaterUiFacade::ShowLogRes(const std::string &tag, bool isClear) const
{
    if (auto [res, it] = CheckMode(); res) {
        ShowMsg(it->second.labelLogResId, tag, isClear);
    }
}

void UpdaterUiFacade::ShowUpdInfo(const std::string &tag, bool isClear) const
{
    if (auto [res, it] = CheckMode(); res) {
        ShowMsg(it->second.labelUpdId, tag, isClear);
    }
}

float UpdaterUiFacade::GetCurrentPercent(void)
{
    return g_currentPercent;
}

void UpdaterUiFacade::ShowCurPage()
{
    auto [res, it] = CheckMode();
    if (IsInProgress()) {
        ShowProgress(0);
        pgMgr_.ShowPage(it->second.progressPage.progressPageId);
        ShowProgressWarning(false);
        return;
    }
    pgMgr_.ShowCurPage();
}

void UpdaterUiFacade::ShowProgress(float value) const
{
    if (!CheckMode().first || (value > FULL_PERCENT_PROGRESS)) {
        return;
    }
    static float lastValue = 0.0;
    // 0.1 : The progress bar changes by less than 0.1
    if (abs(value - lastValue) <= 0.1 && lastValue != 0.0) {
        return;
    }
    LOG(INFO) << "current progress " << value;
    lastValue = value;
    if (auto it = progressMap_.find(mode_); it != progressMap_.end() && it->second != nullptr) {
        g_currentPercent = value;
        it->second->ShowProgress(value);
        return;
    }
    LOG(ERROR) << "progress is null, can't show progress";
}

bool UpdaterUiFacade::IsInProgress() const
{
    if (auto [res, it] = CheckMode(); res) {
        return pgMgr_[it->second.progressPage.progressPageId].IsVisible();
    }
    return false;
}

void UpdaterUiFacade::SetLogoVisible(bool isVisible) const
{
    if (!CheckMode().first) {
        return;
    }
    if (auto it = logoMap_.find(mode_); it != logoMap_.end() && it->second != nullptr) {
        isVisible ? it->second->Show() : it->second->Hide();
        return;
    }
    LOG(ERROR) << "logo is null, can't show logo";
}

void UpdaterUiFacade::SetProgressVisible(bool isVisible) const
{
    if (!CheckMode().first) {
        return;
    }
    if (auto it = progressMap_.find(mode_); it != progressMap_.end() && it->second != nullptr) {
        isVisible ? it->second->Show() : it->second->Hide();
        return;
    }
    LOG(ERROR) << "progress is null, can't show progress";
}

void UpdaterUiFacade::SetBackgroundVisible(bool isVisible) const
{
    if (!CheckMode().first) {
        return;
    }
    if (auto it = backgroundMap_.find(mode_); it != backgroundMap_.end() && it->second != nullptr) {
        isVisible ? it->second->Show() : it->second->Hide();
        return;
    }
    LOG(ERROR) << "background is null, skip background control";
}

void UpdaterUiFacade::ShowProgressWarning(bool isShow) const
{
    if (auto [res, it] = CheckMode(); res) {
        auto &progressPg = it->second.progressPage;
        pgMgr_[progressPg.progressPageId][progressPg.warningComId]->SetZIndex(20); // 20: Raise the level
        pgMgr_[progressPg.progressPageId][progressPg.warningComId]->SetVisible(isShow);
    }
}

void UpdaterUiFacade::ShowProgressPage() const
{
    auto [res, it] = CheckMode();
    if (IsInProgress() || !res) {
        return;
    }
    SetProgressVisible(true);
    SetLogoVisible(true);
    SetBackgroundVisible(true);
    ShowProgress(0);
    pgMgr_.ShowPage(it->second.progressPage.progressPageId);
    ShowProgressWarning(false);
}

void UpdaterUiFacade::ShowSuccessPage() const
{
    auto [res, it] = CheckMode();
    if (!res) {
        return;
    }
    LOG(DEBUG) << "show success page";
    SetProgressVisible(false);
    SetLogoVisible(false);
    SetBackgroundVisible(false);
    ShowProgressWarning(false);

    const auto &resPage = it->second.resPage;
    const PageBackground *bgCfg = FindPageBackground(it->second.pageBackgrounds, resPage.successPageId);
    if (bgCfg != nullptr && !bgCfg->backgroundComId.empty() && !bgCfg->backgroundType.empty()) {
        ComInfo bgId = {resPage.successPageId, bgCfg->backgroundComId};
        auto background = BackgroundStrategy::Factory(bgCfg->backgroundType, bgId, bgCfg->foregroundComIds);
        if (background) {
            background->Show();
        }
    }

    pgMgr_.ShowPage(it->second.resPage.successPageId);
}

void UpdaterUiFacade::ShowFailedPage() const
{
    auto [res, it] = CheckMode();
    if (!res) {
        return;
    }
    LOG(DEBUG) << "show failed page";
    SetProgressVisible(false);
    SetLogoVisible(false);
    ShowProgressWarning(false);
    pgMgr_.ShowPage(it->second.resPage.failPageId);
}

void UpdaterUiFacade::ShowFactoryConfirmPage()
{
    auto [res, it] = CheckMode();
    if (!res) {
        return;
    }
    LOG(DEBUG) << "show confirm page";
    ClearLog();
    pgMgr_.ShowPage(it->second.confirmPageId);
}

void UpdaterUiFacade::ShowMainpage() const
{
    pgMgr_.ShowMainPage();
}

void UpdaterUiFacade::ClearText() const
{
    auto [res, it] = CheckMode();
    if (!res) {
        return;
    }
    ClearLog();
    ShowMsg(it->second.labelUpdId, "");
}

void UpdaterUiFacade::ClearLog() const
{
    if (auto [res, it] = CheckMode(); res) {
        ShowMsg(it->second.labelLogId, "");
        ShowMsg(it->second.labelLogResId, "");
    }
}

void UpdaterUiFacade::ShowMsg(const ComInfo &id, const std::string &tag, bool isClear) const
{
    if (isClear) {
        LOG(INFO) << "clear all log label's text";
        ClearText();
    }
    pgMgr_[id.pageId][id.comId].As<TextLabelAdapter>()->SetText(tag);
}

void UpdaterUiFacade::ShowMsg(const ComInfo &id, const std::string &tag) const
{
    pgMgr_[id.pageId][id.comId].As<TextLabelAdapter>()->SetText(tag);
}

void UpdaterUiFacade::SetLogoProgress()
{
    auto [res, it] = CheckMode();
    if (!res) {
        return;
    }
    const ProgressPage &progressPage { it->second.progressPage };
    if (progressMap_.find(mode_) == progressMap_.end()) {
        progressMap_[mode_] = ProgressStrategy::Factory(progressPage.progressType, {
            progressPage.progressPageId, progressPage.progressComId
        });
    }
    if (logoMap_.find(mode_) == logoMap_.end()) {
        logoMap_[mode_] = LogoStrategy::Factory(progressPage.logoType, {
            progressPage.progressPageId, progressPage.logoComId
        });
    }

    const PageBackground *bgCfg = FindPageBackground(it->second.pageBackgrounds, progressPage.progressPageId);
    if (backgroundMap_.find(mode_) == backgroundMap_.end() && bgCfg != nullptr &&
        !bgCfg->backgroundComId.empty() && !bgCfg->backgroundType.empty()) {
        backgroundMap_[mode_] = BackgroundStrategy::Factory(bgCfg->backgroundType,
            {progressPage.progressPageId, bgCfg->backgroundComId}, bgCfg->foregroundComIds);
    }
}

const PageBackground *UpdaterUiFacade::FindPageBackground(const std::vector<PageBackgroundCfg> &pageBackgrounds,
    const std::string &pageId) const
{
    for (const auto &cfg : pageBackgrounds) {
        if (cfg.pageId == pageId) {
            return &cfg.background;
        }
    }
    return nullptr;
}

void UpdaterUiFacade::Sleep(int ms) const
{
    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}

void UpdaterUiFacade::SaveScreen() const
{
    Sleep(50); /* wait for page flush 50ms */
    UpdaterUiTools::SaveUxBuffToFile("/tmp/mainpage.png");
}

void OnKeyUpEvent()
{
    UpdaterUiFacade::GetInstance().ShowProgressWarning(false);
}

void OnKeyDownEvent()
{
    UpdaterUiFacade::GetInstance().ShowProgressWarning(true);
}
} // namespace Updater