/*

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



#include <cerrno>

#include <sys/resource.h>

#include <sys/syscall.h>



#include <hitrace_meter.h>

#include "parameters.h"

#include "perform_reporter.h"

#include "scene_session_dirty_manager.h"

#include "screen_session_manager_client/include/screen_session_manager_client.h"

#include "session_manager/include/scene_session_manager.h"

#include "session/host/include/session_change_recorder.h"



namespace OHOS {

namespace Rosen {



namespace {

constexpr HiviewDFX::HiLogLabel LABEL = { LOG_CORE, HILOG_DOMAIN_WINDOW, "SceneInputManager" };

const std::string SCENE_INPUT_MANAGER_THREAD = "SceneInputManager";

const std::string FLUSH_DISPLAY_INFO_THREAD = "OS_FlushDisplayInfoThread";



constexpr int MAX_WINDOWINFO_NUM = 15;

constexpr int DEFALUT_DISPLAYID = 0;

constexpr int EMPTY_FOCUS_WINDOW_ID = -1;

constexpr int INVALID_PERSISTENT_ID = 0;

constexpr int DEFAULT_SCREEN_POS = 0;

constexpr int DEFAULT_SCREEN_SCALE = 100;

constexpr int DEFAULT_EXPAND_HEIGHT = 0;

constexpr int DELAY_REPORT_TIME = 3000;

constexpr int FLUSH_DISPLAY_INFO_THREAD_PRIORITY = -20;

constexpr float DIRECTION90 = 90.0F;



bool IsEqualUiExtentionWindowInfo(const std::vector<MMI::WindowInfo>& a, const std::vector<MMI::WindowInfo>& b);

constexpr unsigned int TRANSFORM_DATA_LEN = 9;



bool operator!=(const MMI::Rect& a, const MMI::Rect& b)

{

    if (a.x != b.x || a.y != b.y || a.width != b.width || a.height != b.height) {

        return true;

    }

    return false;

}



bool operator==(const MMI::ScreenInfo& a, const MMI::ScreenInfo& b)

{

    if (a.id != b.id || a.uniqueId != b.uniqueId || a.screenType != b.screenType ||

        a.width != b.width || a.height != b.height ||

        a.physicalWidth != b.physicalWidth || a.physicalHeight != b.physicalHeight ||

        static_cast<int32_t>(a.tpDirection) != static_cast<int32_t>(b.tpDirection) ||

        a.dpi != b.dpi || a.ppi != b.ppi) {

        return false;

    }

    if (a.rotation != b.rotation) {

        return false;

    }

    return true;

}



bool operator==(const MMI::DisplayInfo& a, const MMI::DisplayInfo& b)

{

    if (a.id != b.id || a.x != b.x || a.y != b.y || a.width != b.width ||

        a.height != b.height || a.dpi != b.dpi || a.name != b.name ||

        static_cast<int32_t>(a.direction) != static_cast<int32_t>(b.direction) ||

        static_cast<int32_t>(a.displayDirection) != static_cast<int32_t>(b.displayDirection) ||

        static_cast<int32_t>(a.displayMode) != static_cast<int32_t>(b.displayMode) ||

        a.transform != b.transform || a.scalePercent != b.scalePercent || a.expandHeight != b.expandHeight ||

        a.isCurrentOffScreenRendering != b.isCurrentOffScreenRendering || a.displaySourceMode != b.displaySourceMode ||

        a.oneHandX != b.oneHandX || a.oneHandY != b.oneHandY || a.screenArea.id != b.screenArea.id ||

        a.screenArea.area != b.screenArea.area || a.rsId != b.rsId) {

        return false;

    }

    if (a.offsetX != b.offsetX || a.offsetY != b.offsetY || a.pointerActiveWidth != b.pointerActiveWidth ||

        a.pointerActiveHeight != b.pointerActiveHeight) {

        return false;

    }

    if (a.deviceRotation != b.deviceRotation || a.rotationCorrection != b.rotationCorrection) {

        return false;

    }

    return true;

}



bool operator!=(const std::vector<float>& a, const std::vector<float>& b)

{

    if (a.size() != b.size()) {

        return true;

    }

    int sizeOfA = static_cast<int>(a.size());

    for (int index = 0; index < sizeOfA; index++) {

        if (a[index] != b[index]) {

            return true;

        }

    }

    return false;

}



bool IsEqualWindowInfo(const MMI::WindowInfo& a, const MMI::WindowInfo& b)

{

    if (a.id != b.id || a.pid != b.pid || a.uid != b.uid || a.agentWindowId != b.agentWindowId || a.flags != b.flags ||

        a.displayId != b.displayId || a.zOrder != b.zOrder || a.agentPid != b.agentPid) {

        return false;

    }



    if (a.windowInputType != b.windowInputType || a.privacyMode != b.privacyMode ||

        a.windowType != b.windowType || a.pixelMap != b.pixelMap) {

        return false;

    }

    return true;

}



bool operator==(const MMI::WindowInfo& a, const MMI::WindowInfo& b)

{

    if (!IsEqualWindowInfo(a, b)) {

        return false;

    }



    if (a.area != b.area || a.defaultHotAreas.size() != b.defaultHotAreas.size() ||

        a.pointerHotAreas.size() != b.pointerHotAreas.size() ||

        a.pointerChangeAreas.size() != b.pointerChangeAreas.size() || a.transform.size() != b.transform.size()) {

        return false;

    }



    int sizeOfDefaultHotAreas = static_cast<int>(a.defaultHotAreas.size());

    for (int index = 0; index < sizeOfDefaultHotAreas; index++) {

        if (a.defaultHotAreas[index] != b.defaultHotAreas[index]) {

            return false;

        }

    }

    int sizeOfPointerHotAreas = static_cast<int>(a.pointerHotAreas.size());

    for (int index = 0; index < sizeOfPointerHotAreas; index++) {

        if (a.pointerHotAreas[index] != b.pointerHotAreas[index]) {

            return false;

        }

    }

    int sizeOfPointerChangeAreas = static_cast<int>(a.pointerChangeAreas.size());

    for (int index = 0; index < sizeOfPointerChangeAreas; index++) {

        if (a.pointerChangeAreas[index] != b.pointerChangeAreas[index]) {

            return false;

        }

    }



    if (a.transform != b.transform) {

        return false;

    }

    if (!IsEqualUiExtentionWindowInfo(a.uiExtentionWindowInfo, b.uiExtentionWindowInfo)) {

        return false;

    }

    return true;

}



bool operator!=(const MMI::WindowInfo& a, const MMI::WindowInfo& b)

{

    if (a == b) {

        return false;

    }

    return true;

}



bool IsEqualUiExtentionWindowInfo(const std::vector<MMI::WindowInfo>& a, const std::vector<MMI::WindowInfo>& b)

{

    if (a.size() != b.size()) {

        return false;

    }

    int size = static_cast<int>(a.size());

    for (int i = 0; i < size; i++) {

        if (a[i] != b[i]) {

            return false;

        }

    }

    return true;

}



std::string DumpTransformInDisplayInfo(const std::vector<float>& transform)

{

    std::stringstream stream("[");

    for (float transformItem : transform) {

        stream << transformItem << ",";

    }

    stream << "]";

    return stream.str();

}



std::string DumpDisplayInfo(const MMI::DisplayInfo& info)

{

    std::ostringstream infoStream("DisplayInfo: ");

    infoStream << " id: " << info.id << " x: " << info.x << " y: " << info.y

               << " width: " << info.width << " height: " << info.height << " dpi: " << info.dpi

               << " name: " << info.name

               << " direction: " << static_cast<int>(info.direction)

               << " direction: " << static_cast<int>(info.displayDirection)

               << " displayMode: " << static_cast<int>(info.displayMode)

               << " transform: " << DumpTransformInDisplayInfo(info.transform);

    std::string infoStr = infoStream.str();

    return infoStr;

}

} //namespace





WM_IMPLEMENT_SINGLE_INSTANCE(SceneInputManager)



void SceneInputManager::Init()

{

    sceneSessionDirty_ = std::make_shared<SceneSessionDirtyManager>();

    eventLoop_ = AppExecFwk::EventRunner::Create(FLUSH_DISPLAY_INFO_THREAD);

    eventHandler_ = std::make_shared<AppExecFwk::EventHandler>(eventLoop_);



    // Fix the priority issue of FlushWindowInfoToMMI, raising the priority to 40.

    eventHandler_->PostTask([]() {

        int tid = syscall(SYS_gettid);

        if (setpriority(PRIO_PROCESS, tid, FLUSH_DISPLAY_INFO_THREAD_PRIORITY) != 0) {

            TLOGNE(WmsLogTag::WMS_EVENT, "Failed to set thread priority, errno=%{public}d", errno);

        } else {

            TLOGNI(WmsLogTag::WMS_EVENT, "FlushDisplayInfo thread priority set to %{public}d",

                FLUSH_DISPLAY_INFO_THREAD_PRIORITY);

        }

    });

    SceneSession::RegisterGetConstrainedModalExtWindowInfo(

        [](const sptr<SceneSession>& sceneSession) -> std::optional<ExtensionWindowEventInfo> {

            return SceneInputManager::GetInstance().GetConstrainedModalExtWindowInfo(sceneSession);

        });

}



void SceneInputManager::RegisterFlushWindowInfoCallback(FlushWindowInfoCallback&& callback)

{

    sceneSessionDirty_->RegisterFlushWindowInfoCallback(std::move(callback));

}



void SceneInputManager::ResetSessionDirty()

{

    sceneSessionDirty_->ResetSessionDirty();

}



auto SceneInputManager::GetFullWindowInfoList() -> FullInfoForMMI

{

    return sceneSessionDirty_->GetFullWindowInfoList();

}



void SceneInputManager::UpdateHotAreas(const sptr<SceneSession>& sceneSession,

    std::vector<MMI::Rect>& touchHotAreas, std::vector<MMI::Rect>& pointerHotAreas) const

{

    sceneSessionDirty_->UpdateHotAreas(sceneSession, touchHotAreas, pointerHotAreas);

}



std::vector<MMI::ScreenInfo> SceneInputManager::ConstructScreenInfos(

    std::map<ScreenId, ScreenProperty>& screensProperties)

{

    std::vector<MMI::ScreenInfo> screenInfos;

    if (screensProperties.empty()) {

        TLOGE(WmsLogTag::WMS_EVENT, "screensProperties is empty");

        return screenInfos;

    }

    for (auto& [screenId, screenProperty] : screensProperties) {

        MMI::ScreenInfo screenInfo = {

            .id = screenId,

            .uniqueId = "default" + std::to_string(screenProperty.GetRsId()),

            .screenType = static_cast<MMI::ScreenType>(screenProperty.GetScreenType()),

            .width = screenProperty.GetScreenRealWidth(),

            .height = screenProperty.GetScreenRealHeight(),

            .physicalWidth = screenProperty.GetPhyWidth(),

            .physicalHeight = screenProperty.GetPhyHeight(),

            .tpDirection = ConvertDegreeToMMIRotation(screenProperty.GetDefaultDeviceRotationOffset()),

            .dpi = screenProperty.GetScreenRealPPI(),

            .ppi = screenProperty.GetXDpi(),

            .rotation = ConvertToMMIRotation(screenProperty.GetRotation())

        };

        screenInfos.emplace_back(screenInfo);

    }

    return screenInfos;

}



void SceneInputManager::ConstructDisplayGroupInfos(std::map<ScreenId, ScreenProperty>& screensProperties,

    std::map<DisplayGroupId, MMI::DisplayGroupInfo>& displayGroupMap)

{

    if (screensProperties.empty()) {

        TLOGE(WmsLogTag::WMS_EVENT, "screensProperties is empty");

        return;

    }

    auto displayMode = ScreenSessionManagerClient::GetInstance().GetFoldDisplayMode();

    for (auto& [screenId, screenProperty] : screensProperties) {

        auto screenSession = ScreenSessionManagerClient::GetInstance().GetScreenSessionById(screenId);

        if (screenSession == nullptr) {

            TLOGE(WmsLogTag::WMS_EVENT, "screenSession get failed, screenId: %{public}" PRIu64"", screenId);

            continue;

        }

        auto transform = Matrix3f::IDENTITY;

        Vector2f scale(screenProperty.GetScaleX(), screenProperty.GetScaleY());

        transform = transform.Scale(scale, screenProperty.GetPivotX() * screenProperty.GetBounds().rect_.GetWidth(),

            screenProperty.GetPivotY() * screenProperty.GetBounds().rect_.GetHeight()).Inverse();

        std::vector<float> transformData(transform.GetData(), transform.GetData() + TRANSFORM_DATA_LEN);

        int32_t screenOneHandX = DEFAULT_SCREEN_POS;

        int32_t screenOneHandY = DEFAULT_SCREEN_POS;

        int32_t scalePercent = DEFAULT_SCREEN_SCALE;

        int32_t expandHeight = DEFAULT_EXPAND_HEIGHT;

        const SingleHandScreenInfo& singleHandScreenInfo = SceneSessionManager::GetInstance().GetSingleHandScreenInfo();

        if (screenId == ScreenSessionManagerClient::GetInstance().GetDefaultScreenId() &&

            singleHandScreenInfo.mode != SingleHandMode::MIDDLE) {

            SingleHandTransform singleHandTransform = SceneSessionManager::GetInstance().GetNormalSingleHandTransform();

            WSRect originRect = SceneSessionManager::GetInstance().GetOriginRect();

            screenOneHandX = singleHandTransform.posX;

            screenOneHandY = singleHandTransform.posY;

            scalePercent = singleHandTransform.scaleX * DEFAULT_SCREEN_SCALE;

            expandHeight = screenProperty.GetBounds().rect_.GetHeight() - originRect.height_;

        }

        int32_t deviceRotation = static_cast<int32_t>(screenProperty.GetDeviceRotation());

        int32_t rotationCorrection = static_cast<int32_t>(screenSession->GetRotationCorrection(displayMode));

        MMI::DisplayInfo displayInfo = {

            .id = screenId,

            .x = screenProperty.GetX(),

            .y = screenProperty.GetY(),

            .width = screenProperty.GetValidWidth(),

            .height =screenProperty.GetValidHeight(),

            .dpi = screenProperty.GetDensity() *  DOT_PER_INCH,

            .name = "display" + std::to_string(screenId),

            .direction = ConvertDegreeToMMIRotation(screenProperty.GetPhysicalRotation()),

            .displayDirection = ConvertDegreeToMMIRotation(screenProperty.GetScreenComponentRotation()),

            .displayMode = static_cast<MMI::DisplayMode>(displayMode),

            .transform = transformData,

            .scalePercent = scalePercent,

            .expandHeight = expandHeight,

            .isCurrentOffScreenRendering = screenProperty.GetCurrentOffScreenRendering(),

            .displaySourceMode = static_cast<MMI::DisplaySourceMode>(screenSession->GetScreenCombination()),

            .oneHandX = screenOneHandX,

            .oneHandY = screenOneHandY,

            .screenArea = {

                .id = screenId,

                .area = {screenProperty.GetScreenAreaOffsetX(), screenProperty.GetScreenAreaOffsetY(),

                    screenProperty.GetScreenAreaWidth(), screenProperty.GetScreenAreaHeight()}

            },

            .rsId = screenProperty.GetRsId(),

            .offsetX = screenProperty.GetInputOffsetX(),

            .offsetY = screenProperty.GetInputOffsetY(),

            .pointerActiveWidth = screenProperty.GetPointerActiveWidth(),

            .pointerActiveHeight = screenProperty.GetPointerActiveHeight(),

            .deviceRotation = ConvertDegreeToMMIRotation(deviceRotation * DIRECTION90),

            .rotationCorrection = ConvertDegreeToMMIRotation(rotationCorrection * DIRECTION90)

        };

        DisplayGroupId displayGroupId = screenSession->GetDisplayGroupId();

        if (displayGroupMap.count(displayGroupId) == 0) {

            MMI::DisplayGroupInfo displayGroupInfo = {

                .id = displayGroupId,

                .name = "displayGroup" + std::to_string(displayGroupId),

                .type = displayGroupId == 0 ? MMI::GROUP_DEFAULT : MMI::GROUP_SPECIAL,

                .mainDisplayId = screenProperty.GetMainDisplayIdOfGroup(),

            };

            displayGroupMap[displayGroupId] = displayGroupInfo;

        }

        displayGroupMap[displayGroupId].displaysInfo.emplace_back(displayInfo);

    }

}



std::unordered_map<DisplayId, int32_t> SceneInputManager::GetFocusedSessionMap() const

{

    std::unordered_map<DisplayId, int32_t> focusInfoMap;

    auto focusInfoMapArray = SceneSessionManager::GetInstance().GetAllFocusedSessionList();

    for (const auto& item : focusInfoMapArray) {

        int32_t focusedSessionId = item.second;

        auto focusedSession = SceneSessionManager::GetInstance().GetSceneSession(focusedSessionId);

        if (focusedSession == nullptr) {

            TLOGE(WmsLogTag::WMS_EVENT, "focusedSession is null, id: %{public}d", focusedSessionId);

            focusInfoMap[item.first] = INVALID_PERSISTENT_ID;

            continue;

        }

        if (auto modalUIExtensionEventInfo = focusedSession->GetLastModalUIExtensionEventInfo()) {

            focusedSessionId = modalUIExtensionEventInfo.value().persistentId;

        }

        focusInfoMap[item.first] = focusedSessionId;

    }

    return focusInfoMap;

}



void SceneInputManager::FlushFullInfoToMMI(const std::vector<MMI::ScreenInfo>& screenInfos,

    std::map<DisplayGroupId, MMI::DisplayGroupInfo>& displayGroupMap,

    const std::vector<MMI::WindowInfo>& windowInfoList,

    const std::vector<MMI::UIExtensionInfo>& uiExtensionInfoList, bool isOverBatchSize)

{

    auto focusInfoMap = GetFocusedSessionMap();

    std::unordered_map<DisplayGroupId, std::vector<MMI::WindowInfo>> windowInfoMap;

    for (const auto& windowInfo : windowInfoList) {

        windowInfoMap[windowInfo.groupId].emplace_back(windowInfo);

    }

    std::vector<MMI::DisplayGroupInfo> displayGroupInfos;

    for (auto& [displayGroupId, displayGroup] : displayGroupMap) {

        if (!isOverBatchSize && !windowInfoMap[displayGroupId].empty()) {

            windowInfoMap[displayGroupId].back().action = MMI::WINDOW_UPDATE_ACTION::ADD_END;

        }

        displayGroup.windowsInfo = windowInfoMap[displayGroupId];

        displayGroup.focusWindowId = focusInfoMap[displayGroupId];

        displayGroupInfos.emplace_back(displayGroup);

    }

    MMI::UserScreenInfo userScreenInfo = {

        .userId = currentUserId_,

        .userState = MMI::USER_ACTIVE,

        .screens = screenInfos,

        .displayGroups = displayGroupInfos,

        .uiExtensionInfos = uiExtensionInfoList

    };

    MMI::InputManager::GetInstance()->UpdateDisplayInfo(userScreenInfo);



    for (auto groupInfo : displayGroupInfos) {

        TLOGD(WmsLogTag::WMS_EVENT, "[EventDispatch] - displayGroupId: %{public}d", groupInfo.id);

        for (const auto& displayInfo : groupInfo.displaysInfo) {

            TLOGD(WmsLogTag::WMS_EVENT, "[EventDispatch] - %{public}s", DumpDisplayInfo(displayInfo).c_str());

        }

        std::string windowInfoListDump = "windowinfo  ";

        for (const auto& windowInfo : groupInfo.windowsInfo) {

            windowInfoListDump.append(DumpWindowInfo(windowInfo).append("  ||  "));

        }

        TLOGD(WmsLogTag::WMS_EVENT, "[EventDispatch] - %{public}s", windowInfoListDump.c_str());

    }

}



void SceneInputManager::FlushEmptyInfoToMMI()

{

    auto task = [this]() {

        std::map<ScreenId, ScreenProperty> screensProperties =

            ScreenSessionManagerClient::GetInstance().GetAllScreensProperties();

        std::vector<MMI::ScreenInfo> screenInfos = ConstructScreenInfos(screensProperties);

        std::map<DisplayGroupId, MMI::DisplayGroupInfo> displayGroupMap;

        ConstructDisplayGroupInfos(screensProperties, displayGroupMap);

        std::vector<MMI::DisplayGroupInfo> displayGroupInfos;

        for (auto& [displayGroupId, displayGroup] : displayGroupMap) {

            displayGroupInfos.emplace_back(displayGroup);

        }

        MMI::UserScreenInfo userScreenInfo = {

            .userId = currentUserId_,

            .userState = MMI::USER_INACTIVE,

            .screens = screenInfos,

            .displayGroups = displayGroupInfos

        };

        TLOGNI(WmsLogTag::WMS_EVENT, "userId:%{public}d", currentUserId_);

        MMI::InputManager::GetInstance()->UpdateDisplayInfo(userScreenInfo);

    };

    if (eventHandler_) {

        eventHandler_->PostTask(task);

    }

}



void SceneInputManager::NotifyWindowInfoChange(const sptr<SceneSession>& sceneSession, const WindowUpdateType& type)

{

    if (sceneSessionDirty_) {

        sceneSessionDirty_->NotifyWindowInfoChange(sceneSession, type);

    }

}



void SceneInputManager::NotifyMMIWindowPidChange(const sptr<SceneSession>& sceneSession, const bool startMoving)

{

    if (sceneSessionDirty_) {

        sceneSessionDirty_->NotifyWindowInfoChange(sceneSession,

            WindowUpdateType::WINDOW_UPDATE_PROPERTY, startMoving);

        if (sceneSession == nullptr) {

            return;

        }

        sceneSession->SetIsStartMoving(startMoving);

    }

}



void SceneInputManager::NotifyWindowInfoChangeFromSession(const sptr<SceneSession>& sceneSesion)

{

    if (sceneSessionDirty_) {

        sceneSessionDirty_->NotifyWindowInfoChange(sceneSesion, WindowUpdateType::WINDOW_UPDATE_PROPERTY);

    } else {

        TLOGD(WmsLogTag::WMS_EVENT, "sceneSessionDirty is nullptr");

    }

}



void SceneInputManager::FlushChangeInfoToMMI(const std::map<uint64_t, std::vector<MMI::WindowInfo>>& screenId2Windows)

{

    for (auto& iter : screenId2Windows) {

        auto displayId = iter.first;

        auto& windowInfos = iter.second;

        std::string windowInfoListDump = "windowinfo  ";

        for (auto& windowInfo : windowInfos) {

            windowInfoListDump.append(DumpWindowInfo(windowInfo).append("  ||  "));

        }

        TLOGD(WmsLogTag::WMS_EVENT, "[EventDispatch] --- %{public}s", windowInfoListDump.c_str());

        MMI::WindowGroupInfo windowGroup = {focusedSessionId_, displayId, windowInfos};

        MMI::InputManager::GetInstance()->UpdateWindowInfo(windowGroup);

    }

}



bool SceneInputManager::CheckNeedUpdate(const std::vector<MMI::ScreenInfo>& screenInfos,

    const std::vector<MMI::DisplayInfo>& displayInfos, const std::vector<MMI::WindowInfo>& windowInfoList)

{

    int32_t focusId = SceneSessionManager::GetInstance().GetFocusedSessionId();

    if (focusId != lastFocusId_) {

        lastFocusId_ = focusId;

        lastScreenInfos_ = screenInfos;

        lastDisplayInfos_ = displayInfos;

        lastWindowInfoList_ = windowInfoList;

        return true;

    }



    if (screenInfos.size() != lastScreenInfos_.size() || displayInfos.size() != lastDisplayInfos_.size() ||

        windowInfoList.size() != lastWindowInfoList_.size()) {

        lastScreenInfos_ = screenInfos;

        lastDisplayInfos_ = displayInfos;

        lastWindowInfoList_ = windowInfoList;

        return true;

    }



    int sizeOfScreenInfos = static_cast<int>(screenInfos.size());

    for (int index = 0; index < sizeOfScreenInfos; index++) {

        if (!(screenInfos[index] == lastScreenInfos_[index])) {

            lastScreenInfos_ = screenInfos;

            lastDisplayInfos_ = displayInfos;

            lastWindowInfoList_ = windowInfoList;

            return true;

        }

    }



    int sizeOfDisplayInfos = static_cast<int>(displayInfos.size());

    for (int index = 0; index < sizeOfDisplayInfos; index++) {

        if (!(displayInfos[index] == lastDisplayInfos_[index])) {

            lastDisplayInfos_ = displayInfos;

            lastWindowInfoList_ = windowInfoList;

            return true;

        }

    }



    int sizeOfWindowInfoList = static_cast<int>(windowInfoList.size());

    for (int index = 0; index < sizeOfWindowInfoList; index++) {

        if (!(windowInfoList[index] == lastWindowInfoList_[index])) {

            lastWindowInfoList_ = windowInfoList;

            return true;

        }

    }

    return false;

}



void SceneInputManager::UpdateFocusedSessionId(int32_t focusedSessionId)

{

    auto focusedSceneSession = SceneSessionManager::GetInstance().GetSceneSession(focusedSessionId);

    if (focusedSceneSession == nullptr) {

        TLOGE(WmsLogTag::WMS_EVENT, "focusedSceneSession is null");

        return;

    }

    if (auto modalUIExtensionEventInfo = focusedSceneSession->GetLastModalUIExtensionEventInfo()) {

        focusedSessionId_ = modalUIExtensionEventInfo.value().persistentId;

    }

}



void DumpUIExtentionWindowInfo(const MMI::WindowInfo& windowInfo)

{

    auto sceneSession = SceneSessionManager::GetInstance().GetSceneSession(windowInfo.id);

    if (sceneSession == nullptr) {

        TLOGE(WmsLogTag::WMS_EVENT, "sceneSession is null");

        return;

    }

    auto surfaceNode = sceneSession->GetSurfaceNode();

    if (surfaceNode == nullptr) {

        TLOGE(WmsLogTag::WMS_EVENT, "surfaceNode is null");

        return;

    }

    auto surfaceId = surfaceNode->GetId();

    std::ostringstream dumpUecWindowInfo;

    dumpUecWindowInfo << "DumpUecWindowInfo:" << windowInfo.id << ";"<< surfaceId;

    dumpUecWindowInfo << ";" << windowInfo.uiExtentionWindowInfo.size();

    for (const auto& uiExtWindowInfo : windowInfo.uiExtentionWindowInfo) {

        auto str = DumpWindowInfo(uiExtWindowInfo);

        str = "sec:" + std::to_string(uiExtWindowInfo.privacyUIFlag) + " " + str;

        dumpUecWindowInfo << ";{" << str << "}";

    }

    TLOGND(WmsLogTag::WMS_EVENT, "DumpUecWindowInfo: wid;surfaceId;uecInfoSize"

        "{wInfo:wId|pid|agentPid|uid|[x,y,width,height]|agentWindowId|flags|displayId|action|zOrder"

        ",hot:[x,y,width,height]}");

    TLOGNI(WmsLogTag::WMS_EVENT, "%{public}s", dumpUecWindowInfo.str().c_str());

}



void SceneInputManager::PrintWindowInfo(const std::vector<MMI::WindowInfo>& windowInfoList)

{

    int windowListSize = static_cast<int>(windowInfoList.size());

    std::ostringstream idListStream;

    std::ostringstream dumpWindowListStream;

    static std::string lastIdList;

    static uint32_t windowEventID = 0;

    if (windowEventID == UINT32_MAX) {

        windowEventID = 0;

    }

    focusedSessionId_ = SceneSessionManager::GetInstance().GetFocusedSessionId();

    std::unordered_map<int32_t, MMI::Rect> currWindowDefaultHotArea;

    static std::unordered_map<int32_t, MMI::Rect> lastWindowDefaultHotArea;

    MMI::WindowInfo focusedWindow;

    for (auto& e : windowInfoList) {

        idListStream << e.id << "|" << e.displayId << "|" << e.flags << "|" << e.zOrder << "|"

                     << e.pid << "|" << e.agentPid << "|" << e.defaultHotAreas.size();



        if (e.defaultHotAreas.size() > 0) {

            auto iter = lastWindowDefaultHotArea.find(e.id);

            if (iter == lastWindowDefaultHotArea.end() || iter->second != e.defaultHotAreas[0]) {

                idListStream << "|" << e.defaultHotAreas[0].x << "," << e.defaultHotAreas[0].y

                             << "," << e.defaultHotAreas[0].width << "," << e.defaultHotAreas[0].height;

            }

            currWindowDefaultHotArea.insert({e.id, e.defaultHotAreas[0]});

        }

        idListStream << ";";

        if ((focusedSessionId_ == e.id) && (e.id == e.agentWindowId)) {

            focusedWindow = e;

            UpdateFocusedSessionId(focusedSessionId_);

        }

        if (e.uiExtentionWindowInfo.size() > 0) {

            DumpUIExtentionWindowInfo(e);

        }

        ConstructDumpWindowInfo(e, dumpWindowListStream);

    }

    lastWindowDefaultHotArea = currWindowDefaultHotArea;

    SingleHandTransform transform = SceneSessionManager::GetInstance().GetNormalSingleHandTransform();

    idListStream << focusedSessionId_ << "|" << transform.posX << "," << transform.posY

        << "|" << transform.scaleX << "," << transform.scaleY;

    if (focusedWindow.defaultHotAreas.size() > 0) {

        idListStream << "|" << focusedWindow.defaultHotAreas[0].x << "," << focusedWindow.defaultHotAreas[0].y << ","

                     << focusedWindow.defaultHotAreas[0].width << "," << focusedWindow.defaultHotAreas[0].height;

    }

    std::string idList = idListStream.str();

    if (lastIdList != idList) {

        windowEventID++;

        TLOGNI(WmsLogTag::WMS_EVENT, "LogWinInfo: eid:%{public}d,size:%{public}d,idList:%{public}s",

            windowEventID, windowListSize, idList.c_str());

        lastIdList = idList;

    }

    std::string dumpWindowList = dumpWindowListStream.str();

    SceneSessionChangeInfo changeInfo {

        .changeInfo_ = "WindowInfos: " + dumpWindowList,

        .logTag_ = WmsLogTag::WMS_EVENT,

    };

    SessionChangeRecorder::GetInstance().RecordSceneSessionChange(RecordType::EVENT_RECORD, changeInfo);

}



void SceneInputManager::PrintScreenInfo(const std::vector<MMI::ScreenInfo>& screenInfos)

{

    int screenListSize = static_cast<int>(screenInfos.size());

    std::ostringstream screenListStream;

    static std::string lastScreenList = "";

    for (auto& screenInfo : screenInfos) {

        screenListStream << screenInfo.id << "|" << screenInfo.uniqueId << "|"

                         << static_cast<uint32_t>(screenInfo.screenType) << "|"

                         << screenInfo.width << "|" << screenInfo.height << "|"

                         << screenInfo.physicalWidth << "|" << screenInfo.physicalHeight << "|"

                         << static_cast<int32_t>(screenInfo.tpDirection) << "|"

                         << screenInfo.dpi << "|" << screenInfo.ppi << ",";

    }



    std::string screenList = screenListStream.str();

    if (lastScreenList != screenList) {

        TLOGI(WmsLogTag::WMS_EVENT, "num:%{public}d,list:%{public}s", screenListSize, screenList.c_str());

        lastScreenList = screenList;

    }

}



void SceneInputManager::PrintDisplayInfo(const std::vector<MMI::DisplayInfo>& displayInfos)

{

    int displayListSize = static_cast<int>(displayInfos.size());

    std::ostringstream displayListStream;

    std::ostringstream dumpDisplayListStream;

    static std::string lastDisplayList = "";

    for (auto& displayInfo : displayInfos) {

        displayListStream << displayInfo.id << "|" << displayInfo.x << "|" << displayInfo.y << "|"

                          << displayInfo.width << "|" << displayInfo.height << "|"

                          << displayInfo.dpi << "|"

                          << static_cast<int32_t>(displayInfo.direction) << "|"

                          << static_cast<int32_t>(displayInfo.displayDirection) << "|"

                          << static_cast<int32_t>(displayInfo.displayMode) << "|"

                          << displayInfo.scalePercent << "|" << displayInfo.expandHeight << "|"

                          << displayInfo.isCurrentOffScreenRendering << "|"

                          << static_cast<uint32_t>(displayInfo.displaySourceMode) << "|" << displayInfo.oneHandX << "|"

                          << displayInfo.oneHandY << "|" << displayInfo.screenArea.area.x << "|"

                          << displayInfo.screenArea.area.y << "|" << displayInfo.screenArea.area.width << "|"

                          << displayInfo.screenArea.area.height << "|" << displayInfo.rsId << ",";

        ConstructDumpDisplayInfo(displayInfo, dumpDisplayListStream);

    }



    std::string displayList = displayListStream.str();

    if (lastDisplayList != displayList) {

        TLOGI(WmsLogTag::WMS_EVENT, "num:%{public}d,list:%{public}s", displayListSize, displayList.c_str());

        lastDisplayList = displayList;

    }

    std::string dumpDisplayList = dumpDisplayListStream.str();

    SceneSessionChangeInfo changeInfo {

        .changeInfo_ = "DisplayInfos: " + dumpDisplayList,

        .logTag_ = WmsLogTag::WMS_EVENT,

    };

    SessionChangeRecorder::GetInstance().RecordSceneSessionChange(RecordType::EVENT_RECORD, changeInfo);

}



void SceneInputManager::SetUserBackground(bool userBackground)

{

    TLOGD(WmsLogTag::WMS_MULTI_USER, "userBackground=%{public}d", userBackground);

    isUserBackground_.store(userBackground);

}



void SceneInputManager::SetIsRotationBegin(bool isRotationBegin)

{

    TLOGD(WmsLogTag::WMS_EVENT, "isRotationBegin=%{public}d", isRotationBegin);

    isRotationBegin_.store(isRotationBegin);

}



void SceneInputManager::SetCurrentUserId(int32_t userId)

{

    TLOGD(WmsLogTag::WMS_MULTI_USER, "Current userId=%{public}d", userId);

    currentUserId_ = userId;

    MMI::InputManager::GetInstance()->SetCurrentUser(userId);

}



void SceneInputManager::UpdateDisplayAndWindowInfo(const std::vector<MMI::ScreenInfo>& screenInfos,

    std::map<DisplayGroupId, MMI::DisplayGroupInfo>& displayGroupMap,

    std::vector<MMI::WindowInfo> windowInfoList,

    const std::vector<MMI::UIExtensionInfo>& uiExtensionInfoList)

{

    if (windowInfoList.size() == 0) {

        FlushFullInfoToMMI(screenInfos, displayGroupMap, windowInfoList, uiExtensionInfoList);

        return;

    }

    int32_t windowBatchSize = MAX_WINDOWINFO_NUM;

    if (windowInfoList[0].defaultHotAreas.size() > MMI::WindowInfo::DEFAULT_HOTAREA_COUNT) {

        windowBatchSize = MMI::InputManager::GetInstance()->GetWinSyncBatchSize(

            static_cast<int32_t>(windowInfoList[0].defaultHotAreas.size()),

            static_cast<int32_t>(displayGroupMap.size()));

    }

    int32_t windowListSize = static_cast<int32_t>(windowInfoList.size());

    if (windowListSize <= windowBatchSize) {

        FlushFullInfoToMMI(screenInfos, displayGroupMap, windowInfoList, uiExtensionInfoList);

        return;

    }

    std::unordered_map<int32_t, std::vector<int32_t>> windowIndexMap;

    int32_t index = 0;

    for (const auto& windowInfo : windowInfoList) {

        windowIndexMap[windowInfo.groupId].emplace_back(index);

        index++;

    }

    for (auto& [displayGroupId, indexInfos] : windowIndexMap) {

        windowInfoList[indexInfos.back()].action = MMI::WINDOW_UPDATE_ACTION::ADD_END;

    }

    auto iterBegin = windowInfoList.begin();

    auto iterEnd = windowInfoList.end();

    auto iterNext = std::next(iterBegin, windowBatchSize);

    FlushFullInfoToMMI(screenInfos, displayGroupMap, std::vector<MMI::WindowInfo>(iterBegin, iterNext),

        uiExtensionInfoList, true);

    while (iterNext != iterEnd) {

        auto iterNewBegin = iterNext;

        if (iterNewBegin->defaultHotAreas.size() <= MMI::WindowInfo::DEFAULT_HOTAREA_COUNT) {

            windowBatchSize = MAX_WINDOWINFO_NUM;

        }

        if (std::distance(iterNewBegin, iterEnd) <= windowBatchSize) {

            iterNext = iterEnd;

        } else {

            iterNext = std::next(iterNewBegin, windowBatchSize);

        }

        std::map<uint64_t, std::vector<MMI::WindowInfo>> screenToWindowInfoList;

        screenToWindowInfoList.emplace(DEFALUT_DISPLAYID, std::vector<MMI::WindowInfo>(iterNewBegin, iterNext));

        FlushChangeInfoToMMI(screenToWindowInfoList);

    }

}



void SceneInputManager::FlushDisplayInfoToMMI(std::vector<MMI::WindowInfo>&& windowInfoList,

                                              std::vector<MMI::UIExtensionInfo>&& uiExtensionInfoList,

                                              std::vector<std::shared_ptr<Media::PixelMap>>&& pixelMapList,

                                              const bool forceFlush)

{

    eventHandler_->PostTask([this, windowInfoList = std::move(windowInfoList),

        uiExtensionInfoList = std::move(uiExtensionInfoList), pixelMapList = std::move(pixelMapList), forceFlush] {

        HITRACE_METER_FMT(HITRACE_TAG_WINDOW_MANAGER, "FlushDisplayInfoToMMI");

        if (isUserBackground_.load()) {

            TLOGND(WmsLogTag::WMS_MULTI_USER, "User in background, no need to flush display info");

            return;

        }

        if (isRotationBegin_.load()) {

            TLOGND(WmsLogTag::WMS_EVENT, "rotating, no need to flush display info");

            return;

        }

        if (sceneSessionDirty_ == nullptr) {

            TLOGNE(WmsLogTag::WMS_EVENT, "sceneSessionDirty_ is nullptr");

            return;

        }

        std::map<ScreenId, ScreenProperty> screensProperties =

            ScreenSessionManagerClient::GetInstance().GetAllScreensProperties();

        std::vector<MMI::ScreenInfo> screenInfos = ConstructScreenInfos(screensProperties);

        std::map<DisplayGroupId, MMI::DisplayGroupInfo> displayGroupMap;

        ConstructDisplayGroupInfos(screensProperties, displayGroupMap);

        if (displayGroupMap.empty()) {

            TLOGNE(WmsLogTag::WMS_EVENT, "displayGroupMap is empty");

            HandleEmptyDisplayGroup();

            return;

        }

        std::vector<MMI::DisplayInfo> displayInfos;

        for (auto& [displayGroupId, displayGroup] : displayGroupMap) {

            for (auto& displayInfo : displayGroup.displaysInfo) {

                displayInfos.emplace_back(displayInfo);

            }

        }

        if (!forceFlush && !CheckNeedUpdate(screenInfos, displayInfos, windowInfoList)) {

            return;

        }

        PrintScreenInfo(screenInfos);

        PrintDisplayInfo(displayInfos);

        PrintWindowInfo(windowInfoList);

        UpdateDisplayAndWindowInfo(screenInfos, displayGroupMap, std::move(windowInfoList), uiExtensionInfoList);

    });

}



void SceneInputManager::UpdateSecSurfaceInfo(const std::map<uint64_t, std::vector<SecSurfaceInfo>>& secSurfaceInfoMap)

{

    if (sceneSessionDirty_ == nullptr) {

        TLOGE(WmsLogTag::WMS_EVENT, "sceneSessionDirty_ is nullptr");

        return;

    }

    sceneSessionDirty_->UpdateSecSurfaceInfo(secSurfaceInfoMap);

}



void SceneInputManager::UpdateConstrainedModalUIExtInfo(

    const std::map<uint64_t, std::vector<SecSurfaceInfo>>& constrainedModalUIExtInfoMap)

{

    if (sceneSessionDirty_ == nullptr) {

        TLOGE(WmsLogTag::WMS_EVENT, "sceneSessionDirty_ is nullptr");

        return;

    }

    sceneSessionDirty_->UpdateConstrainedModalUIExtInfo(constrainedModalUIExtInfoMap);

}



std::optional<ExtensionWindowEventInfo> SceneInputManager::GetConstrainedModalExtWindowInfo(

    const sptr<SceneSession>& sceneSession)

{

    if (sceneSession == nullptr) {

        TLOGE(WmsLogTag::WMS_EVENT, "sceneSession is nullptr");

        return std::nullopt;

    }

    if (sceneSessionDirty_ == nullptr) {

        TLOGE(WmsLogTag::WMS_EVENT, "sceneSessionDirty_ is nullptr");

        return std::nullopt;

    }

    SecSurfaceInfo constrainedModalUIExtInfo;

    if (!sceneSessionDirty_->GetLastConstrainedModalUIExtInfo(sceneSession, constrainedModalUIExtInfo)) {

        return std::nullopt;

    }

    auto persistentId = sceneSession->GetUIExtPersistentIdBySurfaceNodeId(constrainedModalUIExtInfo.uiExtensionNodeId);

    if (persistentId == INVALID_PERSISTENT_ID) {

        TLOGE(WmsLogTag::WMS_EVENT, "invalid persistentId");

        return std::nullopt;

    }

    return ExtensionWindowEventInfo {

        .persistentId = persistentId,

        .pid = constrainedModalUIExtInfo.uiExtensionPid,

        .isConstrainedModal = true };

}



void SceneInputManager::ConstructDumpDisplayInfo(const MMI::DisplayInfo& displayInfo,

    std::ostringstream& dumpDisplayListStream)

{

    std::ostringstream transformStream;

    transformStream << "[";

    for (auto& it : displayInfo.transform) {

        transformStream << it << ", ";

    }

    transformStream << "]";

    dumpDisplayListStream << "id:" << displayInfo.id

                          << "|x:" << displayInfo.x

                          << "|y:" << displayInfo.y

                          << "|width:" << displayInfo.width

                          << "|height:" << displayInfo.height

                          << "|dpi:" << displayInfo.dpi

                          << "|name:" << displayInfo.name

                          << "|direction:" << static_cast<int32_t>(displayInfo.direction)

                          << "|displayDirection:" << static_cast<int32_t>(displayInfo.displayDirection)

                          << "|displayMode:" << static_cast<int32_t>(displayInfo.displayMode)

                          << "|transform:" << transformStream.str()

                          << "|screenOneHandX:" << displayInfo.oneHandX

                          << "|screenOneHandY:" << displayInfo.oneHandY

                          << "|scalePercent:" << displayInfo.scalePercent

                          << "|expandHeight:" << displayInfo.expandHeight

                          << "|isCurrentOffScreenRendering:" << displayInfo.isCurrentOffScreenRendering

                          << "|displaySourceMode:" << static_cast<int32_t>(displayInfo.displaySourceMode)

                          << "|screenArea.id:" << displayInfo.screenArea.id

                          << "|screenArea.area:[" << displayInfo.screenArea.area.x << ","

                          << displayInfo.screenArea.area.y << ","

                          << displayInfo.screenArea.area.width << ","

                          << displayInfo.screenArea.area.height << "]"

                          << "|rsId:" << displayInfo.rsId

                          << "|offsetX:" << displayInfo.offsetX

                          << "|offsetY:" << displayInfo.offsetY

                          << "|pointerActiveWidth:" << displayInfo.pointerActiveWidth

                          << "|pointerActiveHeight:" << displayInfo.pointerActiveHeight << ", ";

}



void SceneInputManager::ConstructDumpWindowInfo(const MMI::WindowInfo& windowInfo,

    std::ostringstream& dumpWindowListStream)

{

    std::ostringstream transformStream;

    transformStream << "[";

    for (auto& it : windowInfo.transform) {

        transformStream << it << ", ";

    }

    transformStream << "]";

    std::ostringstream defaultHotAreasStream;

    for (auto& defaultHotArea : windowInfo.defaultHotAreas) {

        std::ostringstream defaultHotAreaStream;

        defaultHotAreaStream << "[" << defaultHotArea.x << ", " <<defaultHotArea.y << ", "

                             << defaultHotArea.width << ", " <<defaultHotArea.height << "]";

        defaultHotAreasStream << defaultHotAreaStream.str() << " ";

    }

    std::ostringstream pointerHotAreasStream;

    for (auto& pointerHotArea : windowInfo.pointerHotAreas) {

        std::ostringstream pointerHotAreaStream;

        pointerHotAreaStream << "[" << pointerHotArea.x << ", " <<pointerHotArea.y << ", "

                             << pointerHotArea.width << ", " <<pointerHotArea.height << "]";

        pointerHotAreasStream << pointerHotAreaStream.str() << " ";

    }

    std::ostringstream pointerChangeAreasStream;

    pointerChangeAreasStream << "[";

    for (auto& pointerChangeArea : windowInfo.pointerChangeAreas) {

        pointerChangeAreasStream << pointerChangeArea << ", ";

    }

    pointerChangeAreasStream << "]";

    dumpWindowListStream << "id:" << windowInfo.id << "|pid:" << windowInfo.pid << "|uid:" << windowInfo.uid

                          << "|area:[" << windowInfo.area.x << ", " << windowInfo.area.y << ", "

                          << windowInfo.area.width << ", " << windowInfo.area.height << "]"

                          << "|defaultHotAreas:" << defaultHotAreasStream.str()

                          << "|pointerHotAreas:" << pointerHotAreasStream.str() << "|agentWindowId:"

                          << windowInfo.agentWindowId << "|action:" << static_cast<uint32_t>(windowInfo.action)

                          << "|displayId:" << windowInfo.displayId << "|zOrder:" << windowInfo.zOrder

                          << "|pointerChangeAreas:" << pointerChangeAreasStream.str() << "|transform:"

                          << transformStream.str() << "|windowInputType:"

                          << static_cast<int32_t>(windowInfo.windowInputType) << "|windowType:"

                          << windowInfo.windowType << "|isSkipSelfWhenShowOnVirtualScreen:"

                          << windowInfo.isSkipSelfWhenShowOnVirtualScreen << "|windowNameType:"

                          << windowInfo.windowNameType << "|groupId:" << windowInfo.groupId

                          << "|flags:" << windowInfo.flags << ", ";

}



void SceneInputManager::HandleEmptyDisplayGroup()

{

    auto currentState = rootSessionState_.load();

    if (currentState == RootSessionState::CREATED_SUBSEQUENT) {

        TLOGI(WmsLogTag::WMS_EVENT, "Report window frozen");

        WindowInfoReporter::GetInstance().ReportWindowFrozen(

            WindowDFXHelperType::WINDOW_FLUSH_EMPTY_DISPLAY_INFO_TO_MMI_EXCEPTION,

            "displayInfos flush to MMI is empty!");

        return;

    }

    bool hasDelayedTaskScheduled = false;

    if (hasDelayedTaskScheduled_.compare_exchange_strong(hasDelayedTaskScheduled, true)) {

        currentState = rootSessionState_.load();

        if (currentState == RootSessionState::CREATED_FIRST_TIME) {

            auto task = [this]() {

                rootSessionState_.store(RootSessionState::CREATED_SUBSEQUENT);

                TLOGI(WmsLogTag::WMS_EVENT, "rootSessionState_: %{public}d",

                    static_cast<int>(rootSessionState_.load()));

                hasDelayedTaskScheduled_.store(false);

            };

            eventHandler_->PostTask(task, "EmptyDisplayGroupDelay", DELAY_REPORT_TIME);

        } else {

            hasDelayedTaskScheduled_.store(false);

        }

    }

}



void SceneInputManager::SetRootSceneSessionCreated(bool created)

{

    if (!created) {

        TLOGE(WmsLogTag::WMS_EVENT, "false");

        return;

    }



    RootSessionState rootSessionState = RootSessionState::NOT_CREATED;

    if (rootSessionState_.compare_exchange_strong(rootSessionState,

        RootSessionState::CREATED_FIRST_TIME)) {

        TLOGI(WmsLogTag::WMS_EVENT, "Root scene session created for the first time");

    }

}

}

} // namespace OHOS::Rosen