<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue'
import { Mic, MicOff, Settings, Activity } from 'lucide-vue-next'

const isListening = ref(false)
const hasError = ref(false)
const errorMessage = ref('')
const canvasRef = ref(null)

// Analyzer Settings
const fftSize = ref('2048')
const smoothingTimeConstant = ref(0.8)

const fftOptions = [
  { value: '256', label: 'Very Low (256/Fast)' },
  { value: '512', label: 'Low (512)' },
  { value: '1024', label: 'Medium (1024)' },
  { value: '2048', label: 'High (2048)' },
  { value: '4096', label: 'Ultra (4096)' },
  { value: '8192', label: 'Max (8192/Slow)' }
]

let audioContext = null
let analyser = null
let microphone = null
let dataArray = null
let animationId = null
let canvasCtx = null

const initAudio = async () => {
  try {
    const stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        echoCancellation: false,
        autoGainControl: false,
        noiseSuppression: false
      }
    })

    // Initialize Audio Context
    audioContext = new (window.AudioContext || window.webkitAudioContext)()
    analyser = audioContext.createAnalyser()

    // Apply Settings
    analyser.fftSize = parseInt(fftSize.value, 10)
    analyser.smoothingTimeConstant = smoothingTimeConstant.value

    microphone = audioContext.createMediaStreamSource(stream)
    microphone.connect(analyser)

    // Buffer for data
    const bufferLength = analyser.frequencyBinCount
    dataArray = new Uint8Array(bufferLength)

    if (canvasRef.value) {
      canvasCtx = canvasRef.value.getContext('2d')
    }

    isListening.value = true
    hasError.value = false
    draw()
  } catch (err) {
    console.error('Error accessing microphone:', err)
    hasError.value = true
    errorMessage.value = 'Could not access the microphone. Please check your system permissions.'
    isListening.value = false
  }
}

const stopAudio = () => {
  isListening.value = false

  if (animationId) {
    cancelAnimationFrame(animationId)
    animationId = null
  }

  if (microphone) {
    microphone.disconnect()
    microphone.mediaStream.getTracks().forEach(track => track.stop())
    microphone = null
  }

  if (analyser) {
    analyser.disconnect()
    analyser = null
  }

  if (audioContext) {
    audioContext.close()
    audioContext = null
  }

  // Clear Canvas
  if (canvasCtx && canvasRef.value) {
    canvasCtx.clearRect(0, 0, canvasRef.value.width, canvasRef.value.height)
  }
}

const toggleListen = () => {
  if (isListening.value) {
    stopAudio()
  } else {
    initAudio()
  }
}

const draw = () => {
  if (!isListening.value || !canvasRef.value || !canvasCtx || !analyser) return

  animationId = requestAnimationFrame(draw)

  const width = canvasRef.value.width
  const height = canvasRef.value.height

  analyser.getByteFrequencyData(dataArray)

  // Clear canvas for next frame
  canvasCtx.clearRect(0, 0, width, height)

  const bufferLength = analyser.frequencyBinCount
  const barWidth = (width / bufferLength) * 2.5 // scale factor to look nicer
  let barHeight
  let x = 0

  for (let i = 0; i < bufferLength; i++) {
    barHeight = dataArray[i]

    // Calculate color based on height/frequency
    // We use a CSS variable mapped to RGB or a fallback gradient
    const r = barHeight + (25 * (i/bufferLength))
    const g = 250 * (i/bufferLength)
    const b = 250 - (barHeight/2)

    canvasCtx.fillStyle = `rgb(${r},${g},${b})`
    // Draw from bottom up
    canvasCtx.fillRect(x, height - barHeight, barWidth, barHeight)

    x += barWidth + 1 // Add 1px gap between bars
  }
}

// Watchers for live updating settings without needing a restart if already running
watch(fftSize, (newVal) => {
  if (analyser) {
    analyser.fftSize = parseInt(newVal, 10)
    dataArray = new Uint8Array(analyser.frequencyBinCount) // Recreate array with new size
  }
})

watch(smoothingTimeConstant, (newVal) => {
  if (analyser) {
    analyser.smoothingTimeConstant = newVal
  }
})

const handleResize = () => {
  if (canvasRef.value) {
    // Make canvas responsive to its container width
    const container = canvasRef.value.parentElement
    canvasRef.value.width = container.clientWidth
    canvasRef.value.height = 300 // Fixed height or could be dynamic
  }
}

onMounted(() => {
  handleResize()
  window.addEventListener('resize', handleResize)
})

onUnmounted(() => {
  stopAudio()
  window.removeEventListener('resize', handleResize)
})
</script>

<template>
  <div class="tool-container full-width">
    <div class="tool-panel">
      <div class="panel-header">
        <h2 class="tool-title">FFT Audio Analyzer</h2>
      </div>

      <div class="fft-layout">
        <div class="canvas-container">
          <canvas ref="canvasRef"></canvas>
          <div class="empty-state" v-if="!isListening && !hasError">
            <Activity size="48" class="pulse-icon" />
            <p>Click start to visualize your microphone audio</p>
          </div>
          <div class="error-state" v-if="hasError">
            <p class="error-msg">{{ errorMessage }}</p>
          </div>
        </div>

        <div class="controls-section">
          <!-- Main Action -->
          <div class="action-row">
            <button
              class="listen-btn"
              :class="{ 'is-active': isListening }"
              @click="toggleListen"
              v-ripple
            >
              <template v-if="!isListening">
                <Mic size="24" />
                <span>Start Listening</span>
              </template>
              <template v-else>
                <MicOff size="24" />
                <span>Stop Listening</span>
              </template>
            </button>
          </div>

          <!-- Settings -->
          <div class="settings-grid">
            <div class="control-group">
              <label>FFT Size (Resolution)</label>
              <select v-model="fftSize" class="select-input">
                <option v-for="opt in fftOptions" :key="opt.value" :value="opt.value">
                  {{ opt.label }}
                </option>
              </select>
            </div>

            <div class="control-group">
              <label>
                Smoothing
                <span class="value-readout">{{ smoothingTimeConstant.toFixed(2) }}</span>
              </label>
              <input
                type="range"
                class="slider"
                v-model.number="smoothingTimeConstant"
                min="0"
                max="0.99"
                step="0.01"
              />
            </div>
          </div>

        </div>
      </div>

    </div>
  </div>
</template>

<style scoped>
.fft-layout {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.canvas-container {
  width: 100%;
  height: 300px;
  background: rgba(0, 0, 0, 0.4);
  border: 1px solid var(--panel-border);
  border-radius: 12px;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: inset 0 2px 10px rgba(0, 0, 0, 0.5);
}

:global(:root[data-theme="light"]) .canvas-container {
  background: rgba(15, 23, 42, 0.05); /* Slight dark tint to make neon colors pop in light mode */
  border-color: rgba(15, 23, 42, 0.1);
  box-shadow: inset 0 2px 10px rgba(15, 23, 42, 0.05);
}

canvas {
  width: 100%;
  height: 100%;
  border-radius: 12px;
  /* Prevent interaction so clicks pass down if needed */
  pointer-events: none;
}

.empty-state {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  color: var(--text-muted);
}

.pulse-icon {
  opacity: 0.5;
  animation: idlePulse 3s ease-in-out infinite;
}

@keyframes idlePulse {
  0% { transform: scale(0.9); opacity: 0.3; }
  50% { transform: scale(1.1); opacity: 0.6; }
  100% { transform: scale(0.9); opacity: 0.3; }
}

.error-state {
  position: absolute;
  padding: 1rem;
  text-align: center;
  background: rgba(239, 68, 68, 0.1);
  border: 1px solid rgba(239, 68, 68, 0.3);
  border-radius: 8px;
  color: #ef4444;
}

.controls-section {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: 1.5rem;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: 12px;
}

@media (max-width: 640px) {
  .controls-section {
    background: transparent;
    border: none;
    padding: 0;
  }
}

.action-row {
  display: flex;
  justify-content: center;
}

.listen-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  background: var(--primary-accent);
  color: #fff;
  border: none;
  border-radius: 30px;
  padding: 1rem 3rem;
  font-size: 1.2rem;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.3s ease;
  box-shadow: 0 4px 15px rgba(var(--primary-accent-rgb), 0.3);
  width: 100%;
  max-width: 300px;
}

.listen-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(var(--primary-accent-rgb), 0.4);
}

.listen-btn.is-active {
  background: #ef4444; /* red for stop */
  box-shadow: 0 4px 15px rgba(239, 68, 68, 0.3);
}

.listen-btn.is-active:hover {
  box-shadow: 0 6px 20px rgba(239, 68, 68, 0.4);
}

.settings-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
}

@media (max-width: 640px) {
  .settings-grid {
    grid-template-columns: 1fr;
  }
}

.control-group {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.control-group label {
  font-weight: bold;
  color: var(--text-strong);
  display: flex;
  justify-content: space-between;
}

.value-readout {
  color: var(--primary-accent);
  font-family: monospace;
}

/* Slider Styles */
.slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 6px;
  background: var(--toggle-border);
  border-radius: 5px;
  outline: none;
  margin-top: 0.5rem;
}

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--primary-accent);
  cursor: pointer;
  transition: transform 0.1s;
  box-shadow: 0 0 10px var(--primary-accent);
}

.slider::-webkit-slider-thumb:hover {
  transform: scale(1.2);
}
</style>
