/*
 * Copyright (c) Huawei Device Co., Ltd. 2024-2025. All rights reserved.
 * 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.
 */
import { LogDomain, LogHelper, } from '@ohos/basicutils';
import { HiSysEventUtil, SCBWindowSceneConfig, viewMgrPolicy, ViewType, } from '@ohos/frameworkwrapper';
import {
  ScbNumber,
  SCBSceneContainerSession,
  SCBSceneContainerSessionArray,
  SCBSceneInfo,
  SCBSceneMode,
  SCBSceneSession,
  SCBSceneSessionManager,
  SCBScreenProperty,
  SCBScreenSession,
  SCBScreenSessionManager,
  SCBSessionRect,
  SCBSpecificSession,
  SessionDisplayChangeReason,
  SystemBarType
} from '@ohos/windowscene';
import { LayoutViewModel, StyleConstants } from '@ohos/launchercommon';
import sceneSessionManager from '@ohos.sceneSessionManager';
import {
  SCBSpecificSceneSessionList,
  SessionChangeInfo,
  SessionRectChangeInfo,
} from '@ohos/windowscene/src/main/ets/scene/session/SCBSceneSessionManager';
import { SwipeGestureDirection } from '../constants/SwipeGestureDirection';
import { systemDateTime } from '@kit.BasicServicesKit';
import { SCBSessionListManager } from './SCBSessionListManager';
import { ViewModelConstant } from '../constants/BaseConstants';
import { DividerStyle } from '../constants/DividerStyleConstants';
import { SCBSplitLayoutHelper } from '../utils/SCBSplitLayoutHelper';
import { SCBSceneHelper } from '../utils/SCBSceneHelper';
import { SCBFreeWindowsViewModelManager } from '../SCBFreeWindowsViewModelManager';
import { LayoutPolicy } from '../layout/LayoutPolicy';
import { Pointer2D } from '../utils/SCBPcModeUtils';
import { LayoutPolicyHelper } from '../layout/LayoutPolicyHelper';
import { display } from '@kit.ArkUI';
import { SCBConstants } from '@ohos/commonconstants';
import { SCBSystemSceneUtils } from '../utils/SCBSystemSceneUtils';

const TAG = 'SCBPcScenePanelManager';
const log = LogHelper.getLogHelper(LogDomain.WINDOW, TAG);
const INVALID_SCREENID = -1;

export type SpecificSessionAddFunc = (session: SCBSpecificSession) => void;

export interface SpecificSessionController {
  addSession: SpecificSessionAddFunc;
  removeSession: (persistentId: number) => boolean;
  searchSession: (persistentId: number) => SCBSpecificSession | undefined ;
}

export class SCBPcScenePanelManager {
  private static instance: SCBPcScenePanelManager;
  // controllerMap to control SCBSpecificScenePanel --> Map<screenId, Map<zorder, SpecificSessionController>>
  private specificControllerMap: Map<number, Map<number, SpecificSessionController>> = new Map();
  // cache session if SCBScenePanel not exist --> Map<screenId, Map<zorder, SpecificSessionController>>
  private specificCacheList: Map<number, Map<number, SCBSpecificSession[]>> = new Map();
  private getIsPairSplitCallbackMap: Map<number, Function> = new Map();
  // callbackMap to add containerSessionListNotShowRecent by screenId
  private addListNotShowRecentCallbackMap: Map<number, Function> = new Map();
  // callbackMap to remove containerSessionListNotShowRecent by screenId
  private removeListNotShowRecentCallbackMap: Map<number, Function> = new Map();
  // callbackMap to sceneHiddenModeVisChange by screenId
  private sceneHiddenModeVisChangeCallbackMap: Map<number, Function> = new Map();

  private isWindowRectAutoSaveMap: Map<string, boolean> = new Map();

  private contextMap: Map<number, UIContext> = new Map()

  private isEnableSpecifiedRectAutoSaveMap: Map<string, boolean> = new Map();

  private screenDensityMap: Map<number, number> = new Map();

  private displayChangeCallbackMap: Map<number, Function> = new Map();

  private constructor() {
    SCBSceneSessionManager.getInstance().registerSessionDisplayChangeCallback(this.onSessionDisplayChange);
    SCBSceneSessionManager.getInstance().registerSetWindowRectAutoSaveCallback((sceneInfo: SCBSceneInfo,
      enable: boolean, isEnableSpecified: boolean) => {
      let abilityKey: string = sceneInfo.bundleName + sceneInfo.moduleName + sceneInfo.abilityName;
      this.setIsEnableSpecifiedRectAutoSaveMap(abilityKey, isEnableSpecified);
      let key: string = this.createIsWindowRectAutoSaveKey(sceneInfo);
      this.isWindowRectAutoSaveMap.set(key, enable);
      SCBSceneSessionManager.getInstance().setIsWindowRectAutoSave(key, enable, abilityKey, isEnableSpecified);
      log.showInfo(`isWindowRectAutoSaveMap set suceess key: ${key}, enable:${this.isWindowRectAutoSaveMap.get(key)},` +
        ` isEnableSpecified:${isEnableSpecified}`);
    });
    this.initListener();
  }

  public getIsWindowRectAutoSaveMap(key: string): boolean | undefined {
    return this.isWindowRectAutoSaveMap.get(key);
  }

  public setIsWindowRectAutoSaveMap(key: string, enabled: boolean): void {
    this.isWindowRectAutoSaveMap.set(key, enabled);
  }

  public getIsEnableSpecifiedRectAutoSaveMap(key: string): boolean | undefined {
    return this.isEnableSpecifiedRectAutoSaveMap.get(key);
  }

  public setIsEnableSpecifiedRectAutoSaveMap(key: string, enable: boolean) {
    this.isEnableSpecifiedRectAutoSaveMap.set(key, enable);
    log.showInfo(`setIsEnableSpecifiedRectAutoSaveMap ${enable}`)
  }

  public createIsWindowRectAutoSaveKey(sceneInfo: SCBSceneInfo): string {
    let key: string = sceneInfo.bundleName + sceneInfo.moduleName + sceneInfo.abilityName;
    if (this.getIsEnableSpecifiedRectAutoSaveMap(key) && sceneInfo.specifiedFlag) {
      key = key + sceneInfo.specifiedFlag;
    }
    return key;
  }

  public static getInstance(): SCBPcScenePanelManager {
    if (!SCBPcScenePanelManager.instance) {
      SCBPcScenePanelManager.instance = new SCBPcScenePanelManager();
    }
    return SCBPcScenePanelManager.instance;
  }

  /**
   * 加入主屏之前修改扩展屏的ContainerSession
   *
   * @param extendContainerSession
   * @param newScreenProperty
   */
  public updateExtendContainerSession(extendContainerSession: SCBSceneContainerSession,
    newScreenProperty: SCBScreenProperty): void {
    log.showInfo(`updateExtendContainerSession`);
    if (!extendContainerSession) {
      return;
    }
    let oldScreenProperty = new SCBScreenProperty();
    oldScreenProperty.copy(extendContainerSession.screenProperty);
    this.updateContainerSessionDisplayInfo(extendContainerSession, newScreenProperty);
    const screenId = newScreenProperty.screenId;
    if (extendContainerSession.primarySession) {
      log.showInfo(`updateExtendContainerSession primarySession ` +
        `persistentId: ${extendContainerSession.primarySession.sceneInfo.persistentId},` +
        `bundleName: ${extendContainerSession.primarySession.sceneInfo.bundleName}`);
      extendContainerSession.primarySession.updateDisplayId(screenId);
      const oldMainRect: SCBSessionRect = extendContainerSession.primarySession.currRect.copy();
      this.doUpdateFreeAndFullSession(extendContainerSession, oldScreenProperty, newScreenProperty);
      log.showInfo(`updateExtendContainerSession: sceneSession: ${extendContainerSession.primarySession.getName()}, ` +
        `rect from ${oldMainRect.printPx()} change to ${extendContainerSession.primarySession.currRect.printPx()}`);
      this.updateSubSession(extendContainerSession.primarySession, oldScreenProperty, newScreenProperty, oldMainRect,
        oldScreenProperty.screenId);
    }
    if (extendContainerSession.isSplit && extendContainerSession.secondarySession) {
      extendContainerSession.secondarySession.updateDisplayId(screenId);
      const oldMainRect: SCBSessionRect = extendContainerSession.secondarySession.currRect.copy();
      this.updateExtendSplitSession(extendContainerSession, oldScreenProperty, newScreenProperty);
      if (extendContainerSession.primarySession) {
        LayoutPolicyHelper.getInstance().handleFreeWindowRect(extendContainerSession.primarySession.lastRect,
          oldScreenProperty, newScreenProperty, extendContainerSession.primarySession);
      }
      LayoutPolicyHelper.getInstance().handleFreeWindowRect(extendContainerSession.secondarySession.lastRect,
        oldScreenProperty, newScreenProperty, extendContainerSession.secondarySession);
      this.updateSubSession(extendContainerSession.secondarySession, oldScreenProperty, newScreenProperty, oldMainRect,
        oldScreenProperty.screenId);
    }
  }

  /**
   * 合并拖拽到扩展屏上的子窗到主屏
   *
   * @param containerSession
   * @param newScreenProperty
   * @param extendScreenId
   */
  public updateExtendSubSession(containerSession: SCBSceneContainerSession,
    newScreenProperty: SCBScreenProperty, extendScreenId: number): void {
    log.showInfo(`updateExtendSubSession`);
    let oldScreenProperty = new SCBScreenProperty();
    let screenSession = SCBScreenSessionManager.getInstance().getScreenSession(extendScreenId);
    if (!screenSession) {
      log.showError(`updateExtendSubSession get screenSession is null`);
      return;
    }
    oldScreenProperty.copy(screenSession.scbScreenProperty);
    if (containerSession.primarySession) {
      const oldMainRect: SCBSessionRect = containerSession.primarySession.currRect.copy();
      this.updateSubSession(containerSession.primarySession, oldScreenProperty, newScreenProperty, oldMainRect,
        containerSession.screenProperty.screenId);
    }
    if (containerSession.secondarySession) {
      const oldMainRect: SCBSessionRect = containerSession.secondarySession.currRect.copy();
      this.updateSubSession(containerSession.secondarySession, oldScreenProperty, newScreenProperty, oldMainRect,
        containerSession.screenProperty.screenId);
    }
    let screenProperty = new SCBScreenProperty();
    screenProperty.screenId = extendScreenId;
    let extendContainerSessionList = SCBSessionListManager.getNormalContainerArray(screenProperty);
    let index = extendContainerSessionList.indexOf(containerSession);
    if (index !== -1) {
      extendContainerSessionList.splice(index, 1);
    }
  }

  /**
   * update subsession when change mode or screen disconnect recursively
   *
   * @param sceneSession main session or subsession
   * @param oldScreenProperty old screen's screen property
   * @param newScreenProperty new screen's screen property
   * @param oldMainRect main session or subsession rect before change
   * @param containerSessionScreenId main session or subsession screen id
   */
  public updateSubSession(sceneSession: SCBSceneSession | SCBSpecificSession, oldScreenProperty: SCBScreenProperty,
    newScreenProperty: SCBScreenProperty, oldMainRect: SCBSessionRect, containerSessionScreenId: number,
    targetDensity?: number): void {
    if (sceneSession.subSessionList) {
      const logName: string = `updateSubSession bundleName: ${sceneSession.getName()}, ` +
        (sceneSession instanceof SCBSceneSession ? `persistentId: ${sceneSession.sceneInfo.persistentId}, ` : ``) +
        `from screenId: ${oldScreenProperty.screenId}, to screenId: ${newScreenProperty.screenId}`;
      log.showInfo(logName);
      let subList: SCBSpecificSceneSessionList = new SCBSpecificSceneSessionList();
      SCBSceneHelper.traverseSubList(sceneSession.subSessionList, subList);
      for (let subSession of subList) {
        if (subSession.screenId !== oldScreenProperty.screenId) {
          continue;
        }
        if (oldScreenProperty.screenId !== newScreenProperty.screenId) {
          subSession.updateDisplayId(newScreenProperty.screenId);
        }
        const oldSubMainRect: SCBSessionRect = subSession.currRect.copy();
        LayoutPolicyHelper.getInstance().updateSingleSubSessionRect(oldMainRect, sceneSession.currRect.copy(),
          subSession, oldScreenProperty, newScreenProperty, containerSessionScreenId === oldScreenProperty.screenId,
          targetDensity);
        log.showInfo(`updateSubSession: subSession: ${subSession.getName()}, ` +
          `rect from ${oldSubMainRect.printPx()} change to ${subSession.currRect.printPx()}`);
      }
    }
  }

  private springBackIfNeeded(sceneSession: SCBSceneSession, newScreenProperty: SCBScreenProperty): void {
    // top area
    let topThreshold = SCBSystemSceneUtils.getInstance().getStatusBarHeightPx(newScreenProperty.screenId);
    if (sceneSession.currRect.top.getPx() < topThreshold) {
      sceneSession.currRect.setPosNum(sceneSession.currRect.left.getPx(), topThreshold);
    }

    let titleHeight = StyleConstants.DEFAULT_CONTAINER_TITLE_HEIGHT;
    try {
      const customDecorHeight: ESObject = sceneSessionManager.getCustomDecorHeight(sceneSession.session.persistentId);
      if (customDecorHeight !== 0 && customDecorHeight !== titleHeight) {
        titleHeight = customDecorHeight;
      }
    } catch (error) {
      log.showError(`get customDecorHeight failed, error: ${error}`);
    }
    // bottom area
    let titleHeightPx = 0;
    let dockHeight = 0;
    let density: number | undefined = this.screenDensityMap.get(newScreenProperty.screenId);
    if (density !== undefined) {
      titleHeightPx = density / ViewModelConstant.BASE_DENSITY * titleHeight;
    } else {
      let uiContext: UIContext | undefined = this.contextMap.get(newScreenProperty.screenId);
      titleHeightPx = SCBSystemSceneUtils.getInstance().vp2px(uiContext, titleHeight);
    }
    let dock = SCBSceneSessionManager.getInstance()
      .getSystemSceneSessionWithSystemBarType(SystemBarType.SMART_DOCK, newScreenProperty.screenId);
    if (dock) {
      dockHeight = dock.currRect.height.getPx();
    }
    let bottomThreshold = newScreenProperty.height - titleHeightPx - dockHeight;
    if (sceneSession.currRect.top.getPx() > bottomThreshold) {
      sceneSession.currRect.setPosNum(sceneSession.currRect.left.getPx(), bottomThreshold);
    }
  }

  private updateExtendSplitSession(extendContainerSession: SCBSceneContainerSession,
    oldScreenProperty: SCBScreenProperty, newScreenProperty: SCBScreenProperty): void {
    if (!extendContainerSession.isActive && extendContainerSession.primarySession) {
      extendContainerSession.dividerParam.positionX =
        `${extendContainerSession.isSplitPrimaryMode() ? extendContainerSession.primarySession.currRect.width.getVp() :
        extendContainerSession.secondarySession?.currRect.width.getVp()}`;
      extendContainerSession.dividerParam.positionY = `${extendContainerSession.primarySession.currRect.top.getVp()}`;
      extendContainerSession.dividerParam.dividerWidth = DividerStyle.DIVIDER_WIDTH;
      extendContainerSession.dividerParam.dividerHeight = extendContainerSession.primarySession.currRect.height.getVp();
      return;
    }
    this.doUpdateSplitSession(extendContainerSession, newScreenProperty);
  }

  private getTopThreshold(screenId: number): number {
    let topThreshold = 0;
    if (!AppStorage.get<boolean>('isDockAutoHide')) {
      topThreshold = SCBSystemSceneUtils.getInstance().getStatusBarHeightPx(screenId);
    }
    return topThreshold;
  }

  private getBottomThreshold(newScreenProperty: SCBScreenProperty): number {
    let bottomThreshold = newScreenProperty.height;
    if (!AppStorage.get<boolean>('isDockAutoHide')) {
      bottomThreshold = bottomThreshold - SCBSystemSceneUtils.getInstance().getDockHeightPx(newScreenProperty.screenId);
    }
    return bottomThreshold;
  }

  private onSessionDisplayChange = (reason: SessionDisplayChangeReason, sessionChangeInfo: SessionChangeInfo): void => {
    if (!sessionChangeInfo) {
      log.showWarn(`onSessionDisplayChange sessionChangeInfo is null. reason:${reason}`);
      return;
    }
    switch (reason) {
      case SessionDisplayChangeReason.MOVE_TO_TARGET_DISPLAY:
        this.moveMainSessionToTargetScreen(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.MOVE_SUB_WINDOW_TO_TARGET_DISPLAY:
        this.moveSubSessionToTargetScreen(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.MOVE_SPECIFIC_WINDOW_TO_TARGET_DISPLAY:
        this.moveSpecificSessionToTargetScreen(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.START_MOVE:
        this.startMoveSession(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.END_MOVE:
        this.endMoveSession(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.DRAG_START:
        this.startMoveSession(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.REFRESH:
        this.refreshContainerSessionList(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.CHANGE_PARENT_SESSION:
        this.subSessionChangeParentSession(sessionChangeInfo);
        break;
      case SessionDisplayChangeReason.MOVE_TO_TARGET_DISPLAY_NOT_SHOW:
        this.moveNotShowRecentToTarget(sessionChangeInfo);
        break;
      default:
        break;
    }
    return;
  }

  private moveMainSessionToTargetScreen(sessionChangeInfo: SessionChangeInfo): void {
    log.showInfo(`moveMainSessionToTargetScreen targetScreenId: ${sessionChangeInfo.targetScreenId}` +
      `, curScreenId: ${sessionChangeInfo.curScreenId}`);
    this.moveSessionToTargetScreen(sessionChangeInfo, true);
  }

  /**
   * when container change to another screen, update screenProperty
   * @param containerSession
   * @param targetScreenProperty
   */
  public updateContainerSessionDisplayInfo(containerSession: SCBSceneContainerSession,
    targetScreenProperty: SCBScreenProperty) {
    if (!containerSession || !targetScreenProperty) {
      log.showWarn(`updateContainerSession failed.`);
      return;
    }
    containerSession.updateScreenProperty(targetScreenProperty);
  }

  private moveSubSessionToTargetScreen(sessionChangeInfo: SessionChangeInfo): void {
    log.showInfo(`moveSubSessionToTargetScreen targetScreenId: ${sessionChangeInfo.targetScreenId}` +
      `, curScreenId: ${sessionChangeInfo.curScreenId}`);
    this.moveSessionToTargetScreen(sessionChangeInfo, false);
  }

  private subSessionChangeParentSession(sessionChangeInfo: SessionChangeInfo): void {
    log.showInfo(`subSessionChangeParentSession targetScreenId: ${sessionChangeInfo.targetScreenId}`);
    this.moveSessionToTargetScreen(sessionChangeInfo, false);
  }

  private moveNotShowRecentToTarget(sessionChangeInfo: SessionChangeInfo): void {
    log.showInfo(`moveNotShowRecentToTarget currentScreenId: ${sessionChangeInfo.curScreenId},
      targetScreenId: ${sessionChangeInfo.targetScreenId}`);
    if (sessionChangeInfo.curScreenId === sessionChangeInfo.targetScreenId) {
      return;
    }
    // get containerSession in not show list
    for (let persistentId of sessionChangeInfo.persistentIds) {
      let containerSession = SCBSceneSessionManager.getInstance().
        getContainerSessionListNotShowRecent(sessionChangeInfo.curScreenId).findByPersistentId(persistentId);
      if (!containerSession) {
        log.showWarn(`persistentId: ${persistentId} moveNotShowRecentToTarget failed.`);
        continue;
      }
      let targetScreenProperty = this.getTargetScreenProperty(sessionChangeInfo.targetScreenId);
      if (!targetScreenProperty) {
        log.showError(`moveNotShowRecentToTarget get targetScreenProperty failed!`);
        return;
      }
      this.updateContainerSessionDisplayInfo(containerSession, targetScreenProperty);
      containerSession.primarySession?.updateDisplayId(sessionChangeInfo.targetScreenId);
      this.addListNotShowRecent(sessionChangeInfo.targetScreenId, containerSession);
      this.removeListNotShowRecent(sessionChangeInfo.curScreenId, containerSession);
    }
  }

  private moveSessionToTargetScreen(sessionChangeInfo: SessionChangeInfo, isMainSession: boolean): void {
    let curContainerSessionList: SCBSceneContainerSessionArray =
      SCBSceneSessionManager.getInstance().getContainerSessionList(sessionChangeInfo.curScreenId);

    let tmpContainerMap: Map<number, SCBSceneContainerSession> = new Map();

    // get containerSession in current screen
    for (let id of sessionChangeInfo.persistentIds) {
      let containerSession = curContainerSessionList.findByPersistentId(id, true);
      if (!containerSession) {
        log.showWarn(`moveSessionToTargetScreen not find containerSession. persistentId:${id}`);
        continue;
      }
      if (containerSession.primarySession?.isSessionResizeWhenDragEnd) {
        log.showWarn(`moveSessionToTargetScreen not to move for isSessionResizeWhenDragEnd is true. persistentId:${id}`);
        continue;
      }
      tmpContainerMap.set(id, containerSession);
    }

    let targetContainerSessionList: SCBSceneContainerSessionArray =
      SCBSceneSessionManager.getInstance().getContainerSessionList(sessionChangeInfo.targetScreenId);
    for (let iter of tmpContainerMap.entries()) {
      let persistentId = iter[0];
      let containerSession = iter[1];

      // add to target screen
      if (!targetContainerSessionList.findByContainerId(containerSession.containerId)) {
        log.showDebug(`moveSessionToTargetScreen add:${containerSession.primarySession?.sceneInfo.persistentId}` +
          `, screenId:${sessionChangeInfo.targetScreenId}, target len:${targetContainerSessionList.length}`);
        targetContainerSessionList.push(containerSession);
      }

      targetContainerSessionList.sortByTopmost();
      let sessionRectChangeInfo = sessionChangeInfo.sessionRectChangeMap.get(persistentId);
      if (sessionRectChangeInfo !== undefined && sessionRectChangeInfo !== null) {
        this.onSessionRectChange(sessionRectChangeInfo, sessionChangeInfo,
          containerSession, persistentId, curContainerSessionList, isMainSession);
      }

      // remove from current screen
      if (!curContainerSessionList.isContainerInScreenHasSession(
        containerSession.containerId, sessionChangeInfo.curScreenId)) {
        let index = curContainerSessionList.indexOf(containerSession);
        if (index === -1) {
          log.showWarn(`moveSessionToTargetScreen find container failed. containerId:${containerSession.containerId}` +
            `, screenId:${sessionChangeInfo.curScreenId}`);
          continue;
        }
        log.showDebug(`moveSessionToTargetScreen remove:${containerSession.primarySession?.sceneInfo.persistentId}` +
          `, screenId:${sessionChangeInfo.curScreenId}`);
        curContainerSessionList.splice(index, 1);
      }
      log.showInfo(`moveSessionToTargetScreen after remove. curScreen len:${curContainerSessionList.length}` +
        `, targetScreen len:${targetContainerSessionList.length}`);
    }
    return;
  }

  private getTargetScreenProperty(screenId: number): SCBScreenProperty | null {
    for (let screenSession of SCBScreenSessionManager.getInstance().getScreenSessionList()) {
      if (screenSession.scbScreenProperty.screenId === screenId) {
        return screenSession.scbScreenProperty;
      }
    }
    return null;
  }

  private updateSessionScreenId(persistentId: number, containerSession: SCBSceneContainerSession,
    containerSessionList: SCBSceneContainerSessionArray, sessionChangeInfo: SessionChangeInfo,
    isMainSession: boolean) {
    let screenId: number = sessionChangeInfo.targetScreenId;
    if (isMainSession) {
      let targetScreenProperty = this.getTargetScreenProperty(screenId);
      if (!targetScreenProperty) {
        log.showError(`updateSessionScreenId get targetScreenProperty failed!`);
        return;
      }
      this.updateContainerSessionDisplayInfo(containerSession, targetScreenProperty);
      if (containerSession.primarySession) {
        containerSession.primarySession.isCrossingScreen = true;
        containerSession.primarySession.updateDisplayId(screenId);
        // moveTo场景,新屏幕上得SCBScene还未注册callback,需要在这里做可用区域避让
        this.springBackIfNeeded(containerSession.primarySession, targetScreenProperty);
      }
    } else {
      let subSession = containerSessionList.getSessionByPersistentId(persistentId) as SCBSpecificSession;
      if (subSession) {
        subSession.isCrossingScreen = true;
        subSession.updateDisplayId(screenId);
      } else {
        log.showWarn(`updateSessionScreenId find subSession failed. persistentId:${persistentId}`);
      }
    }
  }

  private startMoveSession(sessionChangeInfo: SessionChangeInfo): void {
    log.showInfo(`startMoveSession curScreenId: ${sessionChangeInfo.curScreenId}`);
    let curContainerSessionList: SCBSceneContainerSessionArray =
      SCBSceneSessionManager.getInstance().getContainerSessionList(sessionChangeInfo.curScreenId);

    let tmpContainerArray: SCBSceneContainerSessionArray = new SCBSceneContainerSessionArray();

    // get containerSession of subwindow in current screen
    for (let id of sessionChangeInfo.persistentIds) {
      let containerSession = curContainerSessionList.findByPersistentId(id, true);
      if (!containerSession) {
        log.showWarn(`moveSubSessionToTargetScreen not find containerSession. persistentId:${id}`);
        continue;
      }
      tmpContainerArray.push(containerSession);
    }

    if (tmpContainerArray.length === 0) {
      return;
    }

    // add temp container session to other screen for window hot area animation.
    for (let screenSession of SCBScreenSessionManager.getInstance().getScreenSessionList()) {
      let screenId: ESObject = screenSession.session.screenId;
      // add container to target screen
      let targetContainerSessionList: SCBSceneContainerSessionArray =
        SCBSceneSessionManager.getInstance().getContainerSessionList(screenId);

      for (let containerSession of tmpContainerArray) {
        // if target screen has container session, no need to add
        if (targetContainerSessionList.findByContainerId(containerSession.containerId)) {
          continue;
        }
        targetContainerSessionList.push(containerSession);
        log.showDebug(`startMoveSession push persistentId${containerSession.primarySession?.sceneInfo.persistentId}, ` +
          `to screen:${screenId}`);
      }
      log.showInfo(`startMoveSession screenId:${screenId}, containerSessionList len:${targetContainerSessionList.length}`);
    }
  }

  private endMoveSession(sessionChangeInfo: SessionChangeInfo): void {
    log.showInfo(`endMoveSession curScreenId: ${sessionChangeInfo.curScreenId}`);
    let curContainerSessionList: SCBSceneContainerSessionArray =
      SCBSceneSessionManager.getInstance().getContainerSessionList(sessionChangeInfo.curScreenId);

    let tmpContainerArray: SCBSceneContainerSessionArray = new SCBSceneContainerSessionArray();

    // get containerSession of subwindow in current screen
    for (let id of sessionChangeInfo.persistentIds) {
      let containerSession = curContainerSessionList.findByPersistentId(id, true);
      if (!containerSession) {
        log.showWarn(`moveSubSessionToTargetScreen not find containerSession. persistentId:${id}`);
        continue;
      }
      tmpContainerArray.push(containerSession);
    }

    // add temp container session to other screen for window hot area animation.
    for (let screenSession of SCBScreenSessionManager.getInstance().getScreenSessionList()) {
      let screenId: ESObject = screenSession.session.screenId;
      // add container to target screen
      let targetContainerSessionList: SCBSceneContainerSessionArray =
        SCBSceneSessionManager.getInstance().getContainerSessionList(screenId);

      for (let containerSession of tmpContainerArray) {
        // if target screen has container session, no need to remove
        if (targetContainerSessionList.isContainerInScreenHasSession(containerSession.containerId, screenId)) {
          continue;
        }
        let index = targetContainerSessionList.indexOf(containerSession);
        if (index === -1) {
          log.showWarn(`endMoveSession find container failed. containerId:${containerSession.containerId}` +
            `, screenId:${sessionChangeInfo.curScreenId}`);
          continue;
        }
        log.showDebug(`endMoveSession remove:${containerSession.primarySession?.sceneInfo.persistentId}, screenId:${screenId}`);
        targetContainerSessionList.splice(index, 1);
      }
      log.showInfo(`endMoveSession screenId:${screenId}, containerSessionList len:${targetContainerSessionList.length}`);
    }
  }

  private refreshContainerSessionList(sessionChangeInfo: SessionChangeInfo): void {
    log.showInfo(`refreshContainerSessionList`);
    let curContainerSessionList: SCBSceneContainerSessionArray =
      SCBSceneSessionManager.getInstance().getContainerSessionList(sessionChangeInfo.curScreenId);

    let tmpContainerArray: SCBSceneContainerSessionArray = new SCBSceneContainerSessionArray();

    // get containerSession of subwindow in current screen
    for (let id of sessionChangeInfo.persistentIds) {
      let containerSession = curContainerSessionList.findByPersistentId(id, true);
      if (!containerSession) {
        log.showWarn(`refreshContainerSessionList not find containerSession. persistentId:${id}`);
        continue;
      }
      tmpContainerArray.push(containerSession);
    }
    log.showInfo(`refreshContainerSessionList tmpContainerArray len:${tmpContainerArray.length}`);

    // if containerSession no session in current screen, remove duplicate containerSession
    for (let screen of SCBScreenSessionManager.getInstance().getScreenSessionList()) {
      let screenId: ESObject = screen.session.screenId;
      let targetContainerSessionList: SCBSceneContainerSessionArray =
        SCBSceneSessionManager.getInstance().getContainerSessionList(screenId);
      for (let containerSession of tmpContainerArray) {
        // if target screen has container session, no need to remove
        if (targetContainerSessionList.isContainerInScreenHasSession(containerSession.containerId, screenId)) {
          log.showInfo(`refreshContainerSessionList isContainerInScreenHasSession`);
          continue;
        }
        let index = targetContainerSessionList.indexOf(containerSession);
        if (index === -1) {
          log.showWarn(`refreshContainerSessionList find container failed.` +
            `, screenId:${sessionChangeInfo.curScreenId}`);
          continue;
        }
        targetContainerSessionList.splice(index, 1);
        log.showInfo(`refreshContainerSessionList remove:${containerSession?.primarySession?.sceneInfo.persistentId},` +
          `screenId:${screenId}, targetContaienrSessionList len:${targetContainerSessionList.length}`);
      }
    }
  }

  /**
   * main session closed
   * @param containerId main session containerId
   */
  public onMainSessionClosed(containerId: number) {
    // remove containerSession in all screen by containerId
    for (let screen of SCBScreenSessionManager.getInstance().getScreenSessionList()) {
      let screenId: ESObject = screen.session.screenId;
      let targetContainerSessionList: SCBSceneContainerSessionArray =
        SCBSceneSessionManager.getInstance().getContainerSessionList(screenId);
      let index = targetContainerSessionList.findIndexByContainerId(containerId);
      if (index === -1) {
        continue;
      }
      targetContainerSessionList.splice(index, 1);
      log.showInfo(`onMainSessionClosed screenId:${screenId},` +
        `targetContaienrSessionList len:${targetContainerSessionList.length}`);
    }
  }


  /**
   * register controller for SCBSpecificScenePanel
   */
  public registerSpecificSessionController(screenId: number, zorder: number,
    controller: SpecificSessionController) : void {
    log.showInfo(`[SCBSpecific] register, screenId ${screenId}, zorder ${zorder}`);
    if (!this.specificControllerMap.has(screenId)) {
      this.specificControllerMap.set(screenId, new Map());
    }
    this.specificControllerMap.get(screenId)?.set(zorder, controller);
    let sessions = this.specificCacheList.get(screenId)?.get(zorder);
    if (sessions) {
      sessions.forEach((session) => {
        log.showInfo(`[SCBSpecific] recover, screenId ${screenId}, zorder ${zorder}`);
        controller.addSession(session);
      })
      sessions.length = 0;
    }
  }

  /**
   * unregister controller for SCBSpecificScenePanel
   */
  public unRegisterSpecificSessionController(screenId: number, zorder: number) : void {
    if (this.specificControllerMap.get(screenId)?.get(zorder)) {
      this.specificControllerMap.get(screenId)?.delete(zorder);
    }
  }

  //SpecificSessionMap:
  private specificSessionMap: Map<number, SCBSpecificSceneSessionList> = new Map();

  public getSpecificSessionList(zorder: number): SCBSpecificSceneSessionList {
    if (!this.specificSessionMap.has(zorder)) {
      this.specificSessionMap.set(zorder, new SCBSpecificSceneSessionList());
    }
    return this.specificSessionMap.get(zorder)!;
  }

  private moveSpecificSessionToTargetScreen(sessionChangeInfo: SessionChangeInfo): void {
    if (sessionChangeInfo.persistentIds.length !== 1) {
      log.showError('[SCBSpecific] do not move specificSessions in one callback');
      return;
    }
    if (!sessionChangeInfo.sessionType) {
      log.showError('[SCBSpecific] sessionType do not exist, return');
      return;
    }
    let zorder = SCBSceneSessionManager.getInstance().getPanelZorderByType(sessionChangeInfo.sessionType);
    if (zorder === undefined) {
      log.showError('[SCBSpecific] zorder do not exist, return');
      return;
    }
    let targetSession : SCBSpecificSession | undefined = this.getSpecificSessionList(zorder).find((scbSession) => {
      return scbSession?.session.persistentId === sessionChangeInfo.persistentIds[0];
    });
    if (targetSession == null) {
      log.showError('[SCBSpecific] can not find session in current specificScenePanel');
      return;
    }
    let curRect: ESObject = sessionChangeInfo.sessionRectChangeMap.get(targetSession.persistentId)?.newRect;
    if (curRect) {
      targetSession.currRect.setRectNum(curRect.posX_, curRect.posY_, curRect.width_, curRect.height_);
    }
    targetSession.updateDisplayId(sessionChangeInfo.targetScreenId);
  }

  public mergeSpecificSessionList(screenId: number): void {
    let mainScreenId = SCBSceneSessionManager.getInstance().mainScreenId;
    const extendScreenSession: SCBScreenSession | null =
      SCBScreenSessionManager.getInstance().getScreenSession(screenId);
    const mainScreenSession: SCBScreenSession | null = SCBScreenSessionManager.getInstance().getMainScreenSession();
    this.specificSessionMap.forEach((list: SCBSpecificSceneSessionList) => {
      list?.forEach((session: SCBSpecificSession) => {
        this.updateSystemSceneFollowScreen(session, extendScreenSession?.scbScreenProperty,
          mainScreenSession?.scbScreenProperty);
        session?.updateDisplayIdRecursively(mainScreenId, screenId);
      });
    });
    let appSubSessionList = SCBSceneSessionManager.getInstance().getFullScreenSubSessionList();
    appSubSessionList?.forEach((item)=> {
      if (item.screenId === screenId) {
        item.updateDisplayId(mainScreenId);
      }
      item.onUpdateMaximizeRect();
    });
    if (appSubSessionList?.length > 0) {
      SCBSceneSessionManager.getInstance().updateSystemBarProperty();
    }
  }

  private updateSystemSceneFollowScreen(specificSession: SCBSpecificSession,
    oldScreenProperty: SCBScreenProperty | null | undefined,
    newScreenProperty: SCBScreenProperty | null | undefined): void {
    if (!specificSession || !specificSession.isActive || !oldScreenProperty || !newScreenProperty) {
      log.showInfo(`${specificSession.getName()} property not satisfy to process`);
      return;
    }
    const oldSessionRect = specificSession.currRect.copy();
    if (specificSession.screenId === oldScreenProperty.screenId) {
      if (this.isFullScreenSessionNeedFollowScreen(specificSession, oldScreenProperty)) {
        specificSession.updateSizeChangeReason(sceneSessionManager.SessionSizeChangeReason.UNDEFINED);
        specificSession.currRect.setRectNum(newScreenProperty.left, newScreenProperty.top, newScreenProperty.width,
          newScreenProperty.height);
      } else if (this.isNonFullScreenFloatSession(specificSession, oldScreenProperty)) {
        LayoutPolicyHelper.getInstance().handleFreeWindowRect(specificSession.currRect, oldScreenProperty,
          newScreenProperty);
      }
    }
    this.updateSubSession(specificSession, oldScreenProperty, newScreenProperty, oldSessionRect,
      oldScreenProperty.screenId);
    log.showInfo(`merge specific session ${specificSession.getName()}, rect is ` +
      `${specificSession.currRect.printPx()}, isFollowScreenChange ${specificSession.isFollowScreenChange}`);
  }

  private initArea(reason: sceneSessionManager.SessionSizeChangeReason,
    rect: sceneSessionManager.SessionRect, sceneSession: SCBSceneSession | SCBSpecificSession): SCBSessionRect {
    let area: SCBSessionRect = sceneSession.currRect.copy();

    log.showInfo(`initAvoidanceArea start, id: ${sceneSession.session.persistentId}, reason: ${reason},` +
      `newRect: [${rect.posX_}, ${rect.posY_}, ${rect.width_}, ${rect.height_}]` +
      `currRect: [${sceneSession.currRect.left.getPx()}, ${sceneSession.currRect.top.getPx()},` +
      `${sceneSession.currRect.width.getPx()}, ${sceneSession.currRect.height.getPx()}]`);
    if (reason === sceneSessionManager.SessionSizeChangeReason.MOVE) {
      area.left.setNumber(rect.posX_);
      area.top.setNumber(rect.posY_);
    } else if (reason === sceneSessionManager.SessionSizeChangeReason.RECOVER ||
      reason === sceneSessionManager.SessionSizeChangeReason.DRAG_END) {
      area.left.setNumber(rect.posX_);
      area.top.setNumber(rect.posY_);
      area.width.setNumber(rect.width_);
      area.height.setNumber(rect.height_);
    }

    return area;
  }

  private onSessionRectChange(sessionRectChangeInfo: SessionRectChangeInfo, sessionChangeInfo: SessionChangeInfo,
    containerSession: SCBSceneContainerSession, persistentId: number,
    containerSessionList: SCBSceneContainerSessionArray, isMainSession: boolean): void {
    let displayId: number = sessionChangeInfo.targetScreenId;
    if (!containerSession) {
      log.showWarn('onSessionRectChange containerSession is null');
      return;
    }

    let session: SCBSceneSession | SCBSpecificSession | null =
      containerSessionList.getSessionByPersistentId(persistentId);
    if (session === null) {
      log.showWarn('onSessionRectChange session is null');
      return;
    }

    let rect = sessionRectChangeInfo.newRect;
    let reason = sessionRectChangeInfo.reason;

    if (!rect) {
      log.showWarn('onSessionRectChange rect is null');
      return;
    }

    let area: SCBSessionRect = this.getTargetSessionLayout(rect, sessionChangeInfo, session, reason);

    if (session instanceof SCBSceneSession) {
      if (session.sceneInfo.windowMode === SCBSceneMode.FULLSCREEN &&
        reason === sceneSessionManager.SessionSizeChangeReason.MOVE && session.isRecoveredFullScreen) {
        log.showInfo('recovered fullscreen sceneSession cannot move');
        return;
      }
    }

    if (session instanceof SCBSpecificSession && (session as SCBSpecificSession).subWindowAnchorInfo.isFollowParent) {
      this.updateSessionScreenId(persistentId, containerSession, containerSessionList,
        sessionChangeInfo, isMainSession);
      return;
    }

    let context: UIContext | undefined = this.contextMap.get(displayId);
    if (!session.sessionData.isDragging && context) {
      context.animateTo({
        duration: 0,
      }, () => {
        this.updateCurrentRect(area, session);
        this.updateSessionScreenId(persistentId, containerSession, containerSessionList,
          sessionChangeInfo, isMainSession);
      })
    } else {
      this.updateCurrentRect(area, session);
      this.updateSessionScreenId(persistentId, containerSession, containerSessionList,
        sessionChangeInfo, isMainSession);
    }
    log.showInfo(`onSessionRectChange end, id: ${session.session.persistentId}, reason: ${reason}` +
      `rect: [${session.currRect.left.getPxStr()}, ${session.currRect.top.getPxStr()}, ` +
      `${session.currRect.width.getPxStr()}, ${session.currRect.height.getPxStr()}]`);
  }

  private getTargetSessionLayout(targetRect: sceneSessionManager.SessionRect, sessionChangeInfo: SessionChangeInfo,
    session: SCBSceneSession | SCBSpecificSession,
    reason: sceneSessionManager.SessionSizeChangeReason): SCBSessionRect {
    let area: SCBSessionRect = this.initArea(reason, targetRect, session);
    if (!this.isNeedUpdateSessionRect(session, sessionChangeInfo.targetScreenId)) {
      return area;
    }

    let anchor: Pointer2D = this.getAnchor(session, area, reason);
    this.updateRectWithDpiChange(area, sessionChangeInfo.curScreenId, sessionChangeInfo.targetScreenId,
      undefined, anchor);

    log.showInfo(`getTargetSessionLayout end. ` +
      `area:[${area.left.getPxStr()}, ${area.top.getPxStr()}, ${area.width.getPxStr()}, ${area.height.getPxStr()}]`);

    return area;
  }

  private getAnchor(session: SCBSceneSession | SCBSpecificSession, targetArea: SCBSessionRect,
    reason: sceneSessionManager.SessionSizeChangeReason): Pointer2D {
    // 拖拽场景以鼠标松手位置为缩放锚点
    if (reason === sceneSessionManager.SessionSizeChangeReason.DRAG_END) {
      return new Pointer2D(session.currMovePointerPos.pointerPosX, targetArea.top.getPx());
    } else if (reason === sceneSessionManager.SessionSizeChangeReason.MOVE) {
      // moveTo场景以窗口左上角进行缩放
      return new Pointer2D(targetArea.left.getPx(), targetArea.top.getPx());
    } else {
      // 默认以窗口左上角为锚点进行缩放
      return new Pointer2D(targetArea.left.getPx(), targetArea.top.getPx());
    }
  }

  public updateRectWithDpiChange(rect: SCBSessionRect, srcScreenId: number, targetScreenId: number,
    tarDensity?: number, anchor?: Pointer2D): void {
    log.showDebug(`updateRectWithDpiChange start update rect:[${rect.left.getPxStr()}, ${rect.top.getPxStr()}, ` +
      `${rect.width.getPxStr()}, ${rect.height.getPxStr()}], ` +
      `srcScreenId:${srcScreenId}, targetScreenId:${targetScreenId}, tarDensity:${tarDensity}`);

    let srcDensity: number | undefined = this.screenDensityMap.get(srcScreenId);
    let targetDensity: number | undefined = tarDensity !== undefined ? tarDensity :
      this.screenDensityMap.get(targetScreenId);
    if (!srcDensity || !targetDensity) {
      log.showWarn(`updateRectWithDpiChange failed. srcDensity:${srcDensity}, targetDensity:${targetDensity}`);
      return;
    }

    if (srcDensity === targetDensity) {
      log.showInfo(`updateRectWithDpiChange density not change`);
      return;
    }

    // 以鼠标所在x坐标为中心做横向缩放
    if (srcDensity === 0) {
      log.showError(`updateRectWithDpiChange srcDensity is 0. Error!`);
      return;
    }
    let scaleRatio: number = targetDensity / srcDensity;
    LayoutPolicyHelper.getInstance().updateRectWithDensityChange(rect, scaleRatio, anchor);
  }

  private isNeedUpdateSessionRect(session: SCBSceneSession | SCBSpecificSession, targetScreenId: number): boolean {
    if (session instanceof SCBSceneSession) {
      if (session.defaultDensityEnabled) {
        log.showInfo(`use default density. ` +
          `session id:${session.sceneInfo.persistentId}, screenId:${session.sceneInfo.screenId}`)
        return false;
      }

      // 跨屏时触发最大化或分屏流程时,窗口大小不需要跟随DPI变化
      return !session.isNeedHandleWindowDragHotArea(targetScreenId);
    }
    return true;
  }

  private updateCurrentRect(area: SCBSessionRect, session: SCBSceneSession | SCBSpecificSession | null): void {
    if (!session) {
      return;
    }
    if ((area.left.getPx() !== session.currRect.left.getPx()) ||
      (area.top.getPx() !== session.currRect.top.getPx())) {
      session.currRect.setPos(area.left, area.top);
    }
    if ((area.width.getPx() !== session.currRect.width.getPx()) ||
      (area.height.getPx() !== session.currRect.height.getPx())) {
      session.currRect.setSize(area.width, area.height);
    }
  }

  /**
   * set scene borderRadius when clear scene
   * @param scene responds to clear scene
   * @param scale control borderRadius scale
   * @param currentPercent current scene's clear progress
   */
  public setSceneBorderRadiusInClearScene(scene: SCBSceneContainerSession | SCBSpecificSession,
    scale: number, currentPercent: number): void {
    if (!scene || scene instanceof SCBSpecificSession) {
      return;
    }
    if (scene instanceof SCBSceneContainerSession) {
      const sceneBorderRadius = SCBWindowSceneConfig.getInstance().windowSceneConfig.floatCornerRadius * scale;
      if (scene.primarySession !== null && scene.primarySession.sceneInfo.windowMode !== SCBSceneMode.FLOATING) {
        scene.primarySession.floatBorderRadius = sceneBorderRadius;
        if (scale === 0 || (scale === 1 && currentPercent === 1)) {
          log.showInfo(`${scene.primarySession.getName()} is set boardRadius in clear scene is: ${sceneBorderRadius}`);
        }
      }
      if (scene.secondarySession !== null && scene.secondarySession.sceneInfo.windowMode !== SCBSceneMode.FLOATING) {
        scene.secondarySession.floatBorderRadius = sceneBorderRadius;
      }
    }
  }

  /**
   * reSet scene borderRadius while into recent
   * @param scenes function queryFrontForWIND result
   */
  public resetSceneBorderRadiusIntoRecent(scenes: Array<SCBSceneContainerSession | SCBSpecificSession> = []): void {
    if (scenes.length <= 0) {
      return;
    }
    for (const scene of scenes) {
      this.setSceneBorderRadiusInClearScene(scene, 0, 1);
    }
  }

  /**
   * recover visibility in clearScene while into recent
   * @param frontScenes scenes in clearScene Operation
   */
  public recoverVisibilityInClearScene(frontScenes: Array<SCBSceneContainerSession | SCBSpecificSession>) {
    if (frontScenes.length <= 0) {
      return;
    }
    for (const scene of frontScenes) {
      this.setSceneBorderRadiusInClearScene(scene, 0, 1);
      if (scene instanceof SCBSceneContainerSession && scene.isActive) {
        scene.needRenderVisibility.setVisibilityWithDfx(true, TAG, 'recoverVisibilityIntoRecent',
          scene.getName());
      }
    }
  }

  /**
   * 注册获取是否待分屏状态事件
   *
   * @param callback
   * @param screenId
   */
  public registerGetIsPairSplitCallback(callback: Function, screenId: number): void {
    this.getIsPairSplitCallbackMap.set(screenId, callback);
  }

  /**
   * 反注册获取是否待分屏状态事件
   *
   * @param screenId
   */
  public unRegisterGetIsPairSplitCallback(screenId: number): void {
    this.getIsPairSplitCallbackMap.delete(screenId);
  }

  /**
   * 获取是否待分屏状态
   *
   * @param screenId
   * @returns
   */
  public getIsPairSplit(screenId: number): boolean {
    let getIsPairSplitCallback = this.getIsPairSplitCallbackMap.get(screenId);
    if (getIsPairSplitCallback) {
      return getIsPairSplitCallback();
    }
    return false;
  }

  /**
   * 注册UI上下文
   *
   * @param context
   * @param screenId
   */
  public registerUIContext(context: UIContext, screenId: number) {
    if (screenId !== INVALID_SCREENID && context) {
      this.contextMap.set(screenId, context);
    }
  }

  public unregisterUIContext(screenId: number) {
    if (screenId !== INVALID_SCREENID) {
      this.contextMap.delete(screenId);
    }
  }

  /**
   * 注册给对应screen的containerSessionListNotShowRecent添加containerSession事件
   *
   * @param callback
   * @param screenId
   */
  public registerAddListNotShowRecentCallback(callback: Function, screenId: number): void {
    this.addListNotShowRecentCallbackMap.set(screenId, callback);
  }

  /**
   * 解注册给对应screen的containerSessionListNotShowRecent添加containerSession事件
   *
   * @param screenId
   */
  public unRegisterAddListNotShowRecentCallback(screenId: number): void {
    this.addListNotShowRecentCallbackMap.delete(screenId);
  }

  /**
   * 给对应screen的containerSessionListNotShowRecent添加containerSession
   *
   * @param screenId
   * @returns
   */
  private addListNotShowRecent(screenId: number, containerSession: SCBSceneContainerSession): void {
    let addListNotShowRecentCallback = this.addListNotShowRecentCallbackMap.get(screenId);
    if (addListNotShowRecentCallback) {
      addListNotShowRecentCallback(containerSession);
    }
  }

  /**
   * 注册给对应screen的containerSessionListNotShowRecent删除containerSession事件
   *
   * @param callback
   * @param screenId
   */
  public registerRemoveListNotShowRecentCallback(callback: Function, screenId: number): void {
    this.removeListNotShowRecentCallbackMap.set(screenId, callback);
  }

  /**
   * 解注册给对应screen的containerSessionListNotShowRecent删除containerSession事件
   *
   * @param screenId
   */
  public unRegisterRemoveListNotShowRecentCallback(screenId: number): void {
    this.removeListNotShowRecentCallbackMap.delete(screenId);
  }

  /**
   * 从对应screen的containerSessionListNotShowRecent删除containerSession
   *
   * @param screenId
   * @returns
   */
  private removeListNotShowRecent(screenId: number, containerSession: SCBSceneContainerSession): void {
    let removeListNotShowRecentCallback = this.removeListNotShowRecentCallbackMap.get(screenId);
    if (removeListNotShowRecentCallback) {
      removeListNotShowRecentCallback(containerSession);
    }
  }

  /**
   * 注册对应screen的SceneHiddenModeVisChange回调函数
   *
   * @param callback
   * @param screenId
   */
  public registerSceneHiddenModeVisChangeCallback(callback: Function, screenId: number): void {
    this.sceneHiddenModeVisChangeCallbackMap.set(screenId, callback);
  }

  /**
   * 解注册对应screen的SceneHiddenModeVisChange回调函数
   *
   * @param screenId
   */
  public unRegisterSceneHiddenModeVisChangeCallback(screenId: number): void {
    this.sceneHiddenModeVisChangeCallbackMap.delete(screenId);
  }

  /**
   * 调用对应screen的onSceneHiddenModeVisChange
   *
   * @param screenId
   * @returns
   */
  private sceneHiddenModeVisChange(screenId: number, containerSession: SCBSceneContainerSession,
    visible?: boolean, isFromClient?: boolean): void {
    let sceneHiddenModeVisChangeCallback = this.sceneHiddenModeVisChangeCallbackMap.get(screenId);
    if (sceneHiddenModeVisChangeCallback) {
      sceneHiddenModeVisChangeCallback(containerSession, visible, isFromClient);
    }
  }

  /**
   * move containerSession to another screen for display
   *
   * @param containerSession
   * @param fromScreenId
   * @param toScreenId
   * @param visible
   * @param isFromClient
   * @returns
   */
  public moveContainerSessionToAnotherScreenForDisplay(containerSession: SCBSceneContainerSession,
    fromScreenId: number, toScreenId: number, visible?: boolean, isFromClient?: boolean): void {
    let targetScreenProperty = this.getTargetScreenProperty(toScreenId);
    if (!targetScreenProperty) {
      log.showError(`get targetScreenProperty failed!`);
      return;
    }
    this.removeListNotShowRecent(fromScreenId, containerSession);
    this.updateExtendContainerSession(containerSession, targetScreenProperty);
    this.addListNotShowRecent(toScreenId, containerSession);
    this.sceneHiddenModeVisChange(toScreenId, containerSession, visible, isFromClient);
  }

  /**
   * update containerSession when changeMode
   *
   * @param containerSession
   * @param screenProperty
   */
  public updateContainerSessionWithChangeMode(containerSession: SCBSceneContainerSession,
    originScreenProperty: SCBScreenProperty, targetScreenProperty: SCBScreenProperty, targetDensity: number): void {
    log.showInfo(`updateContainerSessionWithChangeMode`);
    let oldScreenProperty = new SCBScreenProperty();
    oldScreenProperty.copy(containerSession.screenProperty);
    if (containerSession.screenProperty.screenId === targetScreenProperty.screenId) {
      containerSession.updateScreenProperty(targetScreenProperty);
    } else {
      // 子窗和主窗不在一个屏幕,需要用originScreenProperty作为oldScreenProperty
      oldScreenProperty.copy(originScreenProperty);
    }
    if (containerSession.primarySession) {
      log.showInfo(`updateContainerSessionWithChangeMode primarySession ` +
        `persistentId: ${containerSession.primarySession.sceneInfo.persistentId},` +
        `bundleName: ${containerSession.primarySession.sceneInfo.bundleName}`);
      const oldMainRect: SCBSessionRect = containerSession.primarySession.currRect.copy();
      if (containerSession.screenProperty.screenId === targetScreenProperty.screenId) {
        this.doUpdateFreeAndFullSession(containerSession, oldScreenProperty, targetScreenProperty, targetDensity);
      }
      log.showInfo(`updateContainerSessionWithChangeMode: ` +
        `sceneSession: ${containerSession.primarySession.getName()}, ` +
        `rect from ${oldMainRect.printPx()} change to ${containerSession.primarySession.currRect.printPx()}`);
      this.updateSubSession(containerSession.primarySession, oldScreenProperty, targetScreenProperty, oldMainRect,
        containerSession.screenProperty.screenId, targetDensity);
    }
    this.updateSplitSessionWithChangeMode(containerSession, oldScreenProperty, targetScreenProperty);
  }

  private doUpdateFreeAndFullSession(containerSession: SCBSceneContainerSession,
    oldScreenProperty: SCBScreenProperty, targetScreenProperty: SCBScreenProperty, targetDensity?: number): void {
    if (!containerSession.primarySession) {
      return;
    }
    let windowMode = containerSession.primarySession.sceneInfo.windowMode;
    switch (windowMode) {
      case SCBSceneMode.FLOATING:
        if (this.isNeedUpdateSessionRect(containerSession.primarySession, targetScreenProperty.screenId)) {
          this.updateRectWithDpiChange(containerSession.primarySession.currRect, oldScreenProperty.screenId,
            targetScreenProperty.screenId, targetDensity);
        }
        LayoutPolicyHelper.getInstance().handleFreeWindowRect(containerSession.primarySession.currRect,
          oldScreenProperty, targetScreenProperty, containerSession.primarySession);
        let curDensity: number | undefined = targetDensity !== undefined ? targetDensity :
          this.screenDensityMap.get(targetScreenProperty.screenId);
        if (curDensity !== undefined) {
          containerSession.primarySession.sessionData.lastDpi = curDensity;
        }
        break;
      case SCBSceneMode.FULLSCREEN:
        LayoutPolicyHelper.getInstance().handleFullScreenWindowRect(containerSession.primarySession,
          oldScreenProperty, targetScreenProperty);
        break;
      default:
        break;
    }
  }

  private updateSplitSessionWithChangeMode(containerSession: SCBSceneContainerSession,
    oldScreenProperty: SCBScreenProperty, screenProperty: SCBScreenProperty): void {
    if (containerSession.isSplit && containerSession.secondarySession) {
      if (containerSession.screenProperty.screenId === screenProperty.screenId) {
        this.doUpdateSplitSession(containerSession, screenProperty);
      }
      if (containerSession.primarySession) {
        if (containerSession.screenProperty.screenId === screenProperty.screenId) {
          LayoutPolicyHelper.getInstance().handleFreeWindowRect(containerSession.primarySession.lastRect,
            oldScreenProperty, screenProperty, containerSession.primarySession);
        }
      }
      const oldMainRect: SCBSessionRect = containerSession.secondarySession.currRect.copy();
      if (containerSession.screenProperty.screenId === screenProperty.screenId) {
        LayoutPolicyHelper.getInstance().handleFreeWindowRect(containerSession.secondarySession.lastRect,
          oldScreenProperty, screenProperty);
      }
      this.updateSubSession(containerSession.secondarySession, oldScreenProperty, screenProperty, oldMainRect,
        containerSession.screenProperty.screenId);
    }
  }

  private doUpdateSplitSession(containerSession: SCBSceneContainerSession, screenProperty: SCBScreenProperty): void {
    const primaryWidth = Number.parseFloat(containerSession.dividerParam.primary.width);
    const secondaryWidth = Number.parseFloat(containerSession.dividerParam.secondary.width);
    const ratios = primaryWidth / (primaryWidth + secondaryWidth);
    let uiContext: UIContext | undefined = this.contextMap.get(screenProperty.screenId);
    const splitSceneWidth = screenProperty.width -
      SCBSystemSceneUtils.getInstance().vp2px(uiContext, DividerStyle.DIVIDER_WIDTH);
    let priTargetWidth = ratios * splitSceneWidth;
    let secTargetWidth = (1 - ratios) * splitSceneWidth;
    const newPriTargetWidth = LayoutPolicyHelper.getInstance().calculatePrimaryWidthBySplitMinWidth(
      containerSession, screenProperty, priTargetWidth, secTargetWidth);
    secTargetWidth = priTargetWidth + secTargetWidth - newPriTargetWidth;
    priTargetWidth = newPriTargetWidth;
    log.showInfo(`doUpdateSplitSession priTargetWidth: ${priTargetWidth},` +
      ` secTargetWidth: ${secTargetWidth}`);
    let topThreshold = this.getTopThreshold(screenProperty.screenId);
    let bottomThreshold = this.getBottomThreshold(screenProperty);
    containerSession.dividerParam.primary.width = (priTargetWidth / screenProperty.width * 100).toString() + '%';
    containerSession.dividerParam.secondary.width = (secTargetWidth / screenProperty.width * 100).toString() + '%';
    containerSession.dividerParam.primary.height = (bottomThreshold - topThreshold) / screenProperty.height * 100 + '%';
    containerSession.dividerParam.secondary.height = containerSession.dividerParam.primary.height;
    SCBSplitLayoutHelper.refreshSplitContainer(containerSession, screenProperty, undefined);
    if (containerSession.primarySession && containerSession.secondarySession) {
      containerSession.dividerParam.positionX = `${containerSession.isSplitPrimaryMode() ?
        SCBSystemSceneUtils.getInstance().px2vp(uiContext, containerSession.primarySession.currRect.width.getPx()) :
        SCBSystemSceneUtils.getInstance().px2vp(uiContext, containerSession.secondarySession.currRect.width.getPx())}`;
      containerSession.dividerParam.positionY = `${SCBSystemSceneUtils.getInstance().px2vp(uiContext, topThreshold)}`;
      containerSession.dividerParam.dividerWidth = DividerStyle.DIVIDER_WIDTH;
      containerSession.dividerParam.dividerHeight = SCBSystemSceneUtils.getInstance()
        .px2vp(uiContext, bottomThreshold - topThreshold);
    }
  }

  /**
   * check whether is full screen size session which need to follow screen change, including two cases:
   * 1. full screen size session whose isFollowScreenChange flag is true
   * 2. full screen size session whose type is TYPE_FLOAT
   *
   * @param specificSession subsession
   * @param screenProperty target screen property
   */
  public isFullScreenSessionNeedFollowScreen(specificSession: SCBSpecificSession,
    screenProperty: SCBScreenProperty): boolean {
    const isSessionFullScreen: boolean =
      LayoutPolicyHelper.getInstance().isFullScene(specificSession.currRect, screenProperty);
    return isSessionFullScreen && (specificSession.isFollowScreenChange ||
      specificSession.session?.type === sceneSessionManager.SessionType.TYPE_FLOAT)
  }

  /**
   * check whether is non-full screen size session whose type is TYPE_FLOAT, indicating pip case
   *
   * @param specificSession subsession
   * @param screenProperty target screen property
   */
  public isNonFullScreenFloatSession(specificSession: SCBSpecificSession, screenProperty: SCBScreenProperty): boolean {
    const isSessionFullScreen: boolean =
      LayoutPolicyHelper.getInstance().isFullScene(specificSession.currRect, screenProperty);
    return specificSession.session?.type === sceneSessionManager.SessionType.TYPE_FLOAT && !isSessionFullScreen;
  }

  /**
   * move hidden containerSession to another screen for display
   *
   * @param containerSession
   * @param fromScreenId
   * @param toScreenId
   */
  public moveHiddenSessionToAnotherScreenForDisplay(containerSession: SCBSceneContainerSession, fromScreenId: number,
    toScreenId: number) {
    this.removeListNotShowRecent(fromScreenId, containerSession);
    containerSession.primarySession?.updateDisplayId(toScreenId);
    if (containerSession.screenProperty) {
      containerSession.screenProperty.screenId = toScreenId;
    }
    this.addListNotShowRecent(toScreenId, containerSession);
  }

  public updateDensity(screenId: number, density: number): void {
    this.screenDensityMap.set(screenId, density);
  }

  private initListener() {
    try {
      display.on('change', (displayId: number) => { this.notifyDisplayChange(displayId) });
    } catch (error) {
      log.showError(`ondisplaychenge error.code: ${error?.code}, error.message: ${error?.message}`);
    }
  }

  private notifyDisplayChange(displayId: number): void {
    log.showInfo(`notifyDisplayChange displayId:${displayId}`);
    let callback = this.displayChangeCallbackMap.get(displayId);
    if (!callback) {
      log.showError(`notifyDisplayChange get callback failed. displayId:${displayId}`);
      return;
    }
    callback();
  }

  /**
   * 注册display变化监听
   * @param callback
   * @param screenId
   */
  public registerDisplayChangeCallback(callback: Function, screenId: number): void {
    if (!callback) {
      log.showWarn(`registerDisplayChangeCallback failed. screenId:${screenId}`);
      return;
    }
    this.displayChangeCallbackMap.set(screenId, callback);
  }

  /**
   * 解注册display变化监听
   * @param screenId
   */
  public unregisterDisplayChangeCallback(screenId: number): void {
    this.displayChangeCallbackMap.delete(screenId);
  }
}