/*
 * 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 systemDateTime from '@ohos.systemDateTime';
import lazy { RecordAction, RecordingState } from '../../function/recordcontrol/RecordAction';
import lazy { Action, UiStateMode } from '../../redux/actions/Action';
import lazy { ModeType } from '../../mode/ModeType';
import lazy { RecordMode } from '../../function/recordcontrol/RecordMode';
import lazy { FunctionId } from '../../function/core/functionproperty/FunctionId';
import lazy { FunctionAction } from '../../function/core/FunctionAction';
import lazy { OhCombinedState, Unsubscribe, Dispatch, reduxSubscribe, getStates } from '../../redux';
import lazy { HiLog } from '../../utils/HiLog';
import lazy { WindowDirection } from '../../utils/WindowDirection';
import lazy { DirectionToAngleUtil } from '../../utils/DirectionToAngleUtil';
import lazy { GlobalContext } from '../../utils/GlobalContext';
import lazy { PositionType, DirectionType } from '../../utils/types';
import lazy { AdaptiveLayoutService, TAB_BAR_HEIGHT } from '../../service/UIAdaptive/AdaptiveLayoutService';
import lazy { execDispatch } from '../../redux/Store';
import lazy { EventBusManager } from '../../worker/eventbus/EventBusManager';
import lazy { EventBus } from '../../worker/eventbus/EventBus';
import lazy { BaseComponent } from '../../worker/BaseComponent';
import Curves from '@ohos.curves';
import lazy { DisplayCalculator } from '../xcomponent/DisplayCalculator';
import lazy { DeviceInfo } from '../deviceinfo/DeviceInfo';
import lazy { DisplayService } from '../../service/UIAdaptive/DisplayService';
import lazy { RecordActionType } from '../../redux/actions/RecordActionType';
import display from '@ohos.display';
import lazy { CommonConstants } from '../../statistics/CommonConstants';
import lazy { PickerUtils } from '../../utils/PickerUtils';
import Configuration from '@system.configuration';
import lazy { DirectionCorrectionService } from '../../service/direction/DirectionCorrectionService';
import lazy { RecordController, RecordType } from '../../function/recordcontrol/RecordController';
import lazy { window } from '@kit.ArkUI';
import lazy { ActionType } from '../../redux/actions/ActionType';
import lazy { WindowActionType } from '../../redux/actions/WindowActionType';
import lazy { WorkerTask } from '../../camera/WorkerTask';
import ResGetter from '../../utils/ResGetter';
import lazy { SystemLanguageUtil } from '../../utils/SystemLanguageUtil';

/* instrument ignore file */
const TAG: string = 'VideoTimer';

const VIDEO_TIMER_DEFAULT_WIDTH: number = 100;
const VIDEO_TIMER_DEFAULT_HEIGHT: number = 44;
const VIDEO_TIMER_POSITION_OFFSET: number = 36;
const VIDEO_TIMER_MINI_MARGIN_TOP_TABLET: number = 65;
const VIDEO_TIMER_MARGIN_TOP_TRICOLLAPS: number = 20;
const VIDEO_TIMER_MARGIN_LEFT_TRICOLLAPS: number = 226;
const TIME_SYSTEM: number = 60;
export const VIDEO_TIMER_MARGIN_TOP: number = 33;

export const INIT_VIDEO_TIMER_HEIGHT: number = 80;
export const INIT_VDE_VIDEO_TIMER_HEIGHT: number = 61.125;

class VideoTimerDispatcher {
  private mDispatch: Dispatch = (data) => data;

  public setDispatch(dispatch: Dispatch) {
    this.mDispatch = dispatch;
  }

  public stopRecording(): void {
    this.mDispatch(Action.uiStateWithMode(false, UiStateMode.EXCLUDE_PREVIEW));
    this.mDispatch(RecordAction.stop());
  }

  public changeFunctionValue(id: FunctionId, value: number | string | boolean): void {
    this.mDispatch(FunctionAction.changeFunctionValue(id, value));
  }

  public updateRecordingTimerComponentLargeState(isLarge: boolean): void {
    this.mDispatch(RecordAction.updateRecordingTimerComponentLargeState(isLarge));
  }
}

@Component
export struct VideoTimer {
  public static timelapseInterval: number = 500;
  private mBase: BaseComponent = new BaseComponent();
  private mEventBus: EventBus = EventBusManager.getInstance().getEventBus();
  private mSubscriber: Unsubscribe | null = null;
  private mAction: VideoTimerDispatcher = new VideoTimerDispatcher();
  private mGlobalContext: GlobalContext = GlobalContext.get();
  private displayInfo: display.Display = DisplayService.getInstance().getDisplay();
  // UX显示
  @State startTimeSeconds: number = 0;
  @State uselessSeconds: number = 0;
  @State isLockRotation: boolean = true;
  @State @Watch('videoStateOnChange') videoState: RecordingState = RecordingState.READY;
  @State mFontSize: Resource = $r('sys.float.Display_M');
  @State mLemFontSize: string = '48fp';
  @State mPosition: Position = { x: 0, y: 0 };
  @State initialLocation: Position = { x: 0, y: 0 };
  @State videoTimerAnimatePosition: Position = { x: 0, y: 0 };
  @State animateSwitch: boolean = false;
  @State mIsRecording: boolean = false;
  @State mIsPausing: boolean = false;
  @State mRecordScale: ScaleOptions = { x: 1, y: 1 };
  @State mPauseScale: ScaleOptions = { x: 0, y: 0 };
  @State mRecordOpacity: number = 1;
  @State mPauseOpacity: number = 0;
  @State recordingTime: number = 0;
  @State videoTimerHeight: number = INIT_VIDEO_TIMER_HEIGHT;
  @State intervalTime: number = 0;
  @State recordingTimeDisplay: string = '00:00';
  @State @Watch('statusChange') isShowSemiCollapsed: boolean = false;
  @State isShowLandscape: boolean = false;
  @State isShowDefault: boolean = false;
  @State windowWidth: number = 0;
  @State windowHeight: number = 0;
  @State rotateAngle: number = 0;
  @State isPickerTopBottomSplitScreenVertical: boolean = false;
  @State isPickerLeftRightSplitScreenHorizontal: boolean = false;
  @State isPickerCollapsedHorizontalSplitScreen: boolean = false;
  private isBigTimer: boolean = true;
  private mode: ModeType = ModeType.NONE;
  private mTimer: number = Number.MIN_VALUE;
  private mFontScalingTimer: number = Number.MIN_VALUE; // 组件缩放计时器
  // 位置计算
  @State videoTimerPosition: number = 0;
  @State @Watch('directionChange') mDirection: WindowDirection = WindowDirection.TOP;
  @StorageLink('closeTabBar') @Watch('handleTabBarCollaps') isCollapsTabBar: boolean = true;
  @State videoTimerOpacity: number = 1; // 同时监听方向和tabBAr的展开状态
  @State startTime: number = 0;
  @State stopTime: number = 0;
  @StorageLink('isLemCollaps') isLemCollaps: boolean = false;
  @State mLemTopSpacing: string = '0fp';
  @State mLemTopSpacingFlag: boolean = false;
  @State videoTimeRules: AlignRuleOption = {
    'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center },
    'center': { 'anchor': '__container__', 'align': VerticalAlign.Center }
  }
  @State @Watch('halfCollapsChange') isHalfCollapsLEM: boolean = false;
  @State isVdeCollapsed: boolean = false;
  @Prop landscapePreviewPosition: PositionType = { x: 0, y: 0 };
  @State isShowRingLightView: boolean = false;
  @State xComponentWidth: number = 0;
  @State xComponentHeight: number = 0;
  @Prop @Watch('onPreviewAnimatePositionChange') previewAnimatePosition: PositionType = { x: 0, y: 0 };
  private mTimerId: number = Number.MIN_VALUE;
  private tabBarPosition: number = AdaptiveLayoutService.getInstance().tabBarPosition;
  public pickerSplitScreenTabBarPosition: number = 0;
  @State isSmallVideoTimer: boolean = false;
  @Prop startVideoRecording: boolean = false;
  @State startVideoTimerRecording: boolean = false;
  @State nowTimerPosition: Position = { x: 0, y: 0 };
  private preRecordDuringTime: number = 0;
  @Prop isHdr: boolean = false;

  aboutToAppear(): void {
    HiLog.i(TAG, 'aboutToAppear E.');
    this.mEventBus.on(ActionType.ACTION_DIRECTION_CHANGE, this.onDealDirection.bind(this),
      this.mBase.hashCode());
    this.mSubscriber = reduxSubscribe((state: OhCombinedState) => {
      this.videoState = state.get<RecordingState>('recordReducer', 'recordingState');
      this.startTimeSeconds = state.get<number>('recordReducer', 'startTimeSeconds');
      this.uselessSeconds = state.get<number>('recordReducer', 'uselessSeconds');
      this.isLockRotation = state.get<boolean>('windowReducer', 'isLockRotation');
      this.mode = state.get<ModeType>('modeReducer', 'mode');
      if (this.isDirectionSubscriptionNeeded(state.get<WindowDirection>('contextReducer', 'direction'),
        state.get<boolean>('windowReducer', 'isLockRotation'),
        state.get<boolean>('collapsReducer', 'isShowSemiCollapsed'))) {
        this.directionCorrection(state.get<WindowDirection>('contextReducer', 'direction'),
          state.get<boolean>('windowReducer', 'isLockRotation'));
        this.rotateAngle = state.get<number>('contextReducer', 'rotateAngle');
      }
      this.isShowSemiCollapsed = state.get<boolean>('collapsReducer', 'isShowSemiCollapsed');
      this.isShowLandscape = state.get<boolean>('collapsReducer', 'isShowLandscape');
      this.isShowDefault = state.get<boolean>('collapsReducer', 'isShowDefault');
      this.isHalfCollapsLEM = state.get<boolean>('collapsReducer', 'isHalfCollapsLEM');
      this.isVdeCollapsed = state.get<boolean>('collapsReducer', 'isVdeCollapsed');
      this.windowWidth = state.get<number>('windowReducer', 'windowWidth');
      this.windowHeight = state.get<number>('windowReducer', 'windowHeight');
      this.xComponentWidth = state.get<number>('previewReducer', 'xComponentWidth');
      this.xComponentHeight = state.get<number>('previewReducer', 'xComponentHeight');
      this.isShowRingLightView = state.get<boolean>('ringLightReducer', 'isShowRingLightView');
    }, (dispatch: Dispatch) => {
      this.mAction.setDispatch(dispatch);
    });
    this.mAction.updateRecordingTimerComponentLargeState(true);
    this.setIntervalTimer();
    this.mEventBus.on(RecordActionType.PAUSE, this.onPause.bind(this), this.mBase.hashCode());
    this.mEventBus.on(RecordActionType.RESUME, this.onResume.bind(this), this.mBase.hashCode());

    this.mEventBus.on(RecordActionType.STOP, this.onStop.bind(this), this.mBase.hashCode());
    this.mEventBus.on(ActionType.ACTION_RECORD_START_SECONDS, this.startToRecord.bind(this), this.mBase.hashCode());
    this.mEventBus.on(WindowActionType.ON_SIZE_CHANGE, this.uxRefresh.bind(this), this.mBase.hashCode());
    this.uxInit();
    this.changeTimeLapseRecord();
    if (RecordController.getInstance().getRecordType() === RecordType.longClick) {
      this.recordingTime = this.preRecordDuringTime;
      this.recordingTimeDisplay = this.getTimerString();
    }
    HiLog.i(TAG, 'aboutToAppear X.');
  }

  aboutToDisappear(): void {
    HiLog.i(TAG, 'aboutToDisappear E.');
    this.mEventBus.clear(this.mBase.hashCode());
    this.mSubscriber?.destroy();
    HiLog.i(TAG, `clearInterval timer id: ${this.mTimer}.`);
    this.intervalClearing();
    this.fontScalingTimerClear();
    clearInterval(this.mTimerId);
    HiLog.i(TAG, 'aboutToDisappear X.');
    //组件销毁前将动画开关置为false
    this.animateSwitch = false;
  }

  private uxInit(): void {
    if (this.isVdeCollapsed) {
      this.videoTimerHeight = INIT_VDE_VIDEO_TIMER_HEIGHT;
    }
    if (PickerUtils.getIsPicker()) {
      this.isPickerTopBottomSplitScreenVertical = PickerUtils.getIsExpandedTopBottomVertical();
      this.isPickerLeftRightSplitScreenHorizontal = PickerUtils.getIsExpandedLeftRightHorizontal();
    }
    if (this.isLemCollaps) {
      this.mLemFontSize = '40fp';
    }
    this.mLemTopSpacingFlag = false;
    if (this.isShowLandscape) {
      this.videoTimerPosition = 0;
    } else {
      this.videoTimerPosition = AdaptiveLayoutService.getInstance().videoTimerPosition;
    }
    this.tabBarPosition = AdaptiveLayoutService.getInstance().tabBarPosition;
    this.mPosition = this.isHalfCollapsLEM ? { x: 0, y: this.tabBarPosition } :
      { x: 0, y: this.videoTimerPosition }
    this.nowTimerPosition = this.getPosition();
  }

  private onPreviewAnimatePositionChange(): void {
    this.nowTimerPosition = this.getPosition();
  }

  private statusChange(): void {
    HiLog.i(TAG, `VideoTimer in statusChange: ${this.animateSwitch}, ${this.isShowSemiCollapsed}.`);
    if (this.animateSwitch) {
      if (this.isShowSemiCollapsed) {
        this.LToSDisLandPosition();
        setTimeout((): void => {
          this.LToSShowSemiPosition();
        }, 150);
      } else {
        this.SemiToLandDisShowSemi();
        setTimeout((): void => {
          this.SemiToLandShowLand();
        }, 150);
      }
    } else {
      this.videoTimerAnimatePosition = this.getLayoutPosition();
      this.animateSwitch = true;
    }
    this.directionChange();
  }

  private changeTimeLapseRecord(): void {
    if (this.startVideoTimerRecording) {
      return;
    }
    if (this.startVideoRecording) {
      this.startToRecord();
    }
  }

  private videoStateOnChange() {
    HiLog.i(TAG, `videoStateOnChange videoState:${this.videoState}.`);
    if (this.videoState === RecordingState.PAUSED) {
      this.recordToPauseAnim();
    } else if (this.videoState === RecordingState.RESUMING) {
      this.pauseToRecordAnim();
    }
  }

  private isDirectionSubscriptionNeeded(direction: WindowDirection, isLockRotation: boolean,
    isShowSemiCollapsed: boolean): boolean {
    if (AppStorage.get('windowDisplayName') === 'SuperLauncher') {
      return false;
    }
    if (DeviceInfo.isTablet() && isLockRotation &&
      !this.isTabletDirectionSameAxisWithOrientation(direction, this.displayInfo.orientation)) {
      return false;
    }
    return !(this.isVertical(direction) && isShowSemiCollapsed);
  }

  private onDealDirection(data: DirectionType) {
    HiLog.i(TAG, `onDealDirection${data.direction}`);
    this.nowTimerPosition = this.getPosition();
    if (data.direction === WindowDirection.LEFT || data.direction === WindowDirection.RIGHT) {
      this.videoTimerOpacity = 1;
    } else {
      if (!this.isCollapsTabBar) {
        this.videoTimerOpacity = 0;
      }
    }
  }

  private handleTabBarCollaps() {
    if (this.isCollapsTabBar) {
      this.videoTimerOpacity = 1;
    } else {
      if ((this.mDirection === WindowDirection.TOP || this.mDirection === WindowDirection.BOTTOM) && !this.isBigTimer) {
        this.videoTimerOpacity = 0;
      }
    }
  }

  private recordToPauseAnim() {
    this.mIsPausing = true;
    animateToImmediately({
      delay: 0,
      duration: 300,
      curve: Curve.Friction,
      onFinish: () => {
        this.mIsRecording = false;
      }
    }, () => {
      this.mRecordScale = { x: 0, y: 0 }
      this.mPauseScale = { x: 1, y: 1 }
    });

    animateToImmediately({
      delay: 50,
      duration: 150,
      curve: Curve.Sharp
    }, () => {
      this.mRecordOpacity = 0;
      this.mPauseOpacity = 1;
    });
  }

  private pauseToRecordAnim() {
    this.mIsRecording = true;
    animateToImmediately({
      delay: 0,
      duration: 300,
      curve: Curve.Friction,
      onFinish: () => {
        this.mIsPausing = false;
      }
    }, () => {
      this.mPauseScale = { x: 0, y: 0 }
      this.mRecordScale = { x: 1, y: 1 }
    });

    animateToImmediately({
      delay: 50,
      duration: 150,
      curve: Curve.Sharp
    }, () => {
      this.mPauseOpacity = 0;
      this.mRecordOpacity = 1;
    });
  }

  private isTabletDirectionSameAxisWithOrientation(direction: WindowDirection,
    orientation: display.Orientation): boolean {
    const verticalAxis: display.Orientation[] = [display.Orientation.PORTRAIT, display.Orientation.PORTRAIT_INVERTED];
    if (verticalAxis.includes(orientation)) {
      return direction === WindowDirection.TOP || direction === WindowDirection.BOTTOM;
    } else {
      return direction === WindowDirection.LEFT || direction === WindowDirection.RIGHT;
    }
  }

  private getRotateAngle(): number | undefined {
    if (PickerUtils.getIsPickerInSplitScreen() && !PickerUtils.getIsTabletTopBottomSplitScreenFromReducer()) {
      return this.rotateAngle;
    }
    if (DeviceInfo.isTablet() && this.isLockRotation) {
      if (this.isOrientationVertical() && this.isVertical() || !this.isOrientationVertical() && !this.isVertical()) {
        return this.mDirection === WindowDirection.TOP ? 0 : 180;
      }
    }
    if (this.isLockRotation) {
      return this.rotateAngle;
    }
    return this.isShowSemiCollapsed ?
      this.mDirection === WindowDirection.LEFT ? 270 : 90 : DirectionToAngleUtil.geViewRotate(this.mDirection);
  }

  build() {
    if (this.isLemCollaps) {
      RelativeContainer() {
        Row() {
          Image($r('app.media.ic_video_continue'))
            .id('Image')
            .width(4)
            .height(4)
            .fillColor('#FF0000')
            .scale(this.mRecordScale)
            .opacity(this.mRecordOpacity)
          Text(`${this.recordingTimeDisplay}`)
            .margin({ left: 2 })
            .textAlign(TextAlign.Center)
            .fontSize(this.mLemFontSize)
            .maxFontScale(1)
            .fontColor(Color.White)
            .fontWeight(300)
            .fontFeature('\"ss01\" on')
            .textShadow({ color: '#33000000', radius: 3 })
            .id('VideoTimerText')
        }
        .accessibilityGroup(true)
        .accessibilityLevel(CommonConstants.ACCESSIBILITY_LEVEL_NO)
        .alignRules(this.videoTimeRules)
        .id('rect')
        .margin({ top: this.getTopSpacing() })
      }

      .width('100%')
      .height('100%')
      .hitTestBehavior(HitTestMode.None)
      .rotate({
        angle: DirectionToAngleUtil.geViewRotate(this.mDirection),
      })
    } else {
      RelativeContainer() {
        if (this.mIsRecording) {
          Image($r('app.media.ic_video_continue'))
            .id('Recording')
            .alignRules({
              top: { anchor: '__container__', align: VerticalAlign.Top },
              bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
              right: { anchor: 'VideoTimerText', align: HorizontalAlign.Start },
            })
            .offset({ x: SystemLanguageUtil.isRTL() ? 8 : -8 })
            .width(12)
            .height(12)
            .fillColor(Color.Red)
            .scale(this.mRecordScale)
            .opacity(this.mRecordOpacity)
            .renderGroup(true)
        }
        if (this.mIsPausing) {
          Image($r('app.media.ic_video_pause'))
            .id('Pausing')
            .alignRules({
              top: { anchor: '__container__', align: VerticalAlign.Top },
              bottom: { anchor: '__container__', align: VerticalAlign.Bottom },
              right: { anchor: 'VideoTimerText', align: HorizontalAlign.Start },
            })
            .offset({ x: SystemLanguageUtil.isRTL() ? 8 : -8 })
            .width(14)
            .height(14)
            .fillColor(Color.White)
            .scale(this.mPauseScale)
            .opacity(this.mPauseOpacity)
            .draggable(false)
        }

        Text(`${this.recordingTimeDisplay}`)
          .margin({ left: $r('sys.float.padding_level4'), right: $r('sys.float.padding_level4') })
          .textAlign(TextAlign.Center)
          .fontSize(this.mFontSize)
          .maxFontScale(1)
          .fontColor(Color.White)
          .fontWeight(300)
          .fontFeature('\"ss01\" on')
          .textShadow({ color: '#33000000', radius: 3 })
          .id('VideoTimerText')
          .accessibilityLevel(CommonConstants.ACCESSIBILITY_LEVEL_YES)
          .accessibilityText(this.getABText())
          .alignRules({
            center: { anchor: '__container__', align: VerticalAlign.Center },
            middle: { anchor: '__container__', align: HorizontalAlign.Center },
          })
      }
      .accessibilityGroup(true)
      .accessibilityLevel(CommonConstants.ACCESSIBILITY_LEVEL_NO)
      .position(this.nowTimerPosition)
      .rotate({
        angle: this.getRotateAngle()
      })
      .opacity(this.videoTimerOpacity)
      .width(this.getContainerWidth())
      .height(this.videoTimerHeight)
      .padding({ left: this.isVdeCollapsed ? 0 : 10 })
      .margin({ top: this.isShowLandscape && !this.isShowSemiCollapsed ? VIDEO_TIMER_MARGIN_TOP : 0 })
      .hitTestBehavior(HitTestMode.None)
    }
  }

  private getABText(): string {
    let minute = Number(this.recordingTimeDisplay.split(':')[0]);
    let sec = Number(this.recordingTimeDisplay.split(':')[1]);
    HiLog.i(TAG, `getABText minute: ${minute}, sec: ${sec}`);
    return ResGetter.getStringSafe(minute === 0 ?
      `${getContext(this)
        .resourceManager
        .getPluralStringValueSync($r('app.plural.accessibility_recorded_seconds'), sec)}` :
      `${ResGetter.getStringSafe($r('app.string.accessibility_recording_duration',
        getContext(this).resourceManager.getPluralStringValueSync($r('app.plural.accessibility_min'), minute),
        getContext(this).resourceManager.getPluralStringValueSync($r('app.plural.accessibility_sec'), sec)))}`);
  }

  private getContainerWidth(): number | string {
    if (this.isShowDefault) {
      if (this.isPickerTopBottomSplitScreenVertical && !this.isVertical() ||
        this.isPickerLeftRightSplitScreenHorizontal && !this.isVertical()) {
        return this.xComponentHeight;
      }
      return this.windowWidth;
    } else if (this.isShowLandscape && !this.isShowSemiCollapsed) {
      return this.xComponentWidth;
    }
    return '100%';
  }

  private isOrientationVertical(): boolean {
    return this.displayInfo.orientation === display.Orientation.PORTRAIT ||
      this.displayInfo.orientation === display.Orientation.PORTRAIT_INVERTED;
  }

  private getPosition(): Position {
    if (DeviceInfo.isTablet()) {
      return this.getTabletVideoTimerPosition();
    } else if (!this.isShowDefault) {
      return this.getTricollapsVideoTimerPosition();
    } else if (this.isVdeCollapsed) {
      return this.getVdeVideoTimerPosition();
    } else if (this.isShowDefault) {
      const coefficient: number = this.mDirection === WindowDirection.RIGHT ? 1 : -1;
      if (this.isPickerTopBottomSplitScreenVertical && !this.isVertical() ||
        this.isPickerLeftRightSplitScreenHorizontal && !this.isVertical()) {
        return {
          x: this.mDirection === WindowDirection.LEFT ? -(this.xComponentHeight - INIT_VIDEO_TIMER_HEIGHT) / 2 :
            coefficient * (this.windowWidth - INIT_VIDEO_TIMER_HEIGHT / 2 - this.xComponentHeight / 2),
          y: this.isHalfCollapsLEM ? -INIT_VIDEO_TIMER_HEIGHT / 2 + this.windowHeight / 4 :
            -INIT_VIDEO_TIMER_HEIGHT / 2 + AdaptiveLayoutService.getInstance().previewPosition +
              this.xComponentHeight / 2
        };
      }
      return this.isVertical() ? this.mPosition : {
        x: coefficient * (this.windowWidth / 2 - INIT_VIDEO_TIMER_HEIGHT / 2),
        y: this.isHalfCollapsLEM ? -INIT_VIDEO_TIMER_HEIGHT / 2 + this.windowHeight / 4 :
          -INIT_VIDEO_TIMER_HEIGHT / 2 + AdaptiveLayoutService.getInstance().previewPosition1to1 + this.windowWidth / 2
      };
    } else if (this.isShowSemiCollapsed) {
      return { x: INIT_VIDEO_TIMER_HEIGHT / 2 + 8, y: this.windowHeight / 2 - INIT_VIDEO_TIMER_HEIGHT / 2 };
    } else {
      HiLog.i(TAG, `getPosition isVertical: ${this.isVertical()}, mDirection: ${this.mDirection}`);
      if (this.isVertical()) {
        if (this.mDirection === WindowDirection.TOP || this.mDirection === WindowDirection.BOTTOM) {
          return { x: this.previewAnimatePosition.x, y: this.videoTimerPosition };
        } else {
          return { x: 0, y: this.windowHeight - INIT_VIDEO_TIMER_HEIGHT - this.videoTimerPosition }
        }
      } else {
        return {
          x: -this.xComponentWidth / 2 + INIT_VIDEO_TIMER_HEIGHT * 2 + 30,
          y: this.windowHeight / 2 - INIT_VIDEO_TIMER_HEIGHT / 2
        };
      }
    }
  }

  private getTabletVideoTimerPosition(): Position {
    if (PickerUtils.getIsTabletTopBottomSplitScreenFromReducer()) {
      return { x: 0, y: 30 };
    }
    if (this.isLockRotation) {
      if (this.isOrientationVertical() && this.isVertical()) {
        return this.mDirection === WindowDirection.TOP ?
          { x: 0, y: (this.windowHeight - this.windowWidth * 4 / 3) / 2 } :
          { x: 0, y: this.windowHeight / 2 + this.windowWidth * 2 / 3 - INIT_VIDEO_TIMER_HEIGHT };
      }
      if (!this.isOrientationVertical() && !this.isVertical()) {
        return this.mDirection === WindowDirection.TOP ?
          { x: 0, y: (this.windowHeight - this.windowWidth * 9 / 16) / 2 } :
          { x: 0, y: this.windowHeight / 2 + this.windowWidth * 9 / 32 - INIT_VIDEO_TIMER_HEIGHT };
      }
    }
    return this.isOrientationVertical() ? { x: 0, y: (this.windowHeight - this.windowWidth * 4 / 3) / 2 } :
      { x: 0, y: (this.windowHeight - this.windowWidth * 9 / 16) / 2 };
  }

  private getTricollapsVideoTimerPosition(): Position {
    if (this.isLockRotation) {
      if (this.mDirection === WindowDirection.TOP || this.mDirection === WindowDirection.BOTTOM) {
        return { x: this.previewAnimatePosition.x, y: this.videoTimerPosition + VIDEO_TIMER_MARGIN_TOP_TRICOLLAPS };
      } else {
        return {
          x: -this.xComponentWidth / 2 + INIT_VIDEO_TIMER_HEIGHT * 2 + 20,
          y: this.windowHeight / 2 - INIT_VIDEO_TIMER_HEIGHT / 2
        };
      }
    }
    const rotation: number = DisplayService.getInstance().getDisplay().rotation;
    switch (rotation) {
      case display.Orientation.PORTRAIT:
        return { x: this.previewAnimatePosition.x, y: this.videoTimerPosition + VIDEO_TIMER_MARGIN_TOP_TRICOLLAPS };
      case display.Orientation.LANDSCAPE:
        return { x: this.previewAnimatePosition.x, y: this.videoTimerPosition + VIDEO_TIMER_MARGIN_LEFT_TRICOLLAPS };
      case display.Orientation.PORTRAIT_INVERTED:
        return { x: this.previewAnimatePosition.x, y: this.videoTimerPosition + VIDEO_TIMER_MARGIN_TOP_TRICOLLAPS };
      case display.Orientation.LANDSCAPE_INVERTED:
        return { x: this.previewAnimatePosition.x, y: this.videoTimerPosition + VIDEO_TIMER_MARGIN_LEFT_TRICOLLAPS };
      default:
        return { x: this.previewAnimatePosition.x, y: this.videoTimerPosition };
    }
  }

  private getRotationVdeShowBackAsFront(): number {
    return this.mDirection === WindowDirection.LEFT ? this.windowWidth / 2 - INIT_VDE_VIDEO_TIMER_HEIGHT / 2 :
      INIT_VDE_VIDEO_TIMER_HEIGHT / 2 - this.windowWidth / 2;
  }

  private getVdeVideoTimerPosition(): Position {
    if (!getStates().get<boolean>('windowReducer', 'isLockRotation')) {
      return this.mPosition;
    }
    const rotation = DisplayService.getInstance().getDisplay().rotation;
    switch (rotation) {
      case 0: // 右
        return this.isVertical() ? {
          x: 0,
          y: 12
        } : {
          x: this.mDirection === WindowDirection.LEFT ? INIT_VDE_VIDEO_TIMER_HEIGHT / 2 - this.windowWidth / 2 :
            this.windowWidth / 2 - INIT_VDE_VIDEO_TIMER_HEIGHT / 2,
          y: this.windowHeight / 2 - INIT_VDE_VIDEO_TIMER_HEIGHT / 2
        };
      case 2:
        return this.isVertical() ? {
          x: 0,
          y: 12
        } : {
          x: this.getRotationVdeShowBackAsFront(),
          y: this.windowHeight / 2 - INIT_VDE_VIDEO_TIMER_HEIGHT / 2
        };
      case 3:
      default:
        return !this.isVertical() ? this.mPosition : {
          x: this.mDirection === WindowDirection.TOP ? INIT_VDE_VIDEO_TIMER_HEIGHT / 2 - this.windowWidth / 2 :
            this.windowWidth / 2 - INIT_VDE_VIDEO_TIMER_HEIGHT / 2,
          y: this.windowHeight / 2 - INIT_VDE_VIDEO_TIMER_HEIGHT / 2
        };
    }
  }

  //展开态到悬停态的定位动画(隐藏展开态的videoTimer)
  private LToSDisLandPosition(): void {
    const height: number = getStates().get<number>('windowReducer', 'windowHeight');
    HiLog.i(TAG, `getLayoutPosition ${height}`);
    this.videoTimerAnimatePosition = { x: 0, y: 0 };
    animateToImmediately({
      duration: 150,
      curve: Curves.cubicBezierCurve(0.35, 0.00, 0.52, 0.52),
    },
      () => {
        this.videoTimerAnimatePosition.x = height * 0.18;
      });
  }

  //展开态到悬停态的定位动画(显示悬停态的videoTimer)
  private LToSShowSemiPosition(): void {
    const width = getStates().get<number>('windowReducer', 'windowWidth');
    const height = getStates().get<number>('windowReducer', 'windowHeight');
    this.videoTimerAnimatePosition.x = this.getLayoutPosition().x - (height * 0.15);
    this.videoTimerAnimatePosition.y = width / 2;
    animateToImmediately({
      duration: 350,
      curve: Curves.cubicBezierCurve(0.18, 0.52, 0.47, 1.00),
    },
      () => {
        this.videoTimerAnimatePosition = this.getLayoutPosition();
      });
  }

  //悬停态到展开态先隐藏悬停态
  private SemiToLandDisShowSemi(): void {
    const width = getStates().get<number>('windowReducer', 'windowWidth');
    const height = getStates().get<number>('windowReducer', 'windowHeight');
    this.videoTimerAnimatePosition = { x: 100, y: width / 2 };
    animateToImmediately({
      duration: 150,
      curve: Curves.cubicBezierCurve(0.35, 0.00, 0.52, 0.52),
    },
      () => {
        this.videoTimerAnimatePosition.x = 100 - (height * 0.18);
      });
  }

  //悬停态到展开态展开态再出现
  private SemiToLandShowLand(): void {
    const height = getStates().get<number>('windowReducer', 'windowHeight');
    this.videoTimerAnimatePosition.x = height * 0.18;
    this.videoTimerAnimatePosition.y = 0;
    animateToImmediately({
      duration: 350,
      curve: Curves.cubicBezierCurve(0.18, 0.52, 0.47, 1.00),
    },
      () => {
        this.videoTimerAnimatePosition = this.getLayoutPosition();
      })
  }

  private bigToSmallAnimationFunction(): void {
    this.isSmallVideoTimer = true;
    this.mFontSize = $r('sys.float.Title_S');
    if (this.isVdeCollapsed) {
      this.videoTimerHeight = 30;
      this.mPosition = { x: 0, y: 12 };
      return;
    }
    if (this.isShowDefault && this.isVertical() && !DeviceInfo.isTablet()) {
      this.videoTimerHeight = 30;
    }
    if (!this.isShowLandscape && !DeviceInfo.isTablet()) {
      this.mPosition = {
        x: 0,
        y: this.tabBarPosition + this.getVideoTimerOffset()
      };
    } else if (!this.isShowLandscape && DeviceInfo.isTablet()) {
      this.mPosition = {
        x: 0,
        y: VIDEO_TIMER_MINI_MARGIN_TOP_TABLET
      };
    } else if (this.isShowSemiCollapsed) {
      this.mPosition = { x: 0, y: 0 };
    } else {
      this.mPosition = { x: 0, y: 0 };
    }
  }

  private getVideoTimerOffset(): number {
    return getStates().get<window.WindowStatusType>('windowReducer', 'windowStatus') ===
    window.WindowStatusType.FLOATING && this.isVertical() ? 20 : 0;
  }

  private bigToSmallAnim() {
    if (!this.isCollapsTabBar) {
      this.videoTimerOpacity = 0;
      this.bigToSmallAnimationFunction();
      return;
    }
    animateToImmediately({
      duration: 300,
      curve: Curve.Friction
    }, () => {
      this.bigToSmallAnimationFunction();
    });
  }

  private uxRefresh(): void {
    if (this.mTimerId !== Number.MIN_VALUE) {
      clearInterval(this.mTimerId);
    }
    this.mTimerId = setTimeout(() => {
      this.uxInit();
      this.bigToSmallAnimationFunction();
      this.nowTimerPosition = this.getPosition();
    }, 50);
  }

  private intervalClearing(): void {
    if (this.mTimer !== Number.MIN_VALUE) {
      clearInterval(this.mTimer);
      this.mTimer = Number.MIN_VALUE;
    }
  }

  private fontScalingTimerClear(): void {
    if (this.mFontScalingTimer != Number.MIN_VALUE) {
      clearTimeout(this.mFontScalingTimer);
      this.mFontScalingTimer = Number.MIN_VALUE;
    }
  }

  private async onPause(): Promise<void> {
    const currentTime: number = await systemDateTime.getRealTime(false);
    this.stopTime = currentTime;
    HiLog.i(TAG, `onTimerPaused, stopTime: ${this.stopTime}.`);
    this.intervalClearing();
  }

  private async onResume(): Promise<void> {
    const currentTime: number = await systemDateTime.getRealTime(false);
    this.startTime = currentTime;
    HiLog.i(TAG, `onTimerResumed, startTime: ${this.startTime}, last stopTime: ${this.stopTime}.`);

    const uselessTime: number = this.uselessSeconds + (this.startTime - this.stopTime) +
      // 经验值,后续待重构成优先用接口回调获取时间
      200;
    execDispatch(Action.updateRecordUselessTime(uselessTime));
    this.setIntervalTimer();
  }

  private onStop(): void {
    this.isSmallVideoTimer = false;
    this.startVideoTimerRecording = false;
    this.startVideoRecording = false;
  }

  private startToRecord(): void {
    HiLog.i(TAG, 'startToRecord in');
    this.startVideoTimerRecording = true;
    this.isSmallVideoTimer = false;
    if (this.isLemCollaps) {
      clearTimeout();
      setTimeout((): void => {
        animateTo({ curve: Curve.Linear, duration: 250 }, () => {
          this.videoTimeRules = {
            'middle': { 'anchor': '__container__', 'align': HorizontalAlign.Center },
            'top': { 'anchor': '__container__', 'align': VerticalAlign.Top }
          }
          this.mLemFontSize = '20fp';
          this.mLemTopSpacingFlag = true;
        })
      }, 3000)
      this.fontScalingTimerClear();
      this.mFontScalingTimer = setTimeout((): void => {
        this.isBigTimer = false;
        this.bigToSmallAnim();
        this.mAction.updateRecordingTimerComponentLargeState(false);
        this.fontScalingTimerClear();
      }, 0);
    } else {
      const startToRecordTimer = setTimeout((): void => {
        this.mIsRecording = true;
        HiLog.e(TAG, 'startToRecord: start recording now');
        clearTimeout(startToRecordTimer);
      }, 100);

      this.fontScalingTimerClear();
      this.mFontScalingTimer = setTimeout((): void => {
        this.isBigTimer = false;
        this.bigToSmallAnim();
        this.mAction.updateRecordingTimerComponentLargeState(false);
        this.fontScalingTimerClear();
        animateToImmediately({
          duration: 300,
          curve: Curve.Friction
        }, () => {
          this.nowTimerPosition = this.getPosition();
        });
      }, 3000);
    }
  }

  public getLayoutPosition(): PositionType {
    const windowWidth = getStates().get<number>('windowReducer', 'windowWidth');
    const windowHeight = getStates().get<number>('windowReducer', 'windowHeight');
    if (this.isShowSemiCollapsed) {
      const width: number = VIDEO_TIMER_DEFAULT_WIDTH;
      const height: number = (windowHeight >> 1) - VIDEO_TIMER_POSITION_OFFSET;
      return { x: width, y: height };
    }
    if (this.mDirection !== WindowDirection.RIGHT && this.mDirection !== WindowDirection.LEFT) {
      return { x: 0, y: 0 };
    }
    const width: number = (windowWidth >> 1) - VIDEO_TIMER_DEFAULT_HEIGHT;
    const previewLocation = AdaptiveLayoutService.getInstance().previewPositionDefaultConst;
    const height: number = (DisplayCalculator.calcSurfaceDisplaySize(4 / 3, windowWidth,
      windowHeight).height - this.videoTimerHeight) / 2 + previewLocation;
    if (this.mDirection === WindowDirection.RIGHT) {
      return { x: width, y: height };
    }
    return { x: -width, y: height };
  }

  private setIntervalTimer() {
    HiLog.i(TAG, 'setIntervalTimer.');
    this.intervalClearing();
    this.mTimer = setInterval(async () => {
      if (this.videoState === RecordingState.RECORDING) {
        this.recordingTime = await this.getCurRecordingTime();
        let currentTime = this.getTimerString();
        if (this.recordingTimeDisplay === currentTime) {
          return;
        }
        HiLog.e(TAG, `time diff ${this.recordingTimeDisplay}, ${currentTime}`);
        this.recordingTimeDisplay = currentTime;
        if (this.mGlobalContext.getIsPicker() &&
          this.mGlobalContext.getCameraAbilityWant()?.parameters?.videoDuration) {
          try {
            let videoDuration: number = Number.parseInt(this.mGlobalContext
              .getCameraAbilityWant()?.parameters?.videoDuration as string)
            HiLog.i(TAG, `videoDuration is ${videoDuration}`);
            if (this.recordingTime >= videoDuration) {
              this.mAction.stopRecording();
              this.mAction.changeFunctionValue(FunctionId.RECORD_CONTROL, RecordMode.TO_STOP);
            }
          } catch (error) {
            HiLog.e(TAG, `picker videoDuration --> ${error?.code}}`);
          }
        }
      }
    }, 70);
  }

  private async getCurRecordingTime(): Promise<number> {
    const curTime: number = await systemDateTime.getRealTime(false);
    let recordingTime: number = curTime - this.startTimeSeconds - this.uselessSeconds;
    AppStorage.setOrCreate('recordingTime', recordingTime);
    HiLog.d(TAG, `getCurRecordingTime curTime: ${curTime}, startTimeSeconds: ${
      this.startTimeSeconds}, uselessSeconds: ${this.uselessSeconds}, recordingTime: ${
      recordingTime}, preRecordTime: ${this.preRecordDuringTime}.`);
    recordingTime = Math.floor((recordingTime > 0 ? recordingTime : 0) / 1000) + this.preRecordDuringTime;
    return recordingTime;
  }

  private async getCurIntervalTime(): Promise<number> {
    let curTime: number = await systemDateTime.getRealTime(false);
    let recordingTime: number = curTime - this.startTimeSeconds - this.uselessSeconds;
    HiLog.d(TAG, `getCurRecordingTime curTime: ${curTime}, startTimeSeconds: ${this.startTimeSeconds}, uselessSeconds: ${
      this.uselessSeconds}, recordingTime: ${recordingTime}.`);
    let intervalTime = Math.floor(recordingTime / VideoTimer.timelapseInterval / 30);
    HiLog.i(TAG, `getCurIntervalTime is ${intervalTime}.`);
    return intervalTime > 0 ? intervalTime : 0;
  }

  private getTimerString(): string {
    let sec = this.recordingTime % TIME_SYSTEM;
    let minute = Math.floor(this.recordingTime % (TIME_SYSTEM * TIME_SYSTEM) / TIME_SYSTEM);
    let currentTime = `${minute < 10 ? `0${minute}` : `${minute}`}:${sec < 10 ? `0${sec}` : `${sec}`}`;
    if (this.recordingTime >= TIME_SYSTEM * TIME_SYSTEM) {
      let minute = Math.floor(this.recordingTime / (TIME_SYSTEM * TIME_SYSTEM));
      currentTime = `${minute < 10 ? `0${minute}` : `${minute}`}:${currentTime}`;
    }
    return currentTime;
  }

  private formatSecTimeLapse(): string {
    let shownSec = '00';
    let sec = Math.floor(this.intervalTime % TIME_SYSTEM);
    if (sec < 10) {
      shownSec = `0${sec}`;
    } else {
      shownSec = `${sec}`;
    }
    return shownSec;
  }

  private formatMinTimeLapse(): string {
    let shownMin = '00';
    let minute = Math.floor(this.intervalTime / TIME_SYSTEM);
    if (minute < 10) {
      shownMin = `0${minute}`;
    } else {
      shownMin = `${minute}`;
    }
    return shownMin;
  }

  private isVertical(inputDirection?: WindowDirection): boolean {
    if (inputDirection) {
      return inputDirection === WindowDirection.TOP || inputDirection === WindowDirection.BOTTOM;
    }
    return this.mDirection === WindowDirection.TOP || this.mDirection === WindowDirection.BOTTOM;
  }

  private getTopSpacing(): string {
    if (!this.mLemTopSpacingFlag) {
      return '0fp';
    }
    if (DirectionToAngleUtil.geViewRotate(this.mDirection) === 90 ||
      DirectionToAngleUtil.geViewRotate(this.mDirection) === 270) {
      return '30fp';
    } else {
      return '10fp';
    }
  }

  private directionChange() {
    HiLog.i(TAG, 'directionChange E.');
    if (this.isBigTimer || this.isShowSemiCollapsed || DeviceInfo.isTablet()) {
      this.videoTimerHeight = 80;
    } else if (this.isShowDefault) {
      this.videoTimerHeight = this.isVertical() && !this.isVdeCollapsed ? 30 : 80;
    }
    if (!this.isShowLandscape) {
      this.mPosition = {
        x: 0,
        y: this.isBigTimer && this.isVertical() ? this.videoTimerPosition : AdaptiveLayoutService.getInstance()
          .tabBarPosition + this.getVideoTimerOffset()
      };
    } else if (this.isShowSemiCollapsed) {
      this.mPosition = { x: 0, y: 0 };
    } else {
      this.mPosition = { x: 0, y: this.videoTimerPosition + 15 };
    }
    HiLog.i(TAG, 'directionChange X.');
  }

  private halfCollapsChange(): void {
    animateToImmediately({
      duration: 300,
      curve: Curve.Friction
    }, (): void => {
      if (this.isBigTimer) {
        this.mPosition = this.isHalfCollapsLEM ? { x: 0, y: AdaptiveLayoutService.getInstance().tabBarPosition } :
          { x: 0, y: this.videoTimerPosition }
      }
    });
  }

  private directionCorrection(inputDirection: WindowDirection, isLockRotation: boolean): void {
    this.mDirection = DirectionCorrectionService.directionCorrection(inputDirection, isLockRotation);
  }

  private isRingLightStatus(): boolean { // 录像计时是否展示环形补光色
    if (this.isShowDefault && this.isVertical() && !DeviceInfo.isTablet()) {
      return !this.isSmallVideoTimer ? this.isShowRingLightView : false;
    } else {
      return this.isShowRingLightView;
    }
  }
}