/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * 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 display from '@ohos.display'
import hardware_deviceManager from '@ohos.distributedHardware.deviceManager'
import KvStoreModel from '../model/KvStoreModel'
import Logger from '../model/Logger'
import mediaQuery from '@ohos.mediaquery'
import PlayerModel from '../model/PlayerModel'
import { RemoteDeviceModel } from '../model/RemoteDeviceModel'
import { DeviceDialog } from '../common/DeviceDialog'

const TAG: string = 'Index'
const DESIGN_WIDTH: number = 720.0
const SYSTEM_UI_HEIGHT: number = 134
const DESIGN_RATIO: number = 16 / 9
const ONE_HUNDRED: number = 100
const ONE_THOUSAND: number = 1000
const SIXTY: number = 60
const REMOTE_ABILITY_STARTED: string = 'remoteAbilityStarted'

@Entry
@Component
struct Index {
  private listener = mediaQuery.matchMediaSync('screen and (min-aspect-ratio: 1.5) or (orientation: landscape)')
  @State isLand: boolean = false
  @State currentTimeText: string = ''
  @State currentProgress: number = 0
  @State totalMs: number = 0
  @State riscale: number = 1
  @State risw: number = 720
  @State rish: number = 1280
  @State isSwitching: boolean = false
  @State deviceLists: Array<hardware_deviceManager.DeviceInfo> = []
  @State isDialogShowing: boolean = false
  @State isDistributed: boolean = false
  @State title: string = ''
  @State totalTimeText: string = '00:00'
  @State albumSrc: Resource = $r('app.media.album')
  @State selectedIndex: number = 0
  @State imageArrays: Array<Resource> = [$r('app.media.ic_hop'), $r('app.media.ic_play_previous'), $r('app.media.ic_play'), $r('app.media.ic_play_next')]
  private dialogController: CustomDialogController = null
  private remoteDeviceModel: RemoteDeviceModel = new RemoteDeviceModel()
  private context
  onLand = (mediaQueryResult) => {
    Logger.info(TAG, `onLand: mediaQueryResult.matches= ${mediaQueryResult.matches}`)
    if (mediaQueryResult.matches) {
      this.isLand = true
    } else {
      this.isLand = false
    }
  }

  showDialog() {
    this.remoteDeviceModel.registerDeviceListCallback(() => {
      Logger.info(TAG, 'registerDeviceListCallback, callback entered')
      this.deviceLists = []
      this.deviceLists.push({
        deviceId: '0',
        deviceName: '本机',
        deviceType: 0,
        networkId: ''
      })
      let deviceTempList = this.remoteDeviceModel.discoverLists.length > 0 ? this.remoteDeviceModel.discoverLists : this.remoteDeviceModel.deviceLists
      for (let i = 0; i < deviceTempList.length; i++) {
        Logger.info(TAG, `device ${i}/${deviceTempList.length} deviceId= ${deviceTempList[i].deviceId},
         deviceName= ${deviceTempList[i].deviceName}, deviceType= ${deviceTempList[i].deviceType}`)
        this.deviceLists.push(deviceTempList[i])
        Logger.info(TAG, 'deviceLists push end')
      }
      Logger.info(TAG, 'CustomDialogController start')
      if (this.dialogController !== null) {
        this.dialogController.close()
        this.dialogController = null
      }
      this.dialogController = new CustomDialogController({
        builder: DeviceDialog({
          deviceLists: this.deviceLists,
          selectedIndex: this.selectedIndex,
          selectedIndexChange: this.selectedIndexChange
        }),
        autoCancel: true
      })
      this.dialogController.open()
      Logger.info(TAG, 'CustomDialogController end')
    })
  }

  onBackPress() {
    if (this.isDialogShowing === true) {
      this.dismissDialog()
      return true
    }
    return false
  }

  dismissDialog() {
    this.dialogController.close()
    this.remoteDeviceModel.unregisterDeviceListCallback()
    this.isDialogShowing = false
  }

  startAbilityContinuation(deviceId) {
    this.dialogController.close()
    let params
    Logger.info(TAG, `startAbilityContinuation PlayerModel.index= ${PlayerModel.index}/${PlayerModel.playlist.audioFiles.length}`)
    if (PlayerModel.index >= 0 && PlayerModel.index <= PlayerModel.playlist.audioFiles.length) {
      params = {
        uri: PlayerModel.playlist.audioFiles[PlayerModel.index].fileUri,
        seekTo: PlayerModel.getCurrentMs(),
        isPlaying: PlayerModel.isPlaying
      }
    } else {
      params = {
        uri: '',
        seekTo: 0,
        isPlaying: false
      }
    }
    Logger.info(TAG, `globalThis.abilityConText.startAbility deviceId= ${deviceId}`)
    let wantValue = {
      bundleName: 'ohos.samples.etsdistributedmusicplayer',
      abilityName: 'MainAbility',
      deviceId: deviceId,
      parameters: params
    }
    let timerId = setTimeout(() => {
      Logger.info(TAG, 'onMessageReceiveTimeout, terminateSelf')
      globalThis.abilityConText.terminateSelf((error) => {
        Logger.info(TAG, `terminateSelf finished, error= ${error}`)
      })
    }, 3000)

    KvStoreModel.setOnMessageReceivedListener(this.context, REMOTE_ABILITY_STARTED, () => {
      Logger.info(TAG, 'OnMessageReceived, terminateSelf')
      clearTimeout(timerId)
      globalThis.abilityConText.terminateSelf((error) => {
        Logger.info(TAG, `terminateSelf finished, error= ${error}`)
      })
    })
    Logger.info(TAG, `globalThis.abilityConText.startAbility start`)
    globalThis.abilityConText.startAbility(wantValue).then((data) => {
      Logger.info(TAG, `globalThis.abilityConText.startAbility finished ${JSON.stringify(data)}`)
    })
    this.clearSelectState()
    Logger.info(TAG, `globalThis.abilityConText.startAbility want= ${JSON.stringify(wantValue)}`)
    Logger.info(TAG, 'globalThis.abilityConText.startAbility end')
  }

  selectedIndexChange = (selectedIndex) => {
    if (selectedIndex === 0) {
      globalThis.abilityConText.startAbility({ bundleName: 'ohos.samples.etsdistributedmusicplayer',
        abilityName: 'MainAbility',
        deviceId: this.deviceLists[selectedIndex].deviceId,
        parameters: {
          isFA: 'EXIT'
        }
      }).then((data) => {
        Logger.info(TAG, `startAbility finished ${JSON.stringify(data)}`)
      })
      this.isDistributed = false
      this.selectedIndex = 0
      this.dialogController.close()
      this.deviceLists = []
      return
    }
    this.selectedIndex = selectedIndex
    this.selectDevice()
  }

  selectDevice() {
    Logger.info(TAG, 'start ability ......')
    if (this.remoteDeviceModel === null || this.remoteDeviceModel.discoverLists.length <= 0) {
      Logger.info(TAG, `start ability device:${JSON.stringify(this.deviceLists)}`)
      this.startAbilityContinuation(this.deviceLists[this.selectedIndex].deviceId)
      this.clearSelectState()
      return
    }
    Logger.info(TAG, 'start ability, needAuth')
    this.remoteDeviceModel.authDevice(this.deviceLists[this.selectedIndex], (device) => {
      Logger.info(TAG, 'auth and online finished')
      this.startAbilityContinuation(device.deviceId)
    })
    Logger.info(TAG, 'start ability2 ......')
    this.clearSelectState()
  }

  clearSelectState() {
    this.deviceLists = []
    this.dialogController.close()
    this.dialogController = null
  }

  getShownTimer(ms) {
    let seconds = Math.floor(ms / ONE_THOUSAND)
    let sec = seconds % SIXTY
    let minStr: string
    let secStr: string
    let min = (seconds - sec) / SIXTY
      sec < 10 ? secStr = '0' + sec : '' + sec
      min < 10 ? minStr = '0' + min : '' + min
    return minStr + ':' + secStr
  }

  refreshSongInfo(index) {
    Logger.info(TAG, `refreshSongInfo ${index}/${PlayerModel.playlist.audioFiles.length}`)
    if (index >= PlayerModel.playlist.audioFiles.length) {
      Logger.warn(TAG, 'refreshSongInfo ignored')
      return
    }
    // update song title
    this.title = PlayerModel.playlist.audioFiles[index].name
    this.albumSrc = (index % 2 === 0) ? $r('app.media.album') : $r('app.media.album2')

    // update duration
    this.totalMs = PlayerModel.getDuration()
    this.totalTimeText = this.getShownTimer(this.totalMs)
    this.currentTimeText = this.getShownTimer(PlayerModel.getCurrentMs())

    Logger.info(TAG, `refreshSongInfo this.title= ${this.title}, this.totalMs=
            ${this.totalMs}, this.totalTimeText= ${this.totalTimeText},this.currentTimeText= ${this.currentTimeText}`)
  }

  onPreviousClick() {
    if (this.isSwitching) {
      Logger.info(TAG, 'onPreviousClick ignored, isSwitching')
      return
    }
    Logger.info(TAG, 'onPreviousClick')
    PlayerModel.index--
    if (PlayerModel.index < 0 && PlayerModel.playlist.audioFiles.length >= 1) {
      PlayerModel.index = PlayerModel.playlist.audioFiles.length - 1
    }
    this.currentProgress = 0
    this.isSwitching = true

    PlayerModel.preLoad(PlayerModel.index, () => {
      this.refreshSongInfo(PlayerModel.index)
      PlayerModel.play(0, true)
      if (PlayerModel.isPlaying) {
        this.imageArrays[2] = $r('app.media.ic_pause')
      }
      this.isSwitching = false
    })
  }

  onNextClick() {
    if (this.isSwitching) {
      Logger.info(TAG, 'onNextClick ignored, isSwitching')
      return
    }
    Logger.info(TAG, 'onNextClick')
    PlayerModel.index++
    if (PlayerModel.index >= PlayerModel.playlist.audioFiles.length) {
      PlayerModel.index = 0
    }
    this.currentProgress = 0
    this.isSwitching = true
    PlayerModel.preLoad(PlayerModel.index, () => {
      this.refreshSongInfo(PlayerModel.index)
      PlayerModel.play(0, true)
      if (PlayerModel.isPlaying) {
        this.imageArrays[2] = $r('app.media.ic_pause')
      }
      this.isSwitching = false
    })
  }

  onPlayClick() {
    if (this.isSwitching) {
      Logger.info(TAG, 'onPlayClick ignored, isSwitching')
      return
    }
    Logger.info(TAG, `onPlayClick isPlaying= ${PlayerModel.isPlaying}`)
    if (PlayerModel.isPlaying) {
      PlayerModel.pause()
      this.imageArrays[2] = $r('app.media.ic_play')
    } else {
      PlayerModel.preLoad(0, () => {
        PlayerModel.play(-1, true)
        this.imageArrays[2] = $r('app.media.ic_pause')
      })
    }
  }

  restoreFromWant() {
    Logger.info(TAG, 'restoreFromWant')
    let status = globalThis.abilityWant.parameters
    if (status !== null && status.uri !== null) {
      KvStoreModel.broadcastMessage(this.context, REMOTE_ABILITY_STARTED)
      Logger.info(TAG, 'restorePlayingStatus')
      PlayerModel.restorePlayingStatus(status, (index) => {
        Logger.info(TAG, `restorePlayingStatus finished, index= ${index}`)
        if (index >= 0) {
          this.refreshSongInfo(index)
        } else {
          PlayerModel.preLoad(0, () => {
            this.refreshSongInfo(0)
          })
        }
      })
    } else {
      PlayerModel.preLoad(0, () => {
        this.refreshSongInfo(0)
      })
    }
  }

  aboutToAppear() {
    Logger.info(TAG, `begin`)
    Logger.info(TAG, 'grantPermission')
    let requestCode = 666
    globalThis.abilityConText.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], requestCode, function (result) {
      Logger.info(TAG, `grantPermission,requestPermissionsFromUser,result= ${result}`)
    })
    this.context = getContext(this)
    display.getDefaultDisplay().then((dis) => {
      Logger.info(TAG, `getDefaultDisplay dis= ${JSON.stringify(dis)}`)
      let proportion = DESIGN_WIDTH / dis.width
      let screenWidth = DESIGN_WIDTH
      let screenHeight = (dis.height - SYSTEM_UI_HEIGHT) * proportion
      this.riscale = (screenHeight / screenWidth) / DESIGN_RATIO
      if (this.riscale < 1) {
        // The screen ratio is shorter than design ratio
        this.risw = screenWidth * this.riscale
        this.rish = screenHeight
      } else {
        // The screen ratio is longer than design ratio
        this.risw = screenWidth
        this.rish = screenHeight / this.riscale
      }
      Logger.info(TAG, `proportion=${proportion} , screenWidth= ${screenWidth},
      screenHeight= ${screenHeight} , riscale= ${this.riscale} , risw= ${this.risw} , rish= ${this.rish}`)
    })
    Logger.info(TAG, 'getDefaultDisplay end')
    this.currentTimeText = this.getShownTimer(0)
    PlayerModel.setOnStatusChangedListener((isPlaying) => {
      Logger.info(TAG, `on player status changed, isPlaying= ${isPlaying} refresh ui`)
      PlayerModel.setOnPlayingProgressListener((currentTimeMs) => {
        this.currentTimeText = this.getShownTimer(currentTimeMs)
        this.currentProgress = Math.floor(currentTimeMs / this.totalMs * ONE_HUNDRED)
      })
      if (isPlaying) {
        this.imageArrays[2] = $r('app.media.ic_pause')
      } else {
        this.imageArrays[2] = $r('app.media.ic_play')
      }
    })

    PlayerModel.getPlaylist(() => {
      Logger.info(TAG, 'on playlist generated, refresh ui')
      this.restoreFromWant()
    })
  }

  build() {
    Column() {
      Text(this.title)
        .width('100%')
        .fontSize(30)
        .margin({ top: '10%' })
        .fontColor(Color.White)
        .textAlign(TextAlign.Center)
      Image(this.albumSrc)
        .width(this.isLand ? '60%' : '80%')
        .height(this.isLand ? '50%' : '35%')
        .objectFit(ImageFit.Contain)
        .margin({ top: 20, left: 40, right: 40 })
      Row() {
        Text(this.currentTimeText)
          .fontSize(20)
          .fontColor(Color.White)
        Blank()
        Text(this.totalTimeText)
          .fontSize(20)
          .fontColor(Color.White)
      }
      .width('90%')
      .margin({ top: '10%' })

      Slider({ value: this.currentProgress })
        .width('80%')
        .selectedColor('#ff0c4ae7')
        .onChange((value: number, mode: SliderChangeMode) => {
          this.currentProgress = value
          if (isNaN(this.totalMs)) {
            this.currentProgress = 0
            Logger.info(TAG, `setProgress ignored, totalMs= ${this.totalMs}`)
            return
          }
          var currentMs = this.currentProgress / ONE_HUNDRED * this.totalMs
          this.currentTimeText = this.getShownTimer(currentMs)
          if (mode === SliderChangeMode.End || mode === SliderChangeMode.Click) {
            Logger.info(TAG, `player.seek= ${currentMs}`)
            PlayerModel.seek(currentMs)
          }
        })

      Flex({
        direction: FlexDirection.Row,
        alignContent: FlexAlign.SpaceAround,
        justifyContent: FlexAlign.SpaceEvenly
      }) {
        ForEach(this.imageArrays, (item, index) => {
          Column() {
            Image(item)
              .size({ width: 72, height: 72 })
              .objectFit(ImageFit.Contain)
              .onClick(() => {
                switch (index) {
                  case 0:
                    this.showDialog()
                    break
                  case 1:
                    this.onPreviousClick()
                    break
                  case 2:
                    this.onPlayClick()
                    break
                  case 3:
                    this.onNextClick()
                    break
                  default:
                    break
                }
              })
          }
          .width(100)
          .height(100)
          .alignItems(HorizontalAlign.Center)
          .justifyContent(FlexAlign.Center)
        })
      }
      .width('100%')
      .margin({ top: '10%' })
    }
    .width('100%')
    .height('100%')
    .backgroundImage($r('app.media.bg_blurry'))
    .backgroundImageSize({ width: '100%', height: '100%' })
  }
}