<template>
  <section
    ref="queueModal"
    class="main with-background component-queue"
    :aria-label="labels.queue"
  >
    <div :class="['ui vertical stripe queue segment', playerFocused ? 'player-focused' : '']">
      <div class="ui fluid container">
        <div
          id="queue-grid"
          class="ui stackable grid"
        >
          <div class="ui six wide column current-track">
            <div
              id="player"
              class="ui basic segment"
            >
              <template v-if="currentTrack">
                <img
                  v-if="currentTrack.cover && currentTrack.cover.urls.large_square_crop"
                  ref="cover"
                  alt=""
                  :src="$store.getters['instance/absoluteUrl'](currentTrack.cover.urls.large_square_crop)"
                >
                <img
                  v-else-if="currentTrack.album && currentTrack.album.cover && currentTrack.album.cover.urls.large_square_crop"
                  ref="cover"
                  alt=""
                  :src="$store.getters['instance/absoluteUrl'](currentTrack.album.cover.urls.large_square_crop)"
                >
                <img
                  v-else
                  class="ui image"
                  alt=""
                  src="../assets/audio/default-cover.png"
                >
                <h1 class="ui header">
                  <div class="content ellipsis">
                    <router-link
                      class="small header discrete link track"
                      :to="{name: 'library.tracks.detail', params: {id: currentTrack.id }}"
                    >
                      {{ currentTrack.title }}
                    </router-link>
                    <div class="sub header ellipsis">
                      <router-link
                        class="discrete link artist"
                        :to="{name: 'library.artists.detail', params: {id: currentTrack.artist.id }}"
                      >
                        {{ currentTrack.artist.name }}
                      </router-link>
                      <template v-if="currentTrack.album">
                        /
                        <router-link
                          class="discrete link album"
                          :to="{name: 'library.albums.detail', params: {id: currentTrack.album.id }}"
                        >
                          {{ currentTrack.album.title }}
                        </router-link>
                      </template>
                    </div>
                  </div>
                </h1>
                <div
                  v-if="currentTrack && errored"
                  class="ui small warning message"
                >
                  <h3 class="header">
                    <translate translate-context="Sidebar/Player/Error message.Title">
                      The track cannot be loaded
                    </translate>
                  </h3>
                  <p v-if="hasNext && playing && $store.state.player.errorCount < $store.state.player.maxConsecutiveErrors">
                    <translate translate-context="Sidebar/Player/Error message.Paragraph">
                      The next track will play automatically in a few seconds…
                    </translate>
                    <i class="loading spinner icon" />
                  </p>
                  <p>
                    <translate translate-context="Sidebar/Player/Error message.Paragraph">
                      You may have a connectivity issue.
                    </translate>
                  </p>
                </div>
                <div class="additional-controls tablet-and-below">
                  <track-favorite-icon
                    v-if="$store.state.auth.authenticated"
                    :track="currentTrack"
                  />
                  <track-playlist-icon
                    v-if="$store.state.auth.authenticated"
                    :track="currentTrack"
                  />
                  <button
                    v-if="$store.state.auth.authenticated"
                    :class="['ui', 'really', 'basic', 'circular', 'icon', 'button']"
                    :aria-label="labels.addArtistContentFilter"
                    :title="labels.addArtistContentFilter"
                    @click="$store.dispatch('moderation/hide', {type: 'artist', target: currentTrack.artist})"
                  >
                    <i :class="['eye slash outline', 'basic', 'icon']" />
                  </button>
                </div>
                <div class="progress-wrapper">
                  <div
                    v-if="currentTrack && !errored"
                    class="progress-area"
                  >
                    <div
                      ref="progress"
                      :class="['ui', 'small', 'vibrant', {'indicating': isLoadingAudio}, 'progress']"
                      @click="touchProgress"
                    >
                      <div
                        class="buffer bar"
                        :data-percent="bufferProgress"
                        :style="{ 'width': bufferProgress + '%' }"
                      />
                      <div
                        class="position bar"
                        :data-percent="progress"
                        :style="{ 'width': progress + '%' }"
                      />
                    </div>
                  </div>
                  <div
                    v-else
                    class="progress-area"
                  >
                    <div
                      ref="progress"
                      :class="['ui', 'small', 'vibrant', 'progress']"
                    >
                      <div class="buffer bar" />
                      <div class="position bar" />
                    </div>
                  </div>
                  <div class="progress">
                    <template v-if="!isLoadingAudio">
                      <a
                        href=""
                        :aria-label="labels.restart"
                        class="left floated timer discrete start"
                        @click.prevent="setCurrentTime(0)"
                      >{{ currentTimeFormatted }}</a>
                      <span class="right floated timer total">{{ durationFormatted }}</span>
                    </template>
                    <template v-else>
                      <span class="left floated timer">00:00</span>
                      <span class="right floated timer">00:00</span>
                    </template>
                  </div>
                </div>
                <div class="player-controls tablet-and-below">
                  <span
                    role="button"
                    :title="labels.previousTrack"
                    :aria-label="labels.previousTrack"
                    class="control"
                    :disabled="emptyQueue || null"
                    @click.prevent.stop="$store.dispatch('queue/previous')"
                  >
                    <i :class="['ui', 'backward step', {'disabled': emptyQueue}, 'icon']" />
                  </span>

                  <span
                    v-if="!playing"
                    role="button"
                    :title="labels.play"
                    :aria-label="labels.play"
                    class="control"
                    @click.prevent.stop="resumePlayback"
                  >
                    <i :class="['ui', 'play', {'disabled': !currentTrack}, 'icon']" />
                  </span>
                  <span
                    v-else
                    role="button"
                    :title="labels.pause"
                    :aria-label="labels.pause"
                    class="control"
                    @click.prevent.stop="pausePlayback"
                  >
                    <i :class="['ui', 'pause', {'disabled': !currentTrack}, 'icon']" />
                  </span>
                  <span
                    role="button"
                    :title="labels.next"
                    :aria-label="labels.next"
                    class="control"
                    :disabled="hasNext || null"
                    @click.prevent.stop="$store.dispatch('queue/next')"
                  >
                    <i :class="['ui', {'disabled': !hasNext}, 'forward step', 'icon']" />
                  </span>
                </div>
              </template>
            </div>
          </div>
          <div class="ui ten wide column queue-column">
            <div class="ui basic clearing fixed-header segment">
              <h2 class="ui header">
                <div class="content">
                  <button
                    class="ui right floated basic button"
                    @click="$store.commit('ui/queueFocused', null)"
                  >
                    <translate translate-context="*/Queue/*/Verb">
                      Close
                    </translate>
                  </button>
                  <button
                    class="ui right floated basic button danger"
                    @click="$store.dispatch('queue/clean')"
                  >
                    <translate translate-context="*/Queue/*/Verb">
                      Clear
                    </translate>
                  </button>
                  {{ labels.queue }}
                  <div class="sub header">
                    <div>
                      <translate
                        translate-context="Sidebar/Queue/Text"
                        :translate-params="{index: currentIndex + 1, length: queue.tracks.length}"
                      >
                        Track %{ index } of %{ length }
                      </translate><template v-if="!$store.state.radios.running">
                        -
                        <span :title="labels.duration">
                          {{ timeLeft }}
                        </span>
                      </template>
                    </div>
                  </div>
                </div>
              </h2>
            </div>
            <table class="ui compact very basic fixed single line selectable unstackable table">
              <draggable
                v-model:list="tracks"
                tag="tbody"
                handle=".handle"
                item-key="id"
                @update="reorder"
              >
                <template #item="{ element: track, index }">
                  <tr
                    :key="track.id"
                    :class="['queue-item', {'active': index === currentIndex}]"
                  >
                    <td class="handle">
                      <i class="grip lines icon" />
                    </td>
                    <td
                      class="image-cell"
                      @click="$store.dispatch('queue/currentIndex', index)"
                    >
                      <img
                        v-if="track.cover && track.cover.urls.original"
                        class="ui mini image"
                        alt=""
                        :src="$store.getters['instance/absoluteUrl'](track.cover.urls.medium_square_crop)"
                      >
                      <img
                        v-else-if="track.album && track.album.cover && track.album.cover.urls.original"
                        class="ui mini image"
                        alt=""
                        :src="$store.getters['instance/absoluteUrl'](track.album.cover.urls.medium_square_crop)"
                      >
                      <img
                        v-else
                        class="ui mini image"
                        alt=""
                        src="../assets/audio/default-cover.png"
                      >
                    </td>
                    <td
                      colspan="3"
                      @click="$store.dispatch('queue/currentIndex', index)"
                    >
                      <button
                        class="title reset ellipsis"
                        :title="track.title"
                        :aria-label="labels.selectTrack"
                      >
                        <strong>{{ track.title }}</strong><br>
                        <span>
                          {{ track.artist.name }}
                        </span>
                      </button>
                    </td>
                    <td class="duration-cell">
                      <template v-if="track.uploads.length > 0">
                        {{ time.durationFormatted(track.uploads[0].duration) }}
                      </template>
                    </td>
                    <td class="controls">
                      <template v-if="$store.getters['favorites/isFavorite'](track.id)">
                        <i class="pink heart icon" />
                      </template>
                      <button
                        :aria-label="labels.removeFromQueue"
                        :title="labels.removeFromQueue"
                        :class="['ui', 'really', 'tiny', 'basic', 'circular', 'icon', 'button']"
                        @click.stop="cleanTrack(index)"
                      >
                        <i class="x icon" />
                      </button>
                    </td>
                  </tr>
                </template>
              </draggable>
            </table>

            <div
              v-if="$store.state.radios.running"
              class="ui info message"
            >
              <div class="content">
                <h3 class="header">
                  <i class="feed icon" /> <translate translate-context="Sidebar/Player/Title">
                    You have a radio playing
                  </translate>
                </h3>
                <p>
                  <translate translate-context="Sidebar/Player/Paragraph">
                    New tracks will be appended here automatically.
                  </translate>
                </p>
                <button
                  class="ui basic primary button"
                  @click="$store.dispatch('radios/stop')"
                >
                  <translate translate-context="*/Player/Button.Label/Short, Verb">
                    Stop radio
                  </translate>
                </button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>
<script>
import { useStore } from '~/store'
import { mapState, mapGetters, mapActions } from 'vuex'
import { nextTick, onMounted, ref, computed } from 'vue'
import moment from 'moment'
import { sum } from 'lodash-es'
import time from '~/utils/time'
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'
import TrackPlaylistIcon from '~/components/playlists/TrackPlaylistIcon.vue'
import draggable from 'vuedraggable'
import { useTimeoutFn, useWindowScroll, useWindowSize } from '@vueuse/core'

export default {
  components: {
    TrackFavoriteIcon,
    TrackPlaylistIcon,
    draggable
  },
  setup () {
    const queueModal = ref()

    const { activate } = useFocusTrap(queueModal, { allowOutsideClick: true })
    activate()

    const store = useStore()
    const queue = store.state.queue
    const currentIndex = computed(() => store.state.queue.currentIndex)

    const { y: pageYOffset } = useWindowScroll()
    const { height: windowHeight } = useWindowSize()
    const scrollToCurrent = async () => {
      await nextTick()
      const item = queueModal.value?.querySelector('.queue-item.active')
      const { top } = item?.getBoundingClientRect() ?? { top: 0 }
      window.scrollTo({
        top: top + pageYOffset.value - windowHeight.value / 2,
        behavior: 'smooth'
      })
    }

    onMounted(async () => {
      await nextTick()
      // delay is to let transition work
      useTimeoutFn(scrollToCurrent, 400)
    })

    // TODO (wvffle): Add useVirtualList to speed up the queue rendering and potentially resolve #1471
    //                Each item has 49px height on desktop and 50.666px on tablet(?) and down
    return { queueModal, scrollToCurrent, queue, currentIndex }
  },
  data () {
    return {
      showVolume: false,
      isShuffling: false,
      tracksChangeBuffer: null,
      time
    }
  },
  computed: {
    ...mapState({
      playing: state => state.player.playing,
      isLoadingAudio: state => state.player.isLoadingAudio,
      volume: state => state.player.volume,
      looping: state => state.player.looping,
      duration: state => state.player.duration,
      bufferProgress: state => state.player.bufferProgress,
      errored: state => state.player.errored,
      currentTime: state => state.player.currentTime
    }),
    ...mapGetters({
      currentTrack: 'queue/currentTrack',
      hasNext: 'queue/hasNext',
      emptyQueue: 'queue/isEmpty',
      durationFormatted: 'player/durationFormatted',
      currentTimeFormatted: 'player/currentTimeFormatted',
      progress: 'player/progress'
    }),
    tracks: {
      get () {
        return this.$store.state.queue.tracks
      },
      set (value) {
        this.tracksChangeBuffer = value
      }
    },
    labels () {
      return {
        queue: this.$pgettext('*/*/*', 'Queue'),
        duration: this.$pgettext('*/*/*', 'Duration'),
        addArtistContentFilter: this.$pgettext('Sidebar/Player/Icon.Tooltip/Verb', 'Hide content from this artist…'),
        restart: this.$pgettext('*/*/*', 'Restart track')
      }
    },
    timeLeft () {
      const seconds = sum(
        this.queue.tracks.slice(this.queue.currentIndex).map((t) => {
          return (t.uploads || []).map((u) => {
            return u.duration || 0
          })[0] || 0
        })
      )
      return moment(this.$store.state.ui.lastDate).add(seconds, 'seconds').fromNow(true)
    },
    sliderVolume: {
      get () {
        return this.volume
      },
      set (v) {
        this.$store.commit('player/volume', v)
      }
    },
    playerFocused () {
      return this.$store.state.ui.queueFocused === 'player'
    }
  },
  watch: {
    '$store.state.ui.queueFocused': {
      handler (v) {
        if (v === 'queue') {
          this.$nextTick(() => {
            this.scrollToCurrent()
          })
        }
      },
      immediate: true
    },
    '$store.state.queue.currentIndex': {
      handler () {
        this.$nextTick(() => {
          this.scrollToCurrent()
        })
      }
    },
    '$store.state.queue.tracks': {
      handler (v) {
        if (!v || v.length === 0) {
          this.$store.commit('ui/queueFocused', null)
        }
      },
      immediate: true,
      deep: true
    },
    '$route.fullPath' () {
      this.$store.commit('ui/queueFocused', null)
    }
  },
  methods: {
    ...mapActions({
      cleanTrack: 'queue/cleanTrack',
      mute: 'player/mute',
      unmute: 'player/unmute',
      clean: 'queue/clean',
      toggleMute: 'player/toggleMute',
      resumePlayback: 'player/resumePlayback',
      pausePlayback: 'player/pausePlayback'
    }),
    reorder: function (event) {
      this.$store.commit('queue/reorder', {
        tracks: this.tracksChangeBuffer,
        oldIndex: event.oldIndex,
        newIndex: event.newIndex
      })
    },
    touchProgress (e) {
      const target = this.$refs.progress
      const time = (e.layerX / target.offsetWidth) * this.duration
      this.$emit('touch-progress', time)
    },
    shuffle () {
      const disabled = this.queue.tracks.length === 0
      if (this.isShuffling || disabled) {
        return
      }
      const self = this
      const msg = this.$pgettext('Content/Queue/Message', 'Queue shuffled!')
      this.isShuffling = true
      setTimeout(() => {
        self.$store.dispatch('queue/shuffle', () => {
          self.isShuffling = false
          self.$store.commit('ui/addMessage', {
            content: msg,
            date: new Date()
          })
        })
      }, 100)
    }
  }
}
</script>
