/*
 * 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 curves from '@ohos.curves';
import {
  LogDomain,
  LogHelper,
  RectInfo
} from '@ohos/basicutils';
import { RTLUtil } from '@ohos/componenthelper';
import { SCBScreenSessionManager } from '@ohos/windowscene';
import { DeviceHelper } from '@ohos/frameworkwrapper';
import { FoldAnimationConstants } from '../constants/FoldAnimationConstants';
import GridLayoutItemInfo from '../bean/GridLayoutItemInfo';
import { DockItemInfo } from '../bean/DockItemInfo';
import { LayoutViewModel } from '../viewmodel/LayoutViewModel';
import { DeviceState, CommonConstants } from '../constants/CommonConstants';
import { RecentLayoutCacheMgr, ResidentLayoutCacheMgr } from '../TsIndex';

const TAG: string = 'FoldExpandAnimUtils';
const log: LogHelper = LogHelper.getLogHelper(LogDomain.WINDOW, TAG);

export class FoldExpandAnimUtils {
  // true for big or small fold product
  public static isSupportProduct: boolean = FoldExpandAnimUtils.checkIfSupportProduct();
  // true for big fold product
  private static isLargeFoldProduct: boolean = DeviceHelper.isLargeInFoldProduct();
  // true for three fold product
  public static isUltraScreenProduct: boolean = DeviceHelper.isUltraScreenProduct();

  /**
   * Judge whether the large or small fold product
   *
   * @returns true for fold product
   */
  public static checkIfSupportProduct(): boolean {
    let isLargeFold: boolean = DeviceHelper.isLargeInFoldProduct();
    return isLargeFold;
  }

  /**
   * Calculate item initial translate in X-axis direction
   *
   * @param itemColumn item column in desktop
   * @param totalColumns number of total columns
   * @param translateX default translateX
   * @param screenWidth width of screen
   * @param isExpand expanded state
   * @returns item animation initial translateX
   */
  public static initItemTransX(itemColumn: number, totalColumns: number, translateX: number, screenWidth: number,
    isExpand: boolean): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      let initialTransX: number = FoldAnimationConstants.INITIAL_VALUE;
      let relativeColumn: number = 0;
      if (isExpand) {
        relativeColumn = FoldExpandAnimUtils.getItemRelativeCol(itemColumn, totalColumns);
        if (relativeColumn === FoldAnimationConstants.CENTER_COLUMN) {
          return initialTransX;
        }
        initialTransX = FoldExpandAnimUtils.getExpandLinearParam(FoldAnimationConstants.TRANS_EDGE * screenWidth,
          FoldAnimationConstants.TRANS_MID * screenWidth, relativeColumn);
        if (itemColumn >= totalColumns / 2) {
          initialTransX = -initialTransX;
        }
        return RTLUtil.isRTL() ? -initialTransX : initialTransX;
      } else {
        relativeColumn = itemColumn + (FoldAnimationConstants.MAX_COLUMN / 2 - totalColumns) / 2;
        initialTransX = -FoldExpandAnimUtils.getFoldLinearParam(FoldAnimationConstants.FOLD_TRANS_EDGE * screenWidth,
          FoldAnimationConstants.FOLD_TRANS_MID * screenWidth, relativeColumn);
        return initialTransX;
      }
    }
    return translateX;
  }

  /**
   * Calculate item initial translate in Y-axis direction
   *
   * @param itemRow number of item column
   * @param screenHeight height of screen
   * @returns item animation initial translateY
   */
  public static initItemTransY(itemRow: number, screenHeight: number, isExpand: boolean = true): number {

    return 0;
  }

  /**
   * Calculate item animation delay for different column of row
   *
   * @param itemRow number of item row
   * @param itemColumn number of item column
   * @param number of total columns
   * @param isExpand expanded state
   * @returns animation delay time
   */
  public static getFoldExpandDelayTime(itemRow: number, itemColumn: number, totalColumns: number,
    isExpand: boolean): number {
    let itemDelay: number = 0;

    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      if (isExpand) {
        let relativeColumn: number = FoldExpandAnimUtils.getItemRelativeCol(itemColumn, totalColumns);
        itemDelay =
          FoldExpandAnimUtils.getExpandLinearParam(FoldAnimationConstants.DELAY_EDGE, FoldAnimationConstants.DELAY_MID,
            relativeColumn);
      } else {
        itemDelay = FoldExpandAnimUtils.getFoldLinearParam(FoldAnimationConstants.DELAY_EDGE,
          FoldAnimationConstants.DELAY_MID, itemColumn);
      }
      return itemDelay;
    }
    let delayFactor: number = isExpand ? FoldAnimationConstants.EXPAND_DELAY : FoldAnimationConstants.FOLD_DELAY;
    return (totalColumns - 1 - itemColumn) * delayFactor / (totalColumns - 1);
  }

  /**
   * Calculate alpha offset delay of middle col or row
   *
   * @param itemRow number of item row
   * @param itemColumn number of item column
   * @returns alpha offset delay time
   */
  public static getMidColOrRowAlphaDelayOffsetTime(itemRow: number, itemColumn: number): number {
    let totalRows: number = FoldExpandAnimUtils.getTotalRows();
    let totalColumns: number = 2 * FoldExpandAnimUtils.getTotalColumns();
    if (FoldExpandAnimUtils.isMiddleRow(itemRow, totalRows)) {
      return FoldAnimationConstants.MIDDLE_ROW_DELAY;
    }
    if (FoldExpandAnimUtils.isMiddleCol(itemColumn, totalColumns)) {
      return FoldAnimationConstants.MIDDLE_DELAY;
    }
    return 0;
  }

  /**
   * Judge whether the middle column in large fold
   *
   * @param itemColumn item column
   * @param totalColumn  total columns
   * @returns true for middle column
   */
  public static isMiddleCol(itemColumn: number, totalColumn: number): boolean {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      let relativeColumn: number = FoldExpandAnimUtils.getItemRelativeCol(itemColumn, totalColumn);
      return (relativeColumn === Math.floor(FoldAnimationConstants.CENTER_COLUMN) ||
        relativeColumn === FoldAnimationConstants.CENTER_COLUMN);
    }
    return false;
  }

  /**
   * Judge whether the middle row in small fold
   *
   * @param itemRow item row
   * @param totalColumn  total rows
   * @returns true for middle row
   */
  public static isMiddleRow(itemRow: number, totalRow: number): boolean {
    let centerRow: number = (LayoutViewModel.getInstance().calculateDesktop().mRows ?? 0) / 2;
    return false;
  }

  /**
   * Calculate dock item expand animation delay time
   *
   * @param rectInfo dock item param
   * @param screenWidth width of screen
   * @param itemColumn number of dock item column
   * @param totalColumn number of dock total columns
   * @returns delay time
   */
  public static getDockExpandAnimDelay(rectInfo: RectInfo, screenWidth: number, itemColumn: number,
    totalColumn: number): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      let relativeColumn: number = FoldExpandAnimUtils.getItemRelativeCol(itemColumn, totalColumn);
      relativeColumn = (relativeColumn === FoldAnimationConstants.CENTER_COLUMN) ?
      Math.floor(FoldAnimationConstants.CENTER_COLUMN) : relativeColumn;
      return FoldExpandAnimUtils.getExpandLinearParam(FoldAnimationConstants.DELAY_EDGE,
        FoldAnimationConstants.DELAY_MID, relativeColumn);
    }
    return (screenWidth - rectInfo.right + (rectInfo.right - rectInfo.left) / 2) / screenWidth *
      FoldAnimationConstants.EXPAND_DELAY;
  }

  /**
   * Calculate dock item fold animation delay time
   *
   * @param itemColumn item column
   * @param totalColumn total columns
   * @returns delay time
   */
  public static getDockFoldAnimDelay(itemColumn: number, totalColumn: number): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      let relativeColumn: number = itemColumn + (FoldAnimationConstants.MAX_COLUMN / 2 - totalColumn) / 2;
      return FoldExpandAnimUtils.getFoldLinearParam(FoldAnimationConstants.DELAY_EDGE, FoldAnimationConstants.DELAY_MID,
        relativeColumn);
    }
    let totalColumns: number = FoldExpandAnimUtils.getTotalColumns();
    return (totalColumns - 1 - itemColumn) * FoldAnimationConstants.FOLD_DELAY / (totalColumns - 1);
  }

  /**
   * Calculate the relative column of the central axis
   *
   * @param itemColumn item column
   * @param totalColumn total columns
   * @returns number of relative column
   */
  private static getItemRelativeCol(itemColumn: number, totalColumn: number): number {
    let column: number = 0;
    if (itemColumn < totalColumn / 2) {
      column = itemColumn + (FoldAnimationConstants.MAX_COLUMN - totalColumn) / 2;
    } else {
      column = Math.floor(totalColumn / 2) - 1 - itemColumn % Math.ceil(totalColumn / 2) +
        (FoldAnimationConstants.MAX_COLUMN - totalColumn) / 2;
    }
    return column;
  }

  /**
   * Calculate the relative row of the central axis
   *
   * @param itemRow item row
   * @param totalRow total rows
   * @returns number of relative row
   */
  private static getItemRelativeRow(itemRow: number, totalRow: number): number {
    let row: number = 0;
    if (itemRow < totalRow / 2) {
      row = itemRow;
    } else {
      row = Math.floor((totalRow - 1) / 2) - itemRow % Math.ceil((totalRow - 1) / 2);
    }
    return row;
  }

  /**
   * Calculate linear mapping expand animation parameters
   *
   * @param max maximum boundary value
   * @param min minimum boundary value
   * @param itemColOrRow item column or row
   * @returns linear mapping value
   */
  public static getExpandLinearParam(max: number, min: number, itemColOrRow: number): number {
    return (1 - itemColOrRow / (FoldAnimationConstants.MAX_COLUMN / 2 - 1)) * (max - min) + min;
  }

  /**
   * Calculate linear mapping fold animation parameters
   *
   * @param max maximum boundary value
   * @param min minimum boundary value
   * @param itemColumn item column
   * @returns linear mapping value
   */
  public static getFoldLinearParam(max: number, min: number, itemColumn: number): number {
    return itemColumn / (FoldAnimationConstants.MAX_COLUMN / 2 - 1) * (max - min) + min;
  }

  /**
   * Get initial scale of animation
   *
   * @returns number of initial item scale
   */
  public static getFoldExpandAnimScale(): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return FoldAnimationConstants.START_SCALE;
    }
    return 1;
  }

  /**
   * Calculate items animation curve when expand
   *
   * @param itemRow number of item row
   * @param itemColumn number of item column
   * @returns ICurve
   */
  public static getIconExpandAnimCurve(itemRow: number, itemColumn: number): ICurve {
    let totalRows: number = FoldExpandAnimUtils.getTotalRows();
    let totalColumns: number = 2 * FoldExpandAnimUtils.getTotalColumns();
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      let relativeColumn: number = FoldExpandAnimUtils.getItemRelativeCol(itemColumn, totalColumns);
      relativeColumn = (relativeColumn === FoldAnimationConstants.CENTER_COLUMN) ?
        Math.floor(FoldAnimationConstants.CENTER_COLUMN) : relativeColumn;
      return curves.springMotion(FoldExpandAnimUtils.getExpandLinearParam(FoldAnimationConstants.RESPONSE_EDGE,
        FoldAnimationConstants.RESPONSE_MID, relativeColumn), 1, 0);
    }
    return FoldAnimationConstants.TRANSLATE_CURVES;
  }

  /**
   * Calculate dock items animation curve when expand
   *
   * @param itemColumn number of item column
   * @param number of total columns
   * @returns ICurve
   */
  public static getDockItemExpandAnimCurve(itemColumn: number, totalColumns: number): ICurve {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      let relativeColumn: number = FoldExpandAnimUtils.getItemRelativeCol(itemColumn, totalColumns);
      relativeColumn = (relativeColumn === FoldAnimationConstants.CENTER_COLUMN) ?
        Math.floor(FoldAnimationConstants.CENTER_COLUMN) : relativeColumn;
      return curves.springMotion(FoldExpandAnimUtils.getExpandLinearParam(FoldAnimationConstants.RESPONSE_EDGE,
        FoldAnimationConstants.RESPONSE_MID, relativeColumn), 1, 0);
    }
    return FoldAnimationConstants.TRANSLATE_CURVES;
  }

  /**
   * Get Icon item Animate Curve while devices is in Folded status
   *
   * @returns ICurve
   */
  public static getIconFoldedAnimCurve(): ICurve {
    return FoldExpandAnimUtils.isLargeFoldProduct ?
      FoldAnimationConstants.ITEM_FOLD_CURVE : FoldAnimationConstants.TRANSLATE_CURVES;
  }

  /**
   * Calculate desktop item column
   *
   * @param item item information
   * @param indexInSwiper
   * @param totalColumn total columns
   * @returns number of item column
   */
  public static getSwiperPageColumns(item: GridLayoutItemInfo, indexInSwiper: number, totalColumn: number): number {
    let foldStatus: number = SCBScreenSessionManager.getInstance().isFoldablePhoneExpandStatus() ?
      DeviceState.EXPAND_STATE : DeviceState.FOLDED_STATE;
    let column: number = 0;
    if (foldStatus === DeviceState.EXPAND_STATE && indexInSwiper && indexInSwiper % 2 === 1) {
      column = (item.column ? item.column : 0) + totalColumn;
    } else {
      column = item.column ? item.column : 0;
    }
    let area: number[] = item.area ? item.area : [0, 0];
    return column + (area[0] - 1) / 2;
  }

  /**
   * Calculate desktop item row
   *
   * @param item item information
   * @returns number of item row
   */
  public static getSwiperPageRows(item: GridLayoutItemInfo): number {
    let row: number = item.row ?? 0;
    let area: number[] = item.area ? item.area : [0, 0];
    return row + (area[1] - 1) / 2;
  }

  /**
   * Get end scale of dock backplane animation
   *
   * @param dockWidth the width of backplane
   * @returns scale of backplane
   */
  public static getBackPlaneAnimScale(dockWidth: number): number {
    return FoldExpandAnimUtils.isLargeFoldProduct ? 1 : (dockWidth - FoldAnimationConstants.INIT_DOCK_BG_WIDTH) /
      FoldAnimationConstants.DIFF_DOCK_BG_WIDTH * FoldAnimationConstants.DIFF_DOCK_BG_SCALE + 2;
  }

  /**
   * Get the total columns
   *
   * @returns number of total columns
   */
  private static getTotalColumns(): number {
    const result = LayoutViewModel.getInstance().calculateDesktop();
    return (result.mColumns === undefined) ? FoldAnimationConstants.MAX_COLUMN / 2 : result.mColumns;
  }

  /**
   * Get the total columns
   *
   * @returns number of total columns
   */
  private static getTotalRows(): number {
    return LayoutViewModel.getInstance().calculateDesktop().mRows ?? 0;
  }

  /**
   * Get dock item column
   *
   * @param index item column
   * @param isResident whether item in Resident Dock
   * @returns [itemColumn, totalColumn]
   */
  public static getDockColumn(index: number, isResident: boolean = true): number[] {
    const recentList: DockItemInfo[] = RecentLayoutCacheMgr.getInstance().getAllDockItems();
    const residentList: DockItemInfo[] = ResidentLayoutCacheMgr.getInstance().getAllDockItems();
    let itemColumn: number = isResident ? index : (residentList.length ? index + residentList.length : index + 1);
    let visibleRecentLength: number | undefined = RecentLayoutCacheMgr.getInstance().getVisibleCount();
    if (visibleRecentLength === 0 || visibleRecentLength === undefined) {
      visibleRecentLength = recentList.length;
    }
    let totalColumn: number = residentList.length ? visibleRecentLength + residentList.length : visibleRecentLength + 1;
    return [itemColumn, totalColumn];
  }

  /**
   * Get indicator animation delay time in different product
   *
   * @returns indicator animation delay time
   */
  public static getIndicatorAnimDelay(): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return 0;
    }
    return FoldAnimationConstants.EXPAND_DELAY / 2;
  }

  /**
   * Init indicator translateX in different product
   *
   * @returns initial translateX
   */
  public static initIndicatorTransX(screenWidth: number, isExpand: boolean = true): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      if (isExpand) {
        return FoldAnimationConstants.INITIAL_VALUE;
      }
      return -FoldAnimationConstants.INDICATOR_TRANS * screenWidth;
    }
    return FoldAnimationConstants.EXPAND_FACTOR * screenWidth;
  }

  /**
   * Init indicator translateY in different product
   *
   * @returns initial translateY
   */
  public static initIndicatorTransY(screenHeight: number): number {
    return 0;
  }

  /**
   * Init Indicator animation scale in different product
   *
   * @returns indicator initial scale
   */
  public static initIndicatorScale(): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return FoldAnimationConstants.START_SCALE;
    }
    return 1;
  }

  /**
   * Get indicator animation param in different product
   *
   * @param delayTime delay time
   * @returns indicator animation param
   */
  public static getIndicatorExpandAnimParam(delayTime: number): AnimateParam {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return {
        curve: FoldAnimationConstants.SCALE_CURVE,
      } as AnimateParam;
    }
    return {
      curve: FoldAnimationConstants.TRANSLATE_CURVES,
      delay: delayTime,
    } as AnimateParam;
  }

  /**
   * Get Indicator's Alpha AnimateParam
   *
   * @param delayTime Animate Delay Time
   * @returns AnimateParam
   */
  public static getIndicatorAlphaAnimParam(delayTime: number): AnimateParam {

    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return {
        curve: FoldAnimationConstants.SHARP_BEZIER_CURVE,
        duration: FoldAnimationConstants.INDICATOR_ALPHA_DURATION,
        delay: delayTime,
      } as AnimateParam;
    }
    return { curve: FoldAnimationConstants.ALPHA_CURVES, delay: delayTime } as AnimateParam;
  }

  /**
   * Get Indicator's Translate Curve
   *
   * @returns ICurve
   */
  public static getIndicatorTranslateCurve(): ICurve {
    return FoldExpandAnimUtils.isLargeFoldProduct ? FoldAnimationConstants.INDICATOR_CURVE :
      FoldAnimationConstants.TRANSLATE_CURVES;
  }

  /**
   * Get Status Bar Start translateX
   *
   * @param factor translate offset factor
   * @param screenWidth screen' width(vp)
   * @returns number of translateX
   */
  public static getStatusBarTranslateX(factor: number, screenWidth: number): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return 0;
    }
    return factor * screenWidth;
  }

  /**
   * Get Status Bar Start Opacity value
   *
   * @returns number of Opacity Value
   */
  public static getStatusBarStartOpacity(): number {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return 1;
    }
    return 0;
  }

  /**
   * Get Status Bar's Alpha AnimateParam
   *
   * @returns AnimateParam
   */
  public static getStatusBarAlphaAnimParam(): AnimateParam {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return {
        curve: FoldAnimationConstants.SHARP_BEZIER_CURVE,
        duration: FoldAnimationConstants.STATUS_BAR_ALPHA_DURATION,
        delay: FoldAnimationConstants.BAR_ALPHA_DELAY,
      } as AnimateParam;
    }
    return { curve: FoldAnimationConstants.ALPHA_CURVES } as AnimateParam;
  }

  /**
   * Get Icon item or Dock Background Alpha AnimateParam
   *
   * @param delayTime Animate Delay Time
   * @returns AnimateParam
   */
  public static getIconOrDockBgAlphaAnimParam(delayTime: number): AnimateParam {
    if (FoldExpandAnimUtils.isLargeFoldProduct) {
      return {
        curve: FoldAnimationConstants.SHARP_BEZIER_CURVE,
        duration: FoldAnimationConstants.ICON_OR_DOCK_ALPHA_DURATION,
        delay: delayTime
      } as AnimateParam;
    }
    return { curve: FoldAnimationConstants.ALPHA_CURVES, delay: delayTime } as AnimateParam;
  }

  /**
   * Get Dock Background Scale Curve
   *
   * @returns ICurve
   */
  public static getDockBgPanelScaleCurve(): ICurve {
    return FoldExpandAnimUtils.isLargeFoldProduct ? FoldAnimationConstants.SCALE_CURVE :
      FoldAnimationConstants.SCALE_CURVES;
  }

  /**
   * Get Dock Background start translateX while devices is in Expand status
   *
   * @param screenWidth screen' width
   * @returns number of translateX
   */
  public static getDockBgExpandAnimStartTranslateX(screenWidth: number): number {
    return FoldExpandAnimUtils.isLargeFoldProduct ? 0 : FoldAnimationConstants.EXPAND_FACTOR * screenWidth;
  }

  /**
   * Get Dock Background start scale size while devices is in Expand status
   *
   * @returns number of scale
   */
  public static getDockBgExpandAnimStartScaleSize(): number {
    return FoldExpandAnimUtils.isLargeFoldProduct ? FoldAnimationConstants.BACKPLANE_SCALE : 1;
  }

  /**
   * Get Dock Background animate delay value while devices is in Expand status
   *
   * @param delayFactor
   * @returns delay time
   */
  public static getRecentBgDelay(delayFactor: number): number {
    return FoldExpandAnimUtils.isLargeFoldProduct ?
      FoldAnimationConstants.DELAY_EDGE : delayFactor * FoldAnimationConstants.EXPAND_DELAY;
  }
}