/*
 * 回放页面
 * EZOpenSDK
 *
 * Created by junecheng on 2024/4/14.
 * Copyright (c) 2024年 Ezviz. All rights reserved.
 */
import fs from '@ohos.file.fs'
import { photoAccessHelper } from '@kit.MediaLibraryKit'
import {
  EZCameraInfo,
  EZCloudRecordFile,
  EZCloudStreamDownload,
  EZDeviceInfo,
  EZDeviceRecordFile,
  EZDeviceStreamDownload,
  EZError,
  EZErrorCode,
  EZMessageCode,
  EZOpenSDK,
  EZPlaybackRate,
  EZPlayer,
  EZPlayerCallback,
  EZRecordCoverFetcherManager,
  EZStreamDownloadError,
  EZTimeUtil,
  EZVideoRecordLocation,
  EZVideoRecordType,
  EZVideoRecordTypeEx
} from '@ohos/EZOpenSDK'

import TimeUtil from '../../utils/EZTimeUtil'
import EZToastUtil from '../../utils/EZToastUtil'
import { PreferenceUtil } from '../../utils/PreferenceUtil'
import EZBusinessTool from '../../businesstool/EZBusinessTool'
import PermissionUtil from '../../utils/PermissionUtil'
import EZMediaFileUtil from '../../utils/EZMediaFileUtil'
import EZLog from '../../utils/EZLog'
import RouterManager from '../../global/RouterManager/RouterManager'
import DemoConfig from '../../global/DemoConfig'

import DeviceRecordFile from './Model/DeviceRecordFile'

import EZPlaybackNavView from './view/PlaybackNavView'
import PlaybackSlider from './view/PlaybackSlider'
import PlaybackToolBar from './view/PlaybackToolBar'
import PlaybackAdvertView from './view/PlaybackAdvertView'
import PlaybackRecordToolView, { EZRecordCategory } from './view/PlaybackRecordToolView'
import PlaybackGridView from './view/PlaybackGridView'
import SheetDialog from '../../global/view/SheetDialog'
import VerifyCodeDialog from '../../global/view/VerifyCodeDialog'
import ChoosePlayRateView from './view/ChoosePlayRateView'
import CloudRecordFile from './Model/CloudRecordFile'
import { RecordDownloadStatus } from './Model/RecordDownloadStatus'
import GlobalKit from '../../global/GlobalKit'

@Entry
@Component
struct EZPlaybackPage {
  private readonly TAG: string = `${EZPlaybackPage.name}:`
  /// ------------- 以下为设备相关信息 -------------
  private deviceInfo: EZDeviceInfo | null = null
  private cameraInfo: EZCameraInfo | null = null
  private cameraIndex: number = 0
  // 是否是双目设备，如果接入的设备没有双目设备，可以忽略相关逻辑
  @State isMultiChannelDevice: boolean = false
  /// ------------- 以上为设备相关信息 -------------

  /// ------------- 以下为回放播放器区域相关 -------------
  xComponentController: XComponentController = new XComponentController()
  xComponentController2: XComponentController = new XComponentController()
  playbackPlayerComponentID: string = 'PlaybackPlayerComponentID'
  playbackPlayerComponentID2: string = 'PlaybackPlayerComponentID2'
  // 回放播放器
  player: EZPlayer | null = null
  // 播放器是否在播放中
  @State isPlaying: boolean = false
  // 取流加载视图
  @State isStreamLoadingShow: boolean = false
  // 播放错误信息
  @State errorMessage: ResourceStr = ''
  // 播放错误信息状态
  @State isErrorMessageShow: boolean = false
  // 取流方式
  @State streamFetchType: string = ''
  // 点击选中的SD卡视频
  deviceRecord: EZDeviceRecordFile | null = null
  // 点击选中的云存储视频
  cloudRecord: EZCloudRecordFile | null = null
  // 是否有选中某一个item
  selectedItem: boolean = false
  // 当前选中的录像索引
  @State selectedRecordIndex: number = 0
  /// ------------- 以上为回放播放器区域相关 -------------

  /// ------------- 以下为回放进度条区域相关 -------------
  // 播放秒数
  playSeconds: number = 0
  // 录像时长，录像总秒数
  duringSeconds: number = 0
  // 录像播放时长
  @State playTime: string = ''
  // 录像总时长
  @State duringTime: string = ''
  // 回放进度条value，[0， 100]
  @State sliderValue: number = 0
  // 进度条定时器ID，刷新回放进度
  sliderTimerIntervalID: number = 0
  // 回放进度条区域是否可见
  @State isSliderAreaShow: boolean = false
  /// ------------- 以上为回放进度条区域相关 -------------

  /// ------------- 以下为回放播放器工具栏相关 -------------
  // 播放器声音是否开启，默认开启
  @State isOpenSound: boolean = true
  // 是否支持下载
  @State isSupportDownload: boolean = true
  // 是否支持录像类型（导航栏右上角选择）
  @State isSupportRecordType: boolean = false
  //当前选中的播放速度
  // @State currentSelectRate: string = `1x`
  // 是否显示播放速度弹窗
  @State isPlayRateViewShow: boolean = false
  // 回放播放器工具栏是否可见
  @State isToolBarAreaShow: boolean = false
  /// ------------- 以上为回放播放器工具栏相关 -------------

  /// ------------- 以下为录像查询相关 -------------
  // 录像查询开始时间
  private endTime: Date = new Date()
  // 录像查询结束时间
  private beginTime: Date = new Date()
  // 当前选中的日期
  @State selectedDate: Date = new Date()
  // 导航栏日期文本
  @State dateTitle: string = ''
  // Refresh组件是否刷新中
  @State isRefreshing: boolean = false
  // 当前选中的录像类别（云存储、SD卡）
  @State recordCategory: EZRecordCategory = EZRecordCategory.EZRecordCategoryCloud
  // 录像类型，仅仅支持国内（所有录像、定时录像、事件录像）
  @State recordType: EZVideoRecordType = EZVideoRecordType.EZVideoRecordTypeAll
  // 录像数组
  playbackRecords: Array<EZCloudRecordFile | EZDeviceRecordFile> | undefined = []
  /**
   * 转换后的录像数组
   * 问题：为什么需要单独创建一个DeviceRecordFile.ets类，将EZDeviceRecordFile转换成DeviceRecordFile
   * 答：
   * SD卡录像封面请求时，封面图是一张一张返回的，需要对SD卡录像对象进行监听，接收到一张封面就要进行局部刷新，
   * 因此需要对PlaybackGridView组件中的deviceRecordFile属性使用@ObjectLink注解进行监听
   * 而DeviceRecordFile是ts类，无法使用@ObjectLink注解，所以需要自己创建一个DeviceRecordFile.ets类进行转换处理
   * CloudRecordFile.ets类同理，需要刷新录像下载进度
   */
  @State playbackConvertRecords: Array<CloudRecordFile | DeviceRecordFile> | undefined = []

  /// ------------- 以上为录像查询相关 -------------

  // ------------------------ 生命周期 ------------------------

  aboutToAppear() {
    this.initData()// 数据初始化
    this.initEZPlayer()// 播放器初始化

    // SD卡本地录像获取初始化
    // 国内支持SD卡录像封面获取，海外不支持
    // 与设备建立链接，获取SD卡录像封面（页面退出的时候必须断开链接，释放资源，见-dealloc方法）
    EZRecordCoverFetcherManager.getInstance().initFetcher(this.cameraInfo!.deviceSerial, this.cameraInfo!.cameraNo, {
      onFetcherInitSuccess: (): void => {
        EZLog.debug(this.TAG, 'EZRecordCoverFetcherManager onFetcherInitSuccess')
      },
      onFetcherInitFailed: (): void => {
        EZLog.error(this.TAG, 'EZRecordCoverFetcherManager onFetcherInitFailed')
      }
    })

    // 默认选中云录像
    this.recordCategoryTabAction(EZRecordCategory.EZRecordCategoryCloud)
  }

  async aboutToDisappear() {
    clearInterval(this.sliderTimerIntervalID)
    await EZOpenSDK.releasePlayer(this.player)
    EZRecordCoverFetcherManager.getInstance().stopFetcher()
  }

  async onPageShow(): Promise<void> {
    // 返回前台或者从其他页面跳转回来后，重新开始取流
    await this.playerStartPlayback()
  }

  async onPageHide(): Promise<void> {
    // 退到后台或者跳转到其他页面后，停止取流
    await this.playerStopPlayback()
  }

  // ------------------------ 回放播放器回调函数 player callback ------------------------

  private playerCallback: EZPlayerCallback = {
    /*
     * 播放器播放失败消息回调
     */
    didPlayFailed: (error: EZError) => {
      this.isStreamLoadingShow = false
      if (error.code == EZErrorCode.EZ_SDK_NEED_VALIDATECODE) {
        this.showSetPassword()
      } else if (error.code == EZErrorCode.EZ_SDK_VALIDATECODE_NOT_MATCH) {
        this.showRetry()
      } else {
        EZToastUtil.showToast(`error code is ${error.code}`)
        this.errorMessage = $r('app.string.device_play_fail', `${error.code}`)
        this.isErrorMessageShow = true
        this.isPlaying = false
      }
    },
    /*
     * 播放器播放成功消息回调
     */
    didReceivedMessage: async (messageCode: number) => {
      EZLog.info(this.TAG, `player didReceivedMessage messageCode: ${messageCode}`)
      this.isStreamLoadingShow = false
      if (messageCode == EZMessageCode.PLAYER_PLAYBACK_START) { // 录像回放开始|seek成功
        await this.handlePlaybackStart()
      } else if (messageCode == EZMessageCode.PLAYER_PLAYBACK_STOP) { // 录像回放完成，回放完成后回调
        await this.handlePlaybackFinish()
      }
    }

  }

  /** 录像回放开始，更新UI */
  async handlePlaybackStart() {
    this.isPlaying = true
    this.isSliderAreaShow = true
    this.isToolBarAreaShow = true
    this.isErrorMessageShow = false
    if (!this.isOpenSound) {
      await this.player?.closeSound()
    }
    clearInterval(this.sliderTimerIntervalID)
    await this.playBoxToolRefresh()
    this.sliderTimerIntervalID = setInterval(async () => {
      await this.playBoxToolRefresh()
    }, 1000)
    await this.showStreamFetchType()
  }

  /** 执行播放完毕的操作：如停止player、更新界面 */
  async handlePlaybackFinish() {
    clearInterval(this.sliderTimerIntervalID)
    await this.player?.stopPlayback() // 停止回放，必须
    this.isPlaying = false
    this.isSliderAreaShow = false // 播放完毕隐藏进度条视图（当前片段播放时长、进度条、总时长）
    this.isToolBarAreaShow = false // 播放完毕隐藏工具栏视图（播放/暂停、声音开关、下载、倍数、全屏）
  }

  // ------------------------ Actions ------------------------

  /** 暂停/播放Action */
  playAction = async () => {
    if (this.isPlaying) {
      await this.player?.pausePlayback()
      if (this.sliderTimerIntervalID > 0) {
        clearInterval(this.sliderTimerIntervalID)
      }
    } else {
      if (!this.cloudRecord && !this.deviceRecord) {
        return
      }
      await this.player?.resumePlayback()
      this.isErrorMessageShow = false
      this.isStreamLoadingShow = true
    }
    this.isPlaying = !this.isPlaying
  }
  /** 点击声音按钮 */
  voiceAction = async () => {
    if (this.isOpenSound) {
      await this.player?.closeSound()
    } else {
      await this.player?.openSound()
    }
    this.isOpenSound = !this.isOpenSound
  }
  /** 点击下载按钮 */
  downloadAction = async () => {
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      if (this.deviceRecord) {
        await this.startDownloadDeviceVideo(this.deviceRecord)
      } else {
        EZLog.error(this.TAG, 'startDownloadDeviceVideo error: deviceFile is null')
      }
    } else {
      if (this.cloudRecord) {
        await this.startDownloadCloudVideo(this.cloudRecord)
      } else {
        EZLog.error(this.TAG, 'startDownloadCloudVideo error: cloudRecord is null')
      }
    }
  }
  /** 点击倍数按钮 */
  rateAction = () => {
    this.isPlayRateViewShow = !this.isPlayRateViewShow
  }
  /** 选中倍数按钮 */
  selectRateAction = async (playbackRate: EZPlaybackRate, playbackRateStr: string) => {
    this.isPlayRateViewShow = !this.isPlayRateViewShow
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      let isSupportDirectInnerRelaySpeed =
        EZBusinessTool.isSupportDirectInnerRelaySpeed(this.deviceInfo, this.cameraInfo)
      let isSupportPlaybackRate = EZBusinessTool.isSupportPlaybackRate(this.deviceInfo, this.cameraInfo)
      // 当前是内网直连 && 设备不支持内网直连时倍数设置
      if (await this.player?.getStreamFetchType() == 2 && !isSupportDirectInnerRelaySpeed) {
        EZToastUtil.showToast($r('app.string.device_directinner_playbackrate_not_support'))
      } else if (!isSupportPlaybackRate) {
        EZToastUtil.showToast($r('app.string.device_playbackrate_not_support'))
      } else {
        // 倍率回放可能设置失败，设备可能无法响应或者不支持该倍数
        let ret = await this.player?.setPlaybackRate(playbackRate, 0)
        EZToastUtil.showToast(`切换为${playbackRateStr}速${ret ? '成功' : '失败'}`)
      }
    } else {
      let ret = await this.player?.setPlaybackRate(playbackRate, 0)
      EZToastUtil.showToast(`切换为${playbackRateStr}速${ret ? '成功' : '失败'}`)
    }
  }
  /** 竖屏->横屏Action */
  largeAction = () => {
    EZToastUtil.showToast('暂未实现')
  }
  /** 播放进程条拖动 */
  duringValueChange = async (value: number) => {
    if (!this.isPlaying) { // 不在播放中，拦截
      return
    }
    if (value == 100) {
      await this.handlePlaybackFinish()
      return
    }
    let secondsAfterStartTime = this.duringSeconds * (value / 100)

    let offsetTime: Date | null = null
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      if (this.deviceRecord) {
        offsetTime = new Date(this.deviceRecord.startTime.getTime() + secondsAfterStartTime * 1000)
      }
    } else {
      if (this.cloudRecord) {
        offsetTime = new Date(this.cloudRecord.startTime.getTime() + secondsAfterStartTime * 1000)
      }
    }
    if (!offsetTime) {
      return
    }
    clearInterval(this.sliderTimerIntervalID)
    await this.player?.seekPlayback(offsetTime)
    this.isStreamLoadingShow = true
  }
  /** 录像类型选择Action */
  onChangeRecordType = () => {
    this.changeRecordTypeDialogController.open()
  }
  /** 录像类型选择弹窗 */
  changeRecordTypeDialogController: CustomDialogController = new CustomDialogController({
    customStyle: true,
    alignment: DialogAlignment.Bottom,
    builder: SheetDialog({
      headerTitle: '选择录像类型',
      titles: ['所有类型', '定时录像', '事件类型'],
      selectIndex: this.recordType,
      confirmClick: (index) => {
        this.recordType = index
        this.isRefreshing = true
      }
    }),
    autoCancel: true,
    offset: { dx: 0, dy: -25 }
  })
  /** 录像类型Tab切换Action */
  recordCategoryTabAction = (recordCategory: EZRecordCategory) => {
    this.recordCategory = recordCategory
    // 下载按钮状态
    if (recordCategory == EZRecordCategory.EZRecordCategoryCloud) {
      this.isSupportDownload = true
      this.isSupportRecordType = false
    } else {
      this.isSupportDownload = EZBusinessTool.isSupportSDRecordDownload(this.deviceInfo, this.cameraInfo)
      this.isSupportRecordType = true
    }
    // Tab切换后停止定时器，否则playBoxToolRefresh方法会崩溃
    clearInterval(this.sliderTimerIntervalID)
    // 请求录像数据
    this.isRefreshing = true
  }
  /** 选择日期Action */
  dateChangeAction = () => {
    DatePickerDialog.show({
      start: new Date('2000-1-1'),
      end: new Date('2100-12-31'),
      selected: this.selectedDate,
      onAccept: (value: DatePickerResult) => {
        // 通过Date的setFullYear方法设置按下确定按钮时的日期，这样当弹窗再次弹出时显示选中的是上一次确定的日期
        this.selectedDate.setFullYear(value.year, value.month, value.day)
        this.changeDate(this.selectedDate)
      }
    })
  }
  /** 录像数据刷新 */
  recordsRefreshAction = () => {
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      EZOpenSDK.searchRecordFileFromDevice(this.cameraInfo?.deviceSerial!!, this.cameraInfo?.cameraNo!!,
        this.beginTime, this.endTime, this.recordType, async (records, error) => {
          await this.refreshRecordsUI(records, error)
        })
    } else {
      EZOpenSDK.searchRecordFileFromCloud(this.cameraInfo?.deviceSerial!!, this.cameraInfo?.cameraNo!!,
        this.beginTime, this.endTime, async (records, error) => {
          await this.refreshRecordsUI(records, error)
        })
    }
  }

  /** 获取到录像列表数据后再刷新 */
  async refreshRecordsUI(records: Array<EZCloudRecordFile | EZDeviceRecordFile>, error: EZError) {
    this.isRefreshing = false
    this.selectedRecordIndex = 0
    if (error) {
      EZToastUtil.showToast(`error code is ${error.code}`)
      clearInterval(this.sliderTimerIntervalID)
      this.playbackRecords = []
      this.playbackConvertRecords = []
      return
    }
    if (records) {
      records.reverse()
    }
    this.playbackRecords = records
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      this.playbackConvertRecords = DeviceRecordFile.convert2DeviceRecordFileArray(records as Array<EZDeviceRecordFile>)
    } else {
      this.playbackConvertRecords = CloudRecordFile.convert2CloudRecordFileArray(records as Array<EZCloudRecordFile>)
    }
    if (records.length > 0) {
      await this.doPlayback()
    } else {
      this.isStreamLoadingShow = false
    }
    // 如果是SD卡录像，请求录像封面
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      await this.requestDeviceRecordCover()
    }
  }

  /**
   * 请求录像封面
   */
  async requestDeviceRecordCover() {
    if (!this.playbackRecords) {
      return
    }
    for (let i = 0; i < this.playbackRecords.length; i++) {
      let recordFile = this.playbackRecords[i] as EZDeviceRecordFile
      recordFile.seq = i // 设置索引，封面回调的时候知道对应哪一个录像
    }
    // 去获取SD卡视频封面
    let isSupportSdCover = EZBusinessTool.isSupportSdCover(this.deviceInfo, this.cameraInfo)
    if (isSupportSdCover) {
      await EZRecordCoverFetcherManager.getInstance()
        .requestRecordCover(this.playbackRecords as Array<EZDeviceRecordFile>, {
          onGetCoverSuccess: async (seq: number, data: Uint8Array): Promise<void> => {
            /**
             * 注意：图片是设备一张一张传回来的，接收到一张就需要局部刷新UI。
             */
            let recordFile = this.playbackConvertRecords![seq] as DeviceRecordFile
            recordFile.pixelMap = await EZBusinessTool.convertToPixelMap(data)
          },
          onGetCoverFailed: (errorCode: number): void => {
            EZLog.error(this.TAG, 'onGetCoverFailed')
          }
        })
    }
  }

  /** 录像点击 */
  playbackItemAction = async (mPosition: number, recordFile: CloudRecordFile | DeviceRecordFile) => {
    if (!this.selectedItem) {
      this.selectedItem = true
      setTimeout(() => {
        this.selectedItem = false
      }, 1000)

      if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
        this.deviceRecord = this.playbackRecords![mPosition] as EZDeviceRecordFile
      } else {
        this.cloudRecord = this.playbackRecords![mPosition] as EZCloudRecordFile
      }

      clearInterval(this.sliderTimerIntervalID)
      await this.startPlaybackByRecordCategory()
      this.setPlaybackSliderUI()

      this.selectedRecordIndex = mPosition
      this.isErrorMessageShow = false
      this.isStreamLoadingShow = true
    }
  }

  /** 云存储图片点击Action */
  advertImageClickAction = () => {

  }

  // ------------------------ ValidateCode Methods 验证码检验 ------------------------

  /*
   * 显示输入密码alert
   */
  showSetPassword() {
    this.verifyCodeDialogController.open()
  }

  /*
   * 显示输入密码alert
   */
  showRetry() {
    this.verifyCodeDialogController.open()
  }

  @State verifyCodeDialogInputContent: string = ''
  verifyCodeDialogController: CustomDialogController = new CustomDialogController({
    customStyle: true,
    builder: VerifyCodeDialog({
      deviceSerial: this.cameraInfo?.deviceSerial,
      title: $r('app.string.setting_device_security_check'),
      message: $r('app.string.setting_input_code_plz'),
      placeholder: $r('app.string.device_input_vierify_code'),
      inputContent: this.verifyCodeDialogInputContent,
      confirmClick: async () => {
        // 输入密码后，开启回放
        this.player?.setPlayVerifyCode(this.verifyCodeDialogInputContent)
        await this.playerStartPlayback()
      },
      cancelClick: () => {

      }
    }),
    alignment: DialogAlignment.Center,
    autoCancel: false
  })

  // ------------------------ 自定义方法 ------------------------

  initEZPlayer() {
    // 1.创建回放播放器
    this.player = EZOpenSDK.createPlayer(this.cameraInfo!.deviceSerial, this.cameraInfo!.cameraNo)
    // 2.配置播放器：设置回调，设置验证码
    this.player.setPlayerCallback(this.playerCallback)
    if (this.deviceInfo!.isEncrypt) {
      let verifyCode: string = PreferenceUtil.getString(getContext(this), this.cameraInfo!.deviceSerial) as string
      this.player.setPlayVerifyCode(verifyCode)
    }
  }

  /** 播放器开始播放 */
  async playerStartPlayback() {
    this.isStreamLoadingShow = true
    await this.startPlaybackByRecordCategory()
  }

  /** 播放器停止播放 */
  async playerStopPlayback() {
    clearInterval(this.sliderTimerIntervalID)
    await this.player?.stopPlayback()
  }

  /**
   * 改变日期
   * @param date            日期
   * @param isRefreshData   是否刷新录像列表数据
   */
  changeDate(date: Date, isRefreshData: boolean = true) {
    // 设置选中时间
    this.selectedDate = date
    // 获取开始结束时间
    let startAndEndOfDay = EZTimeUtil.getStartAndEndOfDay(this.selectedDate)
    this.beginTime = startAndEndOfDay.startOfDay
    this.endTime = startAndEndOfDay.endOfDay
    // 更新UI
    this.dateTitle = EZTimeUtil.dateFormat(this.selectedDate, 'MM-dd')
    clearInterval(this.sliderTimerIntervalID)
    if (isRefreshData) {
      this.isRefreshing = true
    }
  }

  /** 根据录像类别开始回放 */
  async startPlaybackByRecordCategory() {
    await this.player?.stopPlayback()
    if (EZOpenSDK.isEnableSDKWithTKToken()) {
      this.player?.setStreamToken(GlobalKit.getInstance().playbackStreamToken)
    }
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice && this.deviceRecord) {
      await this.player?.startPlaybackFromDevice(this.deviceRecord)
    } else if (this.recordCategory == EZRecordCategory.EZRecordCategoryCloud && this.cloudRecord) {
      await this.player?.startPlaybackFromCloud(this.cloudRecord)
    } else {
      this.isStreamLoadingShow = false
    }
  }

  /** 开始回放 */
  async doPlayback() {
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      this.deviceRecord = this.playbackRecords![this.selectedRecordIndex] as EZDeviceRecordFile
    } else {
      this.cloudRecord = this.playbackRecords![this.selectedRecordIndex] as EZCloudRecordFile
    }
    await this.startPlaybackByRecordCategory()
    this.setPlaybackSliderUI()
    this.isStreamLoadingShow = true
  }

  /** 录像播放进度UI初始化（已播放时长、总时长、Slider进度） */
  setPlaybackSliderUI() {
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice) {
      this.duringSeconds = EZTimeUtil.compareDates(this.deviceRecord?.stopTime, this.deviceRecord?.startTime)
    } else {
      this.duringSeconds = EZTimeUtil.compareDates(this.cloudRecord?.stopTime, this.cloudRecord?.startTime)
    }
    this.duringTime = TimeUtil.formatSeconds(this.duringSeconds)
    this.playTime = this.duringSeconds >= 3600 ? '00:00:00' : '00:00'
    this.sliderValue = 0
  }

  /** 刷新播放进度 */
  async playBoxToolRefresh() {
    let currentTime = await this.player?.getOSDTime()
    if (!currentTime) {
      return
    }
    // EZLog.debug(this.TAG, `currentTime: ${EZTimeUtil.dateFormat(currentTime)} OSDTime: ${currentTime}`)
    if (this.recordCategory == EZRecordCategory.EZRecordCategoryDevice && this.deviceRecord) {
      // EZLog.debug(this.TAG, `deviceRecord.startTime: ${EZTimeUtil.dateFormat(this.deviceRecord.startTime)}`)
      this.playSeconds = EZTimeUtil.compareDates(currentTime, this.deviceRecord.startTime)
    } else if (this.recordCategory == EZRecordCategory.EZRecordCategoryCloud && this.cloudRecord) {
      // EZLog.debug(this.TAG, `cloudRecord.startTime: ${EZTimeUtil.dateFormat(this.cloudRecord.startTime)}`)
      this.playSeconds = EZTimeUtil.compareDates(currentTime, this.cloudRecord.startTime)
    }
    // EZLog.debug(this.TAG, `playSeconds: ${this.playSeconds}`)

    // 播放时长 < 0 ||  > 录像总时长，置0处理
    if (this.playSeconds < 0 || this.playSeconds > this.duringSeconds) {
      this.playSeconds = 0
    }
    this.playTime = TimeUtil.formatSeconds(this.playSeconds)
    this.sliderValue = (this.playSeconds / this.duringSeconds) * 100
  }

  /** 显示取流方式 */
  async showStreamFetchType() {
    let type = await this.player?.getStreamFetchType() as number
    if (type >= 0) {
      this.streamFetchType = EZBusinessTool.getStreamType(type)
    }
  }

  initData() {
    const routerParams = RouterManager.getParams() as Record<string, object>
    this.deviceInfo = routerParams.deviceInfo as EZDeviceInfo
    this.cameraIndex = routerParams.cameraIndex ? parseInt(`${routerParams.cameraIndex}`) : 0
    let array = this.deviceInfo.cameraInfo.length > 0 ? this.deviceInfo.cameraInfo : this.deviceInfo.subDeviceInfo
    this.cameraInfo = array[this.cameraIndex]
    this.isMultiChannelDevice = EZBusinessTool.isSupportMultiChannel(this.deviceInfo, this.cameraInfo)

    this.changeDate(new Date(), false)
  }

  /**
   * 云存储录像下载 download video from ezviz cloud
   * @param cloudFile
   */
  async startDownloadCloudVideo(cloudFile: EZCloudRecordFile) {
    if (!cloudFile) {
      EZLog.error(this.TAG, 'startDownloadCloudVideo error: deviceFile is null')
      return
    }
    if (!this.cameraInfo) {
      EZLog.error(this.TAG, 'startDownloadCloudVideo error: cameraInfo is null')
      return
    }
    const index = this.playbackRecords?.indexOf(cloudFile)
    let convertCloudFile = this.playbackConvertRecords![index!] as CloudRecordFile
    if (convertCloudFile.isRecordDownloading()) {
      EZToastUtil.showToast($r('app.string.playback_record_download_downloading'))
      return
    }
    // 1、录像文件下载路径
    const strRecordFilePath = `${DemoConfig.PATH_CloudRecord}/cloud_${Date.now()}.mp4`
    // 获取文件的父目录路径
    const parentDirPath = strRecordFilePath.substring(0, strRecordFilePath.lastIndexOf('/'))
    // 父级文件夹创建
    if (!fs.accessSync(parentDirPath)) {
      await fs.mkdir(parentDirPath)
      EZLog.info(this.TAG, `startDownloadCloudVideo Directory created successfully(${parentDirPath})`)
    }
    // 2、创建下载器
    const ezCloudStreamDownload = new EZCloudStreamDownload()
    await ezCloudStreamDownload.prepareDownloadParams(strRecordFilePath, cloudFile)
    if (this.isMultiChannelDevice) {
      ezCloudStreamDownload.setMultiChannelDevice(true)
    }
    // 3、设置验证码
    let verifyCode = PreferenceUtil.getString(getContext(this), this.cameraInfo.deviceSerial) as string
    ezCloudStreamDownload.setSecretKey(verifyCode)
    // 4、设置下载监听
    ezCloudStreamDownload.setStreamDownloadCallback({
      onSuccess: async (filepath: string): Promise<void> => {
        EZToastUtil.showToast($r('app.string.playback_record_download_success'))
        convertCloudFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Success
        convertCloudFile.downloadPercent = 100
        let r = await this.requestWriteImageVideoPermission()
        if (!r) {
          EZLog.error(this.TAG, `startDownloadCloudVideo`, 'not apply WRITE_IMAGEVIDEO Permission')
          return
        }
        await EZMediaFileUtil.saveMediaToAlbum(filepath, photoAccessHelper.PhotoType.VIDEO)
        await fs.unlink(filepath)
        if (this.isMultiChannelDevice) {
          let index = filepath.lastIndexOf('.')
          filepath = filepath.substring(0, index) + '_1' + filepath.substring(index)
          await EZMediaFileUtil.saveMediaToAlbum(filepath, photoAccessHelper.PhotoType.VIDEO)
          await fs.unlink(filepath)
        }
      },
      onError: (error: EZStreamDownloadError): void => {
        EZLog.error(this.TAG, `startDownloadCloudVideo onError-> error:${error.toString()}`)
        // EZToastUtil.showToast($r('app.string.playback_record_download_failed'))
        convertCloudFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Error
        convertCloudFile.downloadPercent = 0
      },
      onErrorCode: (code: number): void => {
        EZLog.error(this.TAG, `startDownloadCloudVideo onErrorCode-> ${code}`)
        let resString = getContext().resourceManager.getStringSync($r('app.string.playback_record_download_failed'))
        EZToastUtil.showToast(`${resString} error code: ${code}`)
        convertCloudFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Error
        convertCloudFile.downloadPercent = 0
      },
      onDownloadingSize: (downloadSize: number): void => {
        convertCloudFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Downloading
        let downloadPercent = Math.floor(downloadSize * 100 / cloudFile.fileSize)
        EZLog.info(this.TAG, `cloud download percent ---> ${downloadPercent}`)
        convertCloudFile.downloadPercent = downloadPercent
      }
    })
    // 5、开始下载
    EZToastUtil.showToast($r('app.string.playback_record_download_start'))
    convertCloudFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Start
    await ezCloudStreamDownload.start()
  }

  /**
   * SD卡录像下载 download video from ezviz device
   * @param deviceFile
   */
  async startDownloadDeviceVideo(deviceFile: EZDeviceRecordFile) {
    if (!deviceFile) {
      EZLog.error(this.TAG, 'startDownloadDeviceVideo error: deviceFile is null')
      return
    }
    if (!this.cameraInfo) {
      EZLog.error(this.TAG, 'startDownloadDeviceVideo error: cameraInfo is null')
      return
    }
    const index = this.playbackRecords?.indexOf(deviceFile)
    let convertDeviceFile = this.playbackConvertRecords![index!] as DeviceRecordFile
    if (convertDeviceFile.isRecordDownloading()) {
      EZToastUtil.showToast($r('app.string.playback_record_download_downloading'))
      return
    }
    // 1、录像文件下载路径
    const strRecordFilePath = `${DemoConfig.PATH_DeviceRecord}/device_${Date.now()}.mp4`
    // 获取文件的父目录路径
    const parentDirPath = strRecordFilePath.substring(0, strRecordFilePath.lastIndexOf('/'))
    // 父级文件夹创建
    if (!fs.accessSync(parentDirPath)) {
      await fs.mkdir(parentDirPath)
      EZLog.info(this.TAG, `startDownloadDeviceVideo Directory created successfully(${parentDirPath})`)
    }
    // 2、创建下载器
    const ezDeviceStreamDownload = new EZDeviceStreamDownload()
    await ezDeviceStreamDownload.prepareDownloadParams(strRecordFilePath, this.cameraInfo.deviceSerial,
      this.cameraInfo.cameraNo, deviceFile)
    if (EZOpenSDK.isEnableSDKWithTKToken()) {
      ezDeviceStreamDownload.setStreamToken(GlobalKit.getInstance().playbackStreamToken)
    }
    if (this.isMultiChannelDevice) {
      ezDeviceStreamDownload.setMultiChannelDevice(true)
    }
    // 3、设置验证码
    let verifyCode = PreferenceUtil.getString(getContext(this), this.cameraInfo.deviceSerial) as string
    ezDeviceStreamDownload.setSecretKey(verifyCode)
    // 4、设置下载监听，SD卡录像下载不支持onDownloadingSize下载进度
    ezDeviceStreamDownload.setStreamDownloadCallback({
      onSuccess: async (filepath: string): Promise<void> => {
        EZToastUtil.showToast($r('app.string.playback_record_download_success'))
        convertDeviceFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Success
        let r = await this.requestWriteImageVideoPermission()
        if (!r) {
          EZLog.error(this.TAG, `startDownloadCloudVideo`, 'not apply WRITE_IMAGEVIDEO Permission')
          return
        }
        await EZMediaFileUtil.saveMediaToAlbum(filepath, photoAccessHelper.PhotoType.VIDEO)
        await fs.unlink(filepath)
        if (this.isMultiChannelDevice) {
          let index = filepath.lastIndexOf('.')
          filepath = filepath.substring(0, index) + '_1' + filepath.substring(index)
          await EZMediaFileUtil.saveMediaToAlbum(filepath, photoAccessHelper.PhotoType.VIDEO)
          await fs.unlink(filepath)
        }
      },
      onError: (error: EZStreamDownloadError): void => {
        EZLog.error(this.TAG, `startDownloadDeviceVideo onError-> error:${error.toString()}`)
        EZToastUtil.showToast($r('app.string.playback_record_download_failed'))
        convertDeviceFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Error
      },
      onErrorCode: (code: number): void => {
        EZLog.error(this.TAG, `startDownloadDeviceVideo onErrorCode-> code:${code}`)
        let resString = getContext().resourceManager.getStringSync($r('app.string.playback_record_download_failed'))
        EZToastUtil.showToast(`${resString} error code: ${code}`)
        convertDeviceFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Error
      }
    })
    // 5、开始下载
    EZToastUtil.showToast($r('app.string.playback_record_download_start'))
    convertDeviceFile.recordDownloadStatus = RecordDownloadStatus.Record_Download_Start
    await ezDeviceStreamDownload.start()
  }

  // 申请相册写入权限
  async requestWriteImageVideoPermission(): Promise<boolean> {
    let p = await PermissionUtil.checkAndRequestPermission(getContext(), ['ohos.permission.WRITE_IMAGEVIDEO'])
    if (p.valueOf()) {
      // 用户授权，可以继续访问目标操作
      EZLog.info(this.TAG, `requestWriteImageVideoPermission`, 'Succeeded in getting permissions.')
    } else {
      EZLog.error(this.TAG, `requestWriteImageVideoPermission:获取相册写入权限失败`)
      PermissionUtil.showGuideToSettingsDialog(getContext(), "打开设置",
        '由于将录像下载到您的相册,请在设置页打开`图片与视频`权限后进行使用')
    }
    return p
  }

  // ------------------------ UI ------------------------

  build() {
    Flex({ direction: FlexDirection.Column }) {
      EZPlaybackNavView({
        title: this.dateTitle,
        isRightNavShow: this.isSupportRecordType,
        onDateClick: this.dateChangeAction,
        onRightNavClick: this.onChangeRecordType
      })
      RelativeContainer() {
        // 播放器视图
        this.PlaybackView()
        // 取流方式 / 解码方式
        Column() {
          Text(this.streamFetchType).fontSize(12).fontColor(Color.Orange)
        }.alignItems(HorizontalAlign.End).margin({ top: 7, right: 7 })
        .alignRules({
          top: { 'anchor': '__container__', align: VerticalAlign.Top },
          right: { 'anchor': '__container__', align: HorizontalAlign.End }
        })

        // 播放进度条
        PlaybackSlider({
          isSliderAreaShow: this.isSliderAreaShow,
          playTime: this.playTime,
          duringSeconds: this.duringSeconds,
          duringTime: this.duringTime,
          sliderValue: this.sliderValue,
          onSliderChange: this.duringValueChange
        })
        // 工具栏视图：开始/暂停、声音、下载、倍数
        PlaybackToolBar({
          isPlaying: this.isPlaying,
          isOpenSound: this.isOpenSound,
          isSupportDownload: this.isSupportDownload,
          isToolBarAreaShow: this.isToolBarAreaShow,
          onPlayClick: this.playAction,
          onVoiceClick: this.voiceAction,
          onDownloadClick: this.downloadAction,
          onRateClick: this.rateAction,
          onLargeClick: this.largeAction,
          // playRate: this.currentSelectRate
        })
        // 倍速播放选择控件
        ChoosePlayRateView({
          isPlayRateViewShow: this.isPlayRateViewShow,
          isCloudPlaybackRate: this.recordCategory == EZRecordCategory.EZRecordCategoryCloud,
          onPlayRateClick: this.selectRateAction
        })
        // 云存储图片
        PlaybackAdvertView({
          onAdvertImageClick: this.advertImageClickAction
        })
        // 录像类型切换
        PlaybackRecordToolView({
          onRecordCategoryTabClick: this.recordCategoryTabAction
        })
        // 录像列表视图
        this.PlaybackListView()
      }
      .id('content_playback_view')
      .flexBasis(450)
    }
  }

  /** 播放器视图 */
  @Builder
  PlaybackView() {
    RelativeContainer() {
      // 播放器视图
      XComponent({
        id: this.playbackPlayerComponentID,
        type: XComponentType.SURFACE,
        controller: this.xComponentController
      })
        .id('XComponent')
        .onLoad(() => {
          // 设置播放窗口
          this.xComponentController.setXComponentSurfaceSize({ surfaceWidth: 1920, surfaceHeight: 1080 })
          this.player?.setPlayerView(this.xComponentController.getXComponentSurfaceId())
        })
        .width('100%')
        .aspectRatio(16 / 9)
        .backgroundColor(Color.Black)
      // 双目设备两个播放器之间的分割线
      Row().height(this.isMultiChannelDevice ? 5 : 0).backgroundColor(Color.Black).id('playerViewDividLine')
        .alignRules({
          top: { 'anchor': 'XComponent', align: VerticalAlign.Bottom },
          left: { 'anchor': 'XComponent', align: HorizontalAlign.Start },
          right: { 'anchor': 'XComponent', align: HorizontalAlign.End }
        })
      // 双目设备第二个播放器视图
      if (this.isMultiChannelDevice) {
        XComponent({
          id: this.playbackPlayerComponentID2,
          type: XComponentType.SURFACE,
          controller: this.xComponentController2
        })
          .id('XComponent2')
          .onLoad(() => {
            // 设置播放窗口
            this.xComponentController2.setXComponentSurfaceSize({ surfaceWidth: 1920, surfaceHeight: 1080 })
            this.player?.setPlayerView(this.xComponentController2.getXComponentSurfaceId(), 1)
          })
          .width('100%')
          .aspectRatio(16 / 9)
          .backgroundColor(Color.Black)
          .alignRules({
            top: { 'anchor': 'playerViewDividLine', align: VerticalAlign.Bottom },
            left: { 'anchor': 'XComponent', align: HorizontalAlign.Start },
            right: { 'anchor': 'XComponent', align: HorizontalAlign.End }
          })
      }
      // 加载进程
      LoadingProgress()
        .width(40)
        .aspectRatio(1)
        .visibility(this.isStreamLoadingShow ? Visibility.Visible : Visibility.Hidden)
        .id('loadingProgress')
        .alignRules({
          middle: { 'anchor': 'XComponent', align: HorizontalAlign.Center },
          center: {
            'anchor': 'XComponent',
            align: this.isMultiChannelDevice ? VerticalAlign.Bottom : VerticalAlign.Center
          }
        })
      // 播放错误信息
      Text(this.errorMessage).fontColor(Color.White)
        .visibility(this.isErrorMessageShow ? Visibility.Visible : Visibility.Hidden)
        .alignRules({
          middle: { 'anchor': 'loadingProgress', align: HorizontalAlign.Center },
          center: { 'anchor': 'loadingProgress', align: VerticalAlign.Center }
        })
    }
    .width("auto").height("auto")
    .id('playback_view')
    .alignRules({
      top: { 'anchor': '__container__', align: VerticalAlign.Top },
      middle: { 'anchor': '__container__', align: HorizontalAlign.Center }
    })
  }

  @Builder
  PlaybackListView() {
    Column() {
      Refresh({ refreshing: $$this.isRefreshing, offset: 120, friction: 100 }) {
        if (this.playbackConvertRecords && this.playbackConvertRecords.length > 0) {
          Grid() {
            ForEach(this.playbackConvertRecords, (item: CloudRecordFile | DeviceRecordFile, index) => {
              GridItem() {
                PlaybackGridView({
                  cloudRecordFile: item instanceof CloudRecordFile ? item as CloudRecordFile : undefined,
                  deviceRecordFile: item instanceof DeviceRecordFile ? item as DeviceRecordFile : undefined,
                  itemPosition: index,
                  isItemSelected: this.selectedRecordIndex == index,
                  onPlaybackItemClick: this.playbackItemAction
                })
              }
            })
          }
          .columnsTemplate('1fr 1fr 1fr')
          .layoutDirection(GridDirection.Row)
          .columnsGap(10)
          .rowsGap(10)
          .id('bottomview_playback_list_view')

          .backgroundColor($r('app.color.message_cell_header_color'))
          .alignRules({
            left: { 'anchor': '__container__', align: HorizontalAlign.Start },
            top: { 'anchor': 'toolView', align: VerticalAlign.Bottom }
          })
          .height(`100%`)
          .margin({ left: 10, right: 10 })
        } else {
          // 无录像片段UI展示
          Column() {
            Image($r('app.media.playback_nodata')).width(100).height(100)
            Text($r('app.string.playback_record_empty'))
              .margin({ top: 5 })
              .fontColor($r('app.color.message_detail_info_content_color'))
          }.justifyContent(FlexAlign.Center)
          .width(`100%`)
          .height(`100%`)
        }
      }
      .onStateChange((refreshStatus: RefreshStatus) => {
        EZLog.info(this.TAG, 'Refresh onStatueChange state is ' + refreshStatus)
      })
      .onRefreshing(this.recordsRefreshAction)
    }
    .alignRules({
      left: { 'anchor': '__container__', align: HorizontalAlign.Start },
      top: { 'anchor': 'toolView', align: VerticalAlign.Bottom }
    })
    .backgroundColor($r('app.color.message_cell_header_color'))
    .id('playback_list_refresh_view')
    .padding({ top: 5, bottom: 170 })
  }
}