<template>
  <vue-progress-bar class="has-background-primary" />
  <navbar-top />
  <router-view v-slot="{ Component }">
    <component :is="Component" />
  </router-view>
  <modal-dialog-remote-pairing
    :show="pairingActive"
    @close="pairingActive = false"
  />
  <modal-dialog-update
    :show="uiStore.showUpdateDialog"
    @close="uiStore.showUpdateDialog = false"
  />
  <list-notifications v-show="!uiStore.showBurgerMenu" />
  <navbar-bottom />
  <div
    v-show="uiStore.showBurgerMenu || uiStore.showPlayerMenu"
    class="overlay-fullscreen"
    @click="uiStore.hideMenus"
  />
</template>

<script>
import ListNotifications from '@/components/ListNotifications.vue'
import ModalDialogRemotePairing from '@/components/ModalDialogRemotePairing.vue'
import ModalDialogUpdate from '@/components/ModalDialogUpdate.vue'
import NavbarBottom from '@/components/NavbarBottom.vue'
import NavbarTop from '@/components/NavbarTop.vue'
import ReconnectingWebSocket from 'reconnectingwebsocket'
import { useConfigurationStore } from '@/stores/configuration'
import { useLibraryStore } from '@/stores/library'
import { useLyricsStore } from '@/stores/lyrics'
import { useNotificationsStore } from '@/stores/notifications'
import { useOutputsStore } from './stores/outputs'
import { usePlayerStore } from '@/stores/player'
import { useQueueStore } from '@/stores/queue'
import { useRemotesStore } from './stores/remotes'
import { useServicesStore } from '@/stores/services'
import { useSettingsStore } from '@/stores/settings'
import { useUIStore } from './stores/ui'
import webapi from '@/webapi'

export default {
  name: 'App',
  components: {
    ListNotifications,
    ModalDialogRemotePairing,
    ModalDialogUpdate,
    NavbarBottom,
    NavbarTop
  },
  setup() {
    return {
      configurationStore: useConfigurationStore(),
      libraryStore: useLibraryStore(),
      lyricsStore: useLyricsStore(),
      notificationsStore: useNotificationsStore(),
      outputsStore: useOutputsStore(),
      playerStore: usePlayerStore(),
      queueStore: useQueueStore(),
      remotesStore: useRemotesStore(),
      servicesStore: useServicesStore(),
      settingsStore: useSettingsStore(),
      uiStore: useUIStore()
    }
  },
  data() {
    return {
      pairingActive: false,
      timerId: 0
    }
  },
  watch: {
    'uiStore.showBurgerMenu'() {
      this.updateClipping()
    },
    'uiStore.showPlayerMenu'() {
      this.updateClipping()
    }
  },
  created() {
    this.connect()
    this.$router.beforeEach((to, from, next) => {
      this.updateClipping()
      if (!(to.path === from.path && to.hash)) {
        if (to.meta.progress) {
          this.$Progress.parseMeta(to.meta.progress)
        }
        this.$Progress.start()
      }
      next()
    })
    this.$router.afterEach(() => {
      this.$Progress.finish()
    })
  },
  methods: {
    connect() {
      webapi
        .config()
        .then(({ data }) => {
          this.configurationStore.$state = data
          this.uiStore.hideSingles = data.hide_singles
          document.title = data.library_name
          this.openWebsocket()
          this.$Progress.finish()
        })
        .catch(() => {
          this.notificationsStore.add({
            text: this.$t('server.connection-failed'),
            topic: 'connection',
            type: 'danger'
          })
        })
    },
    openWebsocket() {
      const socket = this.createWebsocket()
      socket.onopen = () => {
        socket.send(
          JSON.stringify({
            notify: [
              'update',
              'database',
              'player',
              'options',
              'outputs',
              'volume',
              'queue',
              'spotify',
              'lastfm',
              'pairing'
            ]
          })
        )
        this.updateOutputs()
        this.updatePlayerStatus()
        this.updateLibraryStats()
        this.updateSettings()
        this.updateQueue()
        this.updateSpotify()
        this.updateLastfm()
        this.updatePairing()
      }

      let updateThrottled = false

      const updateInfo = () => {
        if (updateThrottled) {
          return
        }
        this.updateOutputs()
        this.updatePlayerStatus()
        this.updateLibraryStats()
        this.updateSettings()
        this.updateQueue()
        this.updateSpotify()
        this.updateLastfm()
        this.updatePairing()
        updateThrottled = true
        setTimeout(() => {
          updateThrottled = false
        }, 500)
      }

      window.addEventListener('focus', updateInfo)
      document.addEventListener('visibilitychange', () => {
        if (document.visibilityState === 'visible') {
          updateInfo()
        }
      })

      socket.onmessage = (response) => {
        const data = JSON.parse(response.data)
        if (
          data.notify.includes('update') ||
          data.notify.includes('database')
        ) {
          this.updateLibraryStats()
        }
        if (
          data.notify.includes('player') ||
          data.notify.includes('options') ||
          data.notify.includes('volume')
        ) {
          this.updatePlayerStatus()
        }
        if (data.notify.includes('outputs') || data.notify.includes('volume')) {
          this.updateOutputs()
        }
        if (data.notify.includes('queue')) {
          this.updateQueue()
        }
        if (data.notify.includes('spotify')) {
          this.updateSpotify()
        }
        if (data.notify.includes('lastfm')) {
          this.updateLastfm()
        }
        if (data.notify.includes('pairing')) {
          this.updatePairing()
        }
      }
    },
    createWebsocket() {
      const protocol = window.location.protocol.replace('http', 'ws')
      const hostname =
        (import.meta.env.DEV &&
          URL.parse(import.meta.env.VITE_OWNTONE_URL)?.hostname) ||
        window.location.hostname
      const suffix =
        this.configurationStore.websocket_port || `${window.location.port}/ws`
      const url = `${protocol}${hostname}:${suffix}`
      return new ReconnectingWebSocket(url, 'notify', {
        maxReconnectInterval: 2000,
        reconnectInterval: 1000
      })
    },
    updateClipping() {
      if (this.uiStore.showBurgerMenu || this.uiStore.showPlayerMenu) {
        document.querySelector('html').classList.add('is-clipped')
      } else {
        document.querySelector('html').classList.remove('is-clipped')
      }
    },
    updateLastfm() {
      webapi.lastfm().then(({ data }) => {
        this.servicesStore.lastfm = data
      })
    },
    updateLibraryStats() {
      webapi.library_stats().then(({ data }) => {
        this.libraryStore.$state = data
      })
      webapi.library_count('scan_kind is rss').then(({ data }) => {
        this.libraryStore.rss = data
      })
    },
    updateLyrics() {
      const track = this.queueStore.current
      if (track?.track_id) {
        webapi.library_track(track.track_id).then(({ data }) => {
          this.lyricsStore.lyrics = data.lyrics
        })
      } else {
        this.lyricsStore.$reset()
      }
    },
    updateOutputs() {
      webapi.outputs().then(({ data }) => {
        this.outputsStore.outputs = data.outputs
      })
    },
    updatePairing() {
      webapi.pairing().then(({ data }) => {
        this.remotesStore.$state = data
        this.pairingActive = data.active
      })
    },
    updatePlayerStatus() {
      webapi.player_status().then(({ data }) => {
        this.playerStore.$state = data
        this.updateLyrics()
      })
    },
    updateQueue() {
      webapi.queue().then(({ data }) => {
        this.queueStore.$state = data
        this.updateLyrics()
      })
    },
    updateSettings() {
      webapi.settings().then(({ data }) => {
        this.settingsStore.$state = data
      })
    },
    updateSpotify() {
      webapi.spotify().then(({ data }) => {
        this.servicesStore.spotify = data
        if (this.timerId > 0) {
          window.clearTimeout(this.timerId)
          this.timerId = 0
        }
        if (data.webapi_token_expires_in > 0 && data.webapi_token) {
          this.timerId = window.setTimeout(
            this.updateSpotify,
            1000 * data.webapi_token_expires_in
          )
        }
      })
    }
  }
}
</script>
