/*
 * 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 lazy { HiLog } from '@ohos/common/src/main/ets/utils/HiLog';
import lazy { RecordAction, RecordingState } from '@ohos/common/src/main/ets/function/recordcontrol/RecordAction';
import lazy { OhCombinedState, Unsubscribe, Dispatch, getStates, reduxSubscribe } from '@ohos/common/src/main/ets/redux';
import lazy { Action, UiStateMode } from '@ohos/common/src/main/ets/redux/actions/Action';
import lazy { FunctionId } from '@ohos/common/src/main/ets/function/core/functionproperty/FunctionId';
import lazy { FunctionAction } from '@ohos/common/src/main/ets/function/core/FunctionAction';
import lazy { FeatureManager } from '@ohos/common/src/main/ets/function/core/FeatureManager';
import lazy { SettingFuncDialogItemIndex } from '@ohos/common/src/main/ets/component/settingview/SettingFuncDialogItemIndex';
import lazy { ModeType } from '@ohos/common/src/main/ets/mode/ModeType';
import lazy { DeviceInfo } from '@ohos/common/src/main/ets/component/deviceinfo/DeviceInfo';
import lazy { DirectionToAngleUtil } from '@ohos/common/src/main/ets/utils/DirectionToAngleUtil';
import lazy { OutputOperation } from '@ohos/common/src/main/ets/function/outputswitcher/OutputOperation';
import lazy { CaptureAction } from '@ohos/common/src/main/ets/function/capture/CaptureAction';
import lazy { EventBusManager } from '@ohos/common/src/main/ets/worker/eventbus/EventBusManager';
import lazy { EventBus } from '@ohos/common/src/main/ets/worker/eventbus/EventBus';
import lazy { RecordMode } from '@ohos/common/src/main/ets/function/recordcontrol/RecordMode';
import lazy { TimeLapseAction } from './TimeLapseAction';
import lazy { ChangeState } from '@ohos/common/src/main/ets/redux/Store';
import lazy { BaseComponent } from '@ohos/common/src/main/ets/worker/BaseComponent';
import lazy { WindowEventData } from '@ohos/common/src/main/ets/utils/types';
import window from '@ohos.window';
import lazy { camera } from '@kit.CameraKit';
import lazy { WindowDirection } from '@ohos/common/src/main/ets/utils/WindowDirection';
import lazy { PickerUtils } from '@ohos/common/src/main/ets/utils/PickerUtils';
import lazy {
  DirectionCorrectionService } from '@ohos/common/src/main/ets/service/direction/DirectionCorrectionService';
import lazy { CommonConstants } from '@ohos/common/src/main/ets/statistics/CommonConstants';
import lazy { MIN_LOADING_MODE_DURATION } from '@ohos/common/src/main/ets/function/capture/ShutterButton';
import lazy { AuxiliaryMode } from '@ohos/common/src/main/ets/function/enumbase/AuxiliaryMode';
import lazy { execAction } from '@ohos/common/src/main/ets/redux/ActionRegistry';
import lazy { ActionType } from '@ohos/common/src/main/ets/redux/actions/ActionType';
import lazy { ContextActionType } from '@ohos/common/src/main/ets/redux/actions/ContextActionType';
import lazy { DisplayService } from '@ohos/common/src/main/ets/service/UIAdaptive/DisplayService';
import lazy { PcInfo } from '@ohos/common/src/main/ets/component/deviceinfo/PcInfo';
import lazy { AccessibilityUtils } from '@ohos/common/src/main/ets/utils/AccessibilityUtils';

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

export const TIME_LAPSE_LENGTH: number = 133;

const TIME_LAPSE_LENGTH_TABLET: number = 70;
const MODE_ROTATE_90: number = 90;
const MODE_ROTATE_270: number = 270;
const MODE_ROTATE_0: number = 0;
const PRO_SHUTTER_THRESHOLD: number = 1000000;

class StateStruct {
  public mode: ModeType = ModeType.NONE;
  public isShowSemiCollapsed: boolean = false;
}


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

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

  public changeTimeLapse(isShowtimeLapse: boolean, timeLapseInterrupt?: boolean): void {
    this.mDispatch(Action.changeTimeLapse(isShowtimeLapse, timeLapseInterrupt));
  }

  public capture(): void {
    this.mDispatch(CaptureAction.capture());
  }

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

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

  public changeTimelapseVideoState(): void {
    this.mDispatch(Action.changeTimelapseVideoState());
  }

  public startVideoAnim(): void {
    this.mDispatch(TimeLapseAction.startVideoAnim());
  }

  public emitSuperMotionTimeLapse(): void {
    this.mDispatch(execAction('MotionAction', 'emitSuperMotionTimeLapse'));
  }

  public startPanoramaShutterButton(): void {
    this.mDispatch(Action.startPanoramaShutterButton());
  }

  public startStitchingShutterButton(): void {
    this.mDispatch(Action.startStitchingShutterButton());
  }

  public countDownShutterAfterTimelapse(): void {
    this.mDispatch(Action.countDownShutterAfterTimelapse());
  }

  public readyForNextAutoCapture(): void {
    this.mDispatch(CaptureAction.isReadyForNextAutoCapture());
  }
}

@Component
export struct TimeLapseView {
  private mBase: BaseComponent = new BaseComponent();
  @State state: StateStruct = new StateStruct();
  @State timerLapse: number = 0;
  @State timerLapseShow: boolean = false;
  private timer: number = 0;
  private mFeatureManager: FeatureManager = FeatureManager.getInstance();
  private mAction: TimeLapseDispatcher = new TimeLapseDispatcher();
  private isPhone: boolean = DeviceInfo.isPhone();
  private timeLapseValue: number = SettingFuncDialogItemIndex.INDEX_NONE;
  @State mSideLength: number = this.isPhone ? TIME_LAPSE_LENGTH : TIME_LAPSE_LENGTH_TABLET;
  private mSubscriber: Unsubscribe | null = null;
  private mEventBus: EventBus = EventBusManager.getInstance().getEventBus();
  @State proShutterValue: number = 0;
  @State mDirection: WindowDirection = WindowDirection.TOP;
  @State isVdeCollapsed: boolean = false;
  @State rotateAngle: number = 0;
  @StorageLink('isNightShutting') isNightShutting: boolean = false;
  @State videoState: RecordingState = RecordingState.READY;
  @StorageLink('enableScreenReader') enableScreenReader: boolean = false;

  public aboutToAppear() {
    HiLog.d(TAG, 'aboutToAppear E.');
    this.mSubscriber = reduxSubscribe((state: OhCombinedState, changeState: ChangeState<StateStruct>) => {
      this.rotateAngle = state.get<number>('contextReducer', 'rotateAngle');
      this.isVdeCollapsed = state.get<boolean>('collapsReducer', 'isVdeCollapsed');
      this.videoState = state.get<RecordingState>('recordReducer', 'recordingState');
      this.mDirection = DirectionCorrectionService.directionCorrection(state.get<WindowDirection>('contextReducer', 'direction'),
        state.get<boolean>('windowReducer', 'isLockRotation'));
      changeState(this.state, {
        mode: state.get<ModeType>('modeReducer', 'mode'),
        isShowSemiCollapsed: state.get<boolean>('collapsReducer', 'isShowSemiCollapsed')
      });
    }, (dispatch: Dispatch): void => {
      this.mAction.setDispatch(dispatch);
    });
    this.mEventBus.on(ContextActionType.CHANGE_WINDOW_EVENT_TYPE, this.onChangeWindowEvent.bind(this),
      this.mBase.hashCode());
    this.timeLapseValue = this.mFeatureManager.getFunction(FunctionId.TIME_LAPSE).getValue() as number;
    switch (this.timeLapseValue) {
      case SettingFuncDialogItemIndex.INDEX_NONE:
      case SettingFuncDialogItemIndex.INDEX_FIR:
        this.timerLapse = -1;
        break;
      case SettingFuncDialogItemIndex.INDEX_SEC:
        this.timerLapse = 2;
        break;
      case SettingFuncDialogItemIndex.INDEX_THR:
        this.timerLapse = 5;
        break;
      case SettingFuncDialogItemIndex.INDEX_FOUR:
        this.timerLapse = 10;
        break;
      case SettingFuncDialogItemIndex.INDEX_FIF:
        this.timerLapse = 1.5;
        break;
      default:
        this.timerLapse = 0;
        break;
    }

    this.calculateTimerLapse();
    HiLog.d(TAG, 'aboutToAppear X.');
  }

  private onChangeWindowEvent(data: WindowEventData): void {
    if (data.windowStageEventType === window.WindowStageEventType.PAUSED ||
      data.windowStageEventType === window.WindowStageEventType.INACTIVE) {
      this.onStatePaused();
    }
  }

  aboutToDisappear(): void {
    HiLog.i(TAG, 'aboutToDisappear E.');
    this.mSubscriber?.destroy();
    this.clearInterval();
    this.mEventBus.clear(this.mBase.hashCode());
    HiLog.i(TAG, 'aboutToDisappear X.');
  }


  private calculateTimerLapse(): void {
    HiLog.i(TAG, `calculate timerLapse: ${this.timerLapse}.`);
    if (this.timerLapse > 0) {
      this.clearInterval();
      this.timerLapseShow = true;
      if (this.timeLapseValue !== SettingFuncDialogItemIndex.INDEX_FIF) {
        setTimeout((): void => {
          this.timerLapseShow = false;
        }, 400);
      }
      if (this.enableScreenReader && this.timerLapse > 0) {
        AccessibilityUtils.sendAnnounceAccessibilityEvent(this.timerLapse.toString());
      }
      this.timer = setInterval(async () => {
        if (this.timeLapseValue === SettingFuncDialogItemIndex.INDEX_FIF) {
          this.timerLapse -= 0.1;
        } else {
          this.timerLapse--;
        }
        this.timerLapseShow = true;
        if (this.timeLapseValue !== SettingFuncDialogItemIndex.INDEX_FIF) {
          setTimeout((): void => {
            this.timerLapseShow = false;
          }, 400);
        }
        if (this.timerLapse <= 0) {
          this.clearInterval();
          this.timerLapseShow = false;
          HiLog.i(TAG, `calculate mode: ${this.state.mode}.`);
          if (!this.state.mode || OutputOperation.isPanPhotoOutput(this.state.mode)) {
            if (!this.isProShutterCapture()) {
              this.mEventBus.emit(ActionType.ACTION_CAPTURE_EFFECT, []);
            }
            this.mAction.capture();
            this.mAction.changeFunctionValue(FunctionId.CAPTURE, true);
          } else if (OutputOperation.isPanVideoOutput(this.state.mode)) {
            this.mAction.startVideoAnim();
            this.mAction.startRecording();
            this.mAction.changeFunctionValue(FunctionId.RECORD_CONTROL, 0);
            this.mAction.changeTimelapseVideoState();
          }
          this.mAction.changeTimeLapse(false);
          return;
        }
        if (this.enableScreenReader && this.timerLapse > 0) {
          AccessibilityUtils.sendAnnounceAccessibilityEvent(this.timerLapse.toString());
        }
      }, this.timeLapseValue === SettingFuncDialogItemIndex.INDEX_FIF ? 100 : 1000);
    }
  }

  private clearInterval(): void {
    if (this.timer !== -1) {
      clearInterval(this.timer);
      this.timer = -1;
    }
  }

  private onStatePaused(): void {
    if (this.timerLapse >= 1) {
      this.clearInterval();
      this.mAction.changeTimeLapse(false, true);
      if (this.videoState === RecordingState.RECORDING || this.videoState === RecordingState.PAUSING ||
        this.videoState === RecordingState.PAUSED || this.videoState === RecordingState.RESUMING) {
        this.mAction.changeFunctionValue(FunctionId.RECORD_CONTROL, RecordMode.TO_STOP);
      }
    }
  }

  private isProShutterCapture(): boolean {
    if (this.proShutterValue > PRO_SHUTTER_THRESHOLD) {
      return true;
    }
    return false;
  }

  build() {
    Flex({
      direction: FlexDirection.Column,
      alignItems: ItemAlign.Center,
      justifyContent: this.state.isShowSemiCollapsed ? FlexAlign.Center : FlexAlign.Center
    }) {
      if (this.timerLapseShow) {
        Text(this.timeLapseValue === SettingFuncDialogItemIndex.INDEX_FIF ? this.timerLapse.toFixed(1) :
        this.timerLapse.toString())
          .fontSize((this.isPhone || DeviceInfo.isTablet()) ? 100 : $r('sys.float.Display_L'))
          .maxFontScale(CommonConstants.TEXT_SIZE_NORMAL)
          .fontColor(Color.White)
          .fontWeight(300)
          .textAlign(TextAlign.Center)
          .lineHeight(this.mSideLength)
          .textShadow({ radius: 3, color: '#33000000', offsetX: 0, offsetY: 0 })
          .rotate({ angle: this.getRotateAngle() })
          .scale({
            x: AppStorage.get('isPSDCollaps') ? 0.5 : 1,
            y: AppStorage.get('isPSDCollaps') ? 0.5 : 1
          })
          .fontFeature('\"ss01\" on')
          .key('TimerLapseView')
      }
    }.width('100%').height('100%')
  }

  private getRotateAngle(): number {
    const rotation = DisplayService.getInstance().getDisplay().rotation;
    const isRotatablePc: boolean = PcInfo.isRotatablePc();
    //TODO hopper的Picker与系统相机角度相差270度,后续重构旋转修改
    if (isRotatablePc) {
      return rotation === 1 ? MODE_ROTATE_270 : rotation === 3 ? MODE_ROTATE_90 : MODE_ROTATE_0
    }
    if (this.isVdeCollapsed) {
      return this.rotateAngle;
    }
    return this.state.isShowSemiCollapsed ?
      (DeviceInfo.isPc() ? 0 : MODE_ROTATE_90) : DirectionToAngleUtil.geViewRotate(this.mDirection);
  }
}