<!--
  RNBOControlValue ist eine Komponente zur Steuerung und Überwachung von Audio-Parametern,
  insbesondere für die Kontrolle von Frequenzbändern. Sie ermöglicht das Testen von
  Audio-Geräten, die Anpassung von Parametern und die Anzeige von Kontrollwerten in Echtzeit.
-->
<template>
  <div v-if="true">
    <h2>Control Values Device Test -- {{ centerFrequency }}Hz</h2>
    <button @click="testControlValuesDevice">Test Control Values Device</button>
    <p v-if="testResult">{{ testResult }}</p>
    <div class="microphone-info">
      <h3>Current Microphone:</h3>
      <p>{{ currentMicrophone || 'No microphone selected' }}</p>
    </div>

    <table v-if="parameters.length > 0" class="parameter-table">
      <thead>
        <tr>
          <th>Parameter Name</th>
          <th>Value</th>
          <th>Action</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="param in parameters" :key="param.name">
          <td>{{ param.name }}</td>
          <td>
            <input
              :value="param.value"
              type="number"
              @input="updateParameter(param.name, $event.target?.value)"
            >
          </td>
          <td>
            <input
              :value="param.value"
              type="range"
              :min="param.min"
              :max="param.max"
              :step="param.step"
              @input="updateParameter(param.name, $event.target?.value)"
            >
          </td>
        </tr>
      </tbody>
    </table>
    <!-- Neue Keynote für den Outlet-Wert -->
    <div class="outlet-keynote">
      <h3>Control Value</h3>
      <div class="outlet-value">{{ formatValue(outletValue) }}</div>
    </div>
  </div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { mapActions, mapState } from 'pinia'
import { useDevicesStore } from '~/stores/device'
import { useMicStore } from '~/stores/microphone'
import { useAudioStore } from '~/stores/audio'

interface ParameterData {
  name: string;
  paramId: string;
  value: number;
  min: number;
  max: number;
  step: number;
}

const minSamples = 48
const maxSamples = 1920000

export default defineComponent({
  name: 'RNBOControlValue',
  props: {
    centerFrequency: {
      type: Number,
      required: true,
      validator: (value: number) => [63, 125, 250, 500, 1000, 1500, 2000, 4000, 8000, 16000, 150].includes(value)
    },
    qFactor: {
      type: Number,
      default: 1.414,
      validator: (value: number) => value > 0.1 && value < 1.5
    },
    attack: {
      type: Number,
      default: 120000,
      validator: (value:number) => value >= minSamples && value <= maxSamples
    },
    release: {
      type: Number,
      default: 144000,
      validator: (value:number) => value >= minSamples && value <= maxSamples
    },
    status: {
      type: Boolean,
      default: false
    }
  },
  emits: ['control-value-change'],
  data () {
    return {
      testResult: '',
      parameters: [] as ParameterData[],
      device: null as any,
      outletValue: 0,
      currentMicrophone: ''
    }
  },
  computed: {
    ...mapState(useMicStore, ['microphone'])
  },
  watch: {
    release (value) {
      this.updateParameter('release', '' + value)
    },
    attack (value) {
      if (!isNaN(value)) {
        this.updateParameter('attack', '' + value)
      }
    },
    status (newStatus) {
      const microphoneStore = useMicStore()
      // status is bind to playing status in audio store
      if (newStatus) {
        microphoneStore.attachMicrophone()
        this.testControlValuesDevice()
      } else {
        microphoneStore.detachMicrophone()
      }
    }

  },
  mounted () {
    this.updateParameter('centerFrequency', '' + this.centerFrequency)
    this.updateParameter('release', '' + this.release)
    this.updateParameter('attack', '' + this.attack)
    this.updateParameter('qFactor', String(this.qFactor))
  },
  methods: {
    async getCurrentMicrophone () {
      const micStore = useMicStore()

      try {
        const micro = await micStore.getMicrophone()
        if (micro?.microphoneNode.label) {
          this.currentMicrophone = micro.microphoneNode.label
        } else {
          this.currentMicrophone = 'No microphone detected'
        }
      } catch (error) {
        useNuxtApp().$logger.error('Error getting microphone:', error)
        this.currentMicrophone = 'Error detecting microphone'
      }
    },
    async testControlValuesDevice () {
      const deviceStore = useDevicesStore()
      const micStore = useMicStore()
      const audioStore = useAudioStore()

      await audioStore.getContext()
      if (!audioStore) { return }
      if (audioStore.audioContext?.state !== 'running') {
        await audioStore.audioContext?.resume()
      }
      this.device = await deviceStore.createControlValuesDevice(`testControlValues_${this.centerFrequency}Hz`, this.centerFrequency)
      if (!this.device) {
        this.testResult = `Failed to create control values device for ${this.centerFrequency}Hz`
        // this.$logger.error('Device creation failed')
      }
      const device = this.device
      // this.$logger.info(`Created device for ${this.centerFrequency}Hz`, { device })
      const microphone = await micStore.getMicrophone()

      try {
        const micSource = microphone.microphoneNode
        if (micSource && this.device.node && audioStore.audioContext) {
          micSource.connect(this.device.node)
          await this.updateParameterList()

          await new Promise(resolve => setTimeout(resolve, 100))

          this.setupOutletListener()
          this.testResult = `Control values device for ${this.centerFrequency}Hz created successfully`
        } else {
          this.testResult = `Failed to connect microphone to device for ${this.centerFrequency}Hz`
          // this.$logger.error('Connection failed:', { micSource, deviceNode: this.device.node, audioContext: audioStore.audioContext })
        }
      } catch (error) {
        // this.$logger.error(`Test failed for ${this.centerFrequency}Hz:`, error)
        this.testResult = `Test failed for ${this.centerFrequency}Hz: ${error instanceof Error ? error.message : String(error)}`
        // this.$logger.error(this.testResult)
      }
    },
    setupOutletListener () {
      try {
        if (!this.device) {
          // this.$logger.warn('Device is not available')
          return
        }
        this.device.messageEvent.subscribe((ev: any) => {
          if (ev.tag === 'out1') {
            const newValue = this.ensureNumber(ev.payload)
            this.outletValue = newValue
            if (!(newValue > -13 && newValue < -12.98)) {
              this.$emit('control-value-change', { frequency: this.centerFrequency, value: newValue })
            }
          }
        })
      } catch (error: unknown) {
        // this.$logger.error(`Test failed for ${this.centerFrequency}Hz:`, error)
        this.testResult = `Test failed for ${this.centerFrequency}Hz: ${error instanceof Error ? error.message : String(error)}`
      }
    },
    updateParameterList () {
      // this.$logger.info('Updating parameter list', this.device)
      if (this.device && this.device.parameters) {
        const params = this.device.parameters
        // this.$logger.info('Parameters:', { params })

        this.parameters = Array.from(params.entries()).map(([name, param]: [string, any]) => {
          return {
            name: param.name,
            value: this.ensureNumber(param.value),
            min: this.ensureNumber(param.min),
            max: this.ensureNumber(param.max),
            step: this.ensureNumber(param.step) || 0.01
          }
        })

        // this.$logger.info('Updated parameters:', this.parameters)
      } else {
        // this.$logger.info('No parameters found')
      }
    },
    updateParameter (name: string, value: string) {
      if (this.device && this.device.parameters) {
        const numValue = this.ensureNumber(value)
        const param = this.parameters.find(p => p.name === name)

        if (param) {
          param.value = numValue

          if (typeof this.device.parameters.set === 'function') {
            this.device.parameters.set(name, numValue)
          } else if (Array.isArray(this.device.parameters)) {
            const deviceParam = this.device.parameters.find((p: any) => p.name === name)
            if (deviceParam) {
              deviceParam.value = numValue
            }
          } else if (typeof this.device.parameters === 'object') {
            if (this.device.parameters[name]) {
              this.device.parameters[name].value = numValue
            }
          }
          this.$forceUpdate()
        }
      } else {
        // Device noch nicht da → update nur in local parameters
        const param = this.parameters.find(p => p.name === name)
        if (param) {
          param.value = this.ensureNumber(value)
        } else {
          this.parameters.push({ name, value: this.ensureNumber(value) })
        }
        this.$forceUpdate()
      }
    },
    ensureNumber (value: any): number {
      const num = Number(value)
      return isNaN(num) ? 0 : num
    },
    formatValue (value: number): string {
      return value.toFixed(2)
    }
  }
})
</script>

<style scoped>
.parameter-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 20px;
}

.parameter-table th, .parameter-table td {
  border: 1px solid #ddd;
  padding: 8px;
  text-align: left;
}

.parameter-table th {
  background-color: #f2f2f2;
}

.parameter-table input[type="range"] {
  width: 100%;
}

.outlet-keynote {
  text-align: center;
  margin-top: 30px;
}

.outlet-keynote h3 {
  font-size: 1.2em;
  margin-bottom: 10px;
}

.outlet-value {
  font-size: 2em;
  font-weight: bold;
}
</style>
