<template>
  <div
    class="w-full flex items-center"
    v-bind="$attrs"
  >
    <div
      v-if="isError"
      class="flex items-center text-destructive-actions text-xs h-8"
    >
      <div
        v-if="!simple"
        class="flex items-center"
      >
        <i class="vc-icon-alert icon-2md mr-2" />
        {{ audioErrorMessage }}
      </div>
      <div
        v-else
        class="flex items-center"
      >
        <i class="vc-icon-alert icon-2md error-icon" />
      </div>
    </div>
    <div
      v-else
      class="w-full flex items-center relative"
    >
      <div
        v-if="!isPlaying"
        class="sound-controls"
        :class="{'right-offset': !simple}"
        @click.stop="play"
      >
        <i
          class="vc-icon-play"
          :class="[iconClass, disabled ? 'text-inactive-elements' : 'text-primary-actions']"
        />
      </div>
      <div
        v-else
        class="sound-controls"
        :class="{'right-offset': !simple}"
        @click.stop="pause"
      >
        <i
          class="vc-icon-pause2 text-primary-actions"
          :class="iconClass"
        />
      </div>
      <div
        v-if="showReset"
        class="sound-controls"
        :class="simple ? 'left-offset' : 'right-offset'"
        @click.stop="stop"
      >
        <i
          class="vc-icon-restart text-primary-actions"
          :class="iconClass"
        />
      </div>
      <div
        v-if="preIcon && !simple"
        class="flex items-center right-offset"
      >
        <i
          :class="preIcon"
          class="text-primary-actions icon-base"
        />
      </div>
      <div
        v-show="!simple"
        class="sound-time-current"
      >
        {{ toMinutes(currentTime) }}
      </div>
      <div
        v-show="!simple"
        class="sound-progress-bar"
        :class="height"
      >
        <VcSkeletonLoader
          v-if="loading"
          type="row"
          row-height="16"
          class="sound-loading"
        />
        <div
          v-show="isPlayingInProgress"
          class="flex-grow w-full h-full wave-form-content"
        >
          <div :id="`waveform-${soundId}`" />
        </div>
        <div
          v-if="!isPlayingInProgress && !loading"
          class="w-full h-px bg-ui-lines rounded-full"
        />
      </div>
      <div
        v-show="!simple"
        class="sound-duration"
      >
        {{ toMinutes(duration) }}
      </div>
      <div
        v-if="!simple"
        class="sound-volume"
      >
        <i
          ref="volumeIcon"
          :class="disabled ? 'text-inactive-elements' : 'text-primary-actions'"
          class="vc-icon-sounds cursor-pointer icon-base"
          @click="toggleVolumeTooltip"
        />
        <div
          v-if="volumeTooltip"
          class="volume-input-block"
        >
          <input
            v-model="volume"
            v-click-outside="onClickOutsideVolume"
            type="range"
            min="0"
            max="100"
            step="1"
            class="range-input"
            :style="{
              backgroundSize: `${volume}% 100%`
            }"
          >
        </div>
      </div>
      <div
        v-if="showDownloadButton && !simple"
        class="flex items-center"
      >
        <div
          v-for="(item, index) in downloadLinks"
          :key="index"
          class="cursor-pointer right-offset"
          @click.stop="download(item)"
        >
          <i
            class="text-primary-actions icon-base"
            :class="downloadIconType(item)"
          />
        </div>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import WaveSurfer from 'wavesurfer.js'
import { defineComponent, PropType } from 'vue'
import VcSkeletonLoader from '@/components/VcSkeletonLoader/VcSkeletonLoader.vue'
import clickOutside from '@/directives/click-outside'
import { VcSoundPlayerSize, VcSoundDownloads } from './VcSoundPlayer.types'

export default defineComponent({
  name: 'VcSoundPlayer',
  components: { VcSkeletonLoader },
  directives: {
    'click-outside': clickOutside
  },
  props: {
    soundId: {
      type: [String, Number],
      default: 'soundId'
    },
    src: {
      type: String,
      default: ''
    },
    simple: {
      type: Boolean,
      default: false
    },
    size: {
      type: String as PropType<VcSoundPlayerSize>,
      default: 'md'
    },
    disabled: {
      type: Boolean,
      default: false
    },
    preIcon: {
      type: String,
      default: ''
    },
    currentPlay: {
      type: String,
      default: ''
    },
    downloadLinks: {
      type: Array as PropType<VcSoundDownloads>,
      default: () => []
    }
  },
  emits: ['start', 'stop', 'onPlay', 'pause'],
  data () {
    return {
      loading: false,
      volumeTooltip: false,
      isPlaying: false,
      showReset: false,
      duration: 0,
      currentTime: 0,
      volume: 100,
      wavesurfer: {} as WaveSurfer,
      wavesurferOptions: {
        height: this.size === 'sm' ? 16 : 32,
        container: `#waveform-${this.soundId}`,
        barWidth: 0.5,
        barHeight: 1,
        barMinHeight: 0.5,
        waveColor: '#E0E2E5',
        progressColor: '#2675FF'
      },
      audioErrorMessage: '',
      isInitSound: false,
      firstPlay: false,
      timeInterval: 0 as number
    }
  },
  computed: {
    isError (): boolean {
      return this.audioErrorMessage !== ''
    },
    height (): string {
      return this.size === 'sm' ? 'h-4' : 'h-8'
    },
    iconClass (): string {
      const classes: {[key: number | string]: string} = {
        md: 'icon-2xl',
        sm: 'icon-lg'
      }
      return classes[this.size] || 'icon-2md'
    },
    isPlayingInProgress (): boolean {
      return this.isPlaying || this.showReset
    },
    showDownloadButton (): boolean {
      return this.downloadLinks.length > 0 && this.size === 'md'
    },
    progressTime (): number {
      return this.isPlayingInProgress ? this.currentTime : 0
    }
  },
  watch: {
    volume (val: number):void {
      this.setVolume(val)
    },
    src (newValue: string) {
      this.audioErrorMessage = ''
      if (newValue && this.isInitSound) {
        this.initSound()
      }
    },
    isPlaying (playing: boolean) {
      if (playing) {
        this.$emit('start', this.soundId, this.src)
      } else {
        this.$emit('stop', this.soundId, this.src)
      }
    },
    currentPlay (newValue: string) {
      if (newValue && newValue !== this.src && this.isPlaying) {
        this.pause()
      }
    }
  },
  mounted () {
    this.wavesurfer = WaveSurfer.create(this.wavesurferOptions)
  },
  beforeUnmount () {
    if (this.wavesurfer) {
      this.wavesurfer.destroy()
    }
  },
  methods: {
    onClickOutsideVolume (e: PointerEvent): void {
      if (e.target !== this.$refs.volumeIcon) {
        this.volumeTooltip = false
      }
    },
    toggleVolumeTooltip (): void {
      this.volumeTooltip = !this.volumeTooltip
    },
    closeTooltip (): void {
      this.volumeTooltip = false
    },
    initSound (): void {
      this.stop()
      this.loadingSound()
    },
    loadingSound (): void {
      if (!this.wavesurfer) {
        return
      }
      this.loading = true
      this.wavesurfer.load(this.src)
      this.wavesurfer.on('ready', this.wavesurfer.play.bind(this.wavesurfer))
      this.wavesurfer.on('play', () => this.onPlay())
      this.wavesurfer.on('finish', () => this.onFinish())

      this.wavesurfer.on('error', (errorMessage: Error) => {
        this.audioErrorMessage = errorMessage.message
      })
    },
    onPlay (): void {
      if (!this.wavesurfer) {
        return
      }
      this.loading = false
      this.duration = this.wavesurfer.getDuration()
      this.$emit('onPlay', this.soundId, this.src)
      this.timeInterval = window.setInterval(() => {
        if (this.isPlayingInProgress && this.wavesurfer) {
          this.currentTime = this.wavesurfer.getCurrentTime()
        }
      }, 300)
    },
    onFinish (): void {
      this.isPlaying = false
      this.showReset = false
      this.currentTime = 0
      if (this.timeInterval) {
        window.clearInterval(this.timeInterval)
      }
    },
    stop (): void {
      this.isPlaying = false
      this.showReset = false
      if (this.wavesurfer) {
        this.wavesurfer.pause()
      }
      this.currentTime = 0
    },
    play (): void {
      if (this.disabled || !this.wavesurfer) {
        return
      }
      this.isPlaying = true
      this.showReset = true

      if (!this.isInitSound) {
        this.isInitSound = true
        this.loadingSound()
        return
      }
      if (!this.currentTime) {
        this.wavesurfer.play(0)
      } else {
        this.wavesurfer.play()
      }
    },
    pause (): void {
      this.isPlaying = false
      this.$emit('pause', this.soundId, this.src)
      if (this.wavesurfer) {
        this.wavesurfer.pause()
      }
    },
    setVolume (val: number): void {
      if (!this.wavesurfer) {
        return
      }
      const floatValue = val * 0.01
      this.wavesurfer.setVolume(Number.parseFloat(floatValue.toFixed(2)))
    },
    toMinutes (seconds: number): string {
      const minutes = Math.floor(seconds / 60)
      const remainingSecondsInt = Math.floor(seconds - minutes * 60)
      let remainingSeconds = ''

      if (remainingSecondsInt < 10) {
        remainingSeconds = `0${remainingSecondsInt}`
      } else {
        remainingSeconds = `${remainingSecondsInt}`
      }

      return `${minutes}:${remainingSeconds}`
    },
    async download (sound: string): Promise<Blob> {
      const response = await fetch(sound)
      const dataBlob = await response.blob()
      const url = window.URL.createObjectURL(dataBlob)
      const link = document.createElement('a')
      const splitedUrl = sound.split('/')
      link.href = url
      link.setAttribute('download', splitedUrl[splitedUrl.length - 1])
      document.body.appendChild(link)
      link.click()
      return dataBlob
    },
    downloadIconType (item: string): string {
      const splittedUrl = item.split('.')
      const type = splittedUrl[splittedUrl.length - 1]
      if (type === 'mp3') {
        return 'vc-icon-mp3'
      }
      return 'vc-icon-wav'
    }
  }
})
</script>

<style lang="scss" scoped>
.wave-form-content :deep(wave) {
  border: none !important;
}

.right-offset {
  @apply mr-3;
}
.left-offset {
  @apply ml-3;
}

[dir=rtl] {
  .right-offset {
    @apply ml-3;
  }
  .left-offset {
    @apply mr-3;
  }
}

.sound-loading {
  @apply px-2 absolute top-0 left-0 flex items-center;
}

.sound-controls {
  @apply flex items-center cursor-pointer;
}

.sound-time-current {
  @apply text-xs w-9;
}

.sound-progress-bar {
  @apply flex-grow flex flex-col items-center justify-center px-2 relative;
}

.sound-duration {
  @apply text-xs w-9 opacity-50;
}

.sound-volume {
  @apply flex items-center mx-3 relative;
  .volume-input-block {
    @apply absolute bg-white flex items-center justify-center border border-ui-lines rounded-sm;
    height: 24px;
    width: 80px;
    top: -60px;
    left: -34px;
    padding: 4px;
    transform: rotate(-90deg);
  }
  .range-input {
    -webkit-appearance: none;
    width: 60px;
    height: 4px;
    background: var(--ui-lines);
    border-radius: 5px;
    background-size: 70% 100%;
    background-image: linear-gradient(var(--active-elements), var(--active-elements));
    background-repeat: no-repeat;
    &::-webkit-slider-thumb {
      -webkit-appearance: none;
      height: 12px;
      width: 12px;
      border-radius: 50%;
      background: var(--active-elements);
      cursor: pointer;
      box-shadow: 0 0 2px 0 #555;
      transition: background .3s ease-in-out;
      &:hover {
        background-color: var(--active-elements--focus);
      }
    }
    &::-webkit-slider-runnable-track  {
      -webkit-appearance: none;
      box-shadow: none;
      border: none;
      background: transparent;
    }

  }
}
</style>
