<template>
  <div class="player">
    <AudioElement
      ref="Noise"
      v-model:volume="volume"
      :src="tropics_src"
      @update:playing="handlePlayingUpdate2"
      @update:canplay="handleCanPlayNoise"
    />
  </div>
</template>
<script lang="ts">
import AudioElement from '../AudioElement.vue'
import { useAudioStore } from '../../../stores/audio'
export default {
  name: 'NoiseGain',
  components: { AudioElement },
  data () {
    return {
      audioContext: useAudioStore().getContext(),
      createdNodes: {} as any,
      musicReady: false,
      tropics_src: window.location.origin + useRuntimeConfig().public.tracks.masking_src as string,
      fading: false,
      connected: false,
      muted: false,
      volume: useAudioStore().noiseVolume,
      previousVolume: useAudioStore().noiseVolume
    }
  },
  beforeUnmount () {
    this.disconnectNodes()
  },
  methods: {
    disconnectNodes () {
      if (typeof this.createdNodes === 'object' && this.createdNodes !== null) {
        Object.values(this.createdNodes).forEach((node) => {
        // Check if the node exists and has a disconnect method
          if (node && typeof AudioNode) {
            const tobedisconnected = node as AudioNode
            tobedisconnected.disconnect()
            node = null
          }
        })
      }
      this.createdNodes = null
    },
    toggleMute () {
      const element = this.$refs.Noise as typeof AudioElement
      const audioElement = element.$refs.audioElement as HTMLMediaElement
      if (this.muted) {
        // Unmute: Stelle den vorherigen Lautstärkewert wieder her
        this.muted = false
        audioElement.muted = false
        this.volume = this.previousVolume || 1 // Falls kein vorheriger Wert gespeichert ist, setze auf 1
        audioElement.volume = this.volume
      } else {
        // Mute: Speichere den aktuellen Lautstärkewert und mute das Audio
        this.previousVolume = this.volume
        this.volume = 0
        audioElement.volume = 0
        this.muted = true
        audioElement.muted = true
      }
      useAudioStore().setNoiseVolume(this.volume)
      element.$emit('update:volume', this.volume)
    },
    mute () {
      const element = this.$refs.Noise as typeof AudioElement
      const audioElement = element.$refs.audioElement as HTMLMediaElement
      audioElement.muted = true
      this.muted = audioElement.muted
    },
    unmute () {
      const element = this.$refs.Noise as typeof AudioElement
      const audioElement = element.$refs.audioElement as HTMLMediaElement
      audioElement.muted = false
      this.muted = audioElement.muted
    },
    // This method helps to get the ressources free when we stop playing the audio
    // without it would be louder each time we start playing
    refreshAudioContext () {
      const newAudioContext = new AudioContext()
      this.audioContext.close()
      useAudioStore().audioContext = newAudioContext
      this.audioContext = useAudioStore().getContext()
    },
    fadeInGains () {
      if (useAudioStore().playing !== true) { return }
      const fadeTime = this.audioContext.currentTime + 3.0
      this.fading = true
      this.unmute()
      const musicGain = this.createdNodes.musicGain
      this.createdNodes.musicGain.gain.setValueAtTime(0, this.audioContext.currentTime)
      musicGain.gain.linearRampToValueAtTime(1.0, fadeTime)

      setTimeout(() => {
        this.fading = false
      }, fadeTime * 1000)
    },
    fadeOutGains () {
      if (this.createdNodes.musicGain) {
        const musicGainValue = this.createdNodes.musicGainValue.gain.value
        this.createdNodes.musicGain.gain.linearRampToValueAtTime(musicGainValue, this.audioContext.currentTime)
        this.createdNodes.musicGain.gain.linearRampToValueAtTime(0, this.audioContext.currentTime + 1.3)
      }
    },

    handleCanPlayNoise () {
      // useNuxtApp().$logger.log('NoiseElemeint has now playingstate: ' + state)
      this.musicReady = true
      this.handlePlayingUpdate(true)
    },

    readyForWebaudio () {
      if (!this.musicReady) {
        // useNuxtApp().$logger.log('music not ready')
        return false
      }
      return true
    },
    handlePlayingUpdate2 (state: boolean) {
      // useNuxtApp().$logger.log('A new State reached us, it is a handlingPlay update' + state)
      // useNuxtApp().$logger.log('ReadyState of all:' + this.readyForWebaudio())
      if (!state) {
        this.mute()
        return
      }
      if (this.readyForWebaudio()) {
        if (state) {
          this.handlePlayingUpdate(state)
        } else {
          this.fadeOutGains()
        }
      }
    },
    handlePlayingUpdate (state: boolean) {
      if (state) {
        const musicElement = this.$refs.Noise as typeof AudioElement
        const musicAudioElement = musicElement.$refs.audioElement as HTMLMediaElement
        const audioContext = this.audioContext
        const destination = this.audioContext.destination

        this.createdNodes.musicGain ||= audioContext.createGain()

        this.createdNodes.musicGain.gain.setValueAtTime(0, audioContext.currentTime)

        if (musicAudioElement.currentSrc !== this.tropics_src) {
          this.createdNodes.musicSource.disconnect()
          this.createdNodes.musicSource = audioContext.createMediaElementSource(musicAudioElement)
          this.createdNodes.musicSource.connect(this.createdNodes.musicGain)
          this.createdNodes.musicGain.connect(destination)
        }

        this.createdNodes.musicSource ||= audioContext.createMediaElementSource(musicAudioElement)

        // useNuxtApp().$logger.log({ currentlyCreatedNodes })
        this.createdNodes.musicSource.connect(this.createdNodes.musicGain)
        this.createdNodes.musicGain.connect(destination)
        this.createdNodes.musicGain.gain.cancelScheduledValues(this.audioContext.currentTime)
        this.createdNodes.musicGain.gain.setValueAtTime(0, this.audioContext.currentTime)

        this.connected = true
        this.fadeInGains()
        // useAudioStore().playing = true
      } else {
        // Noise has just stopped react on it.
        // useNuxtApp().$logger.log('Stop everything webaudio is still running')
        this.fadeOutGains()
        this.createdNodes = []
        this.refreshAudioContext()
        this.connected = false
      }
    },
    applyStoredVolume () {
      const element = this.$refs.Noise as typeof AudioElement
      const audioElement = element.$refs.audioElement as HTMLMediaElement

      // Setze die Lautstärke des Audio-Elements
      audioElement.volume = this.volume

      // Emitiere ein Event, um die Lautstärke in AudioElement zu aktualisieren
      element.$emit('update:volume', this.volume)
    },
    updateNoiseGain (volume: number) {
      if (this.createdNodes.musicGain) {
        this.createdNodes.musicGain.gain.linearRampToValueAtTime(volume, this.createdNodes.musicGain.context.currentTime + 0.30)
      }
    }
  }
}
</script>
