package dev.adriankuta.visualizer.domain.processors

import androidx.compose.ui.graphics.Color
import dev.adriankuta.visualizer.data.FftFrame
import dev.adriankuta.visualizer.data.VisualizerFrame
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import javax.inject.Inject
import kotlin.math.sqrt
import kotlin.reflect.KClass

class FftToColor @Inject constructor() : VisualizerProcessor {

    override suspend fun invoke(frame: VisualizerFrame): Color = withContext(Dispatchers.Default) {
        // --- Extract raw FFT bytes from your frame ---
        // Adjust the property name if your FftFrame differs (e.g., `frame.data`, `frame.bytes`, etc.)
        val fft: ByteArray = frame.data

        if (fft.isEmpty()) return@withContext Color.Black

        // Visualizer FFT format: interleaved [re0, im0, re1, im1, ...]
        // Skip DC (bin 0: re0, im0) as it often behaves like noise/offset.
        var sumMagnitude = 0.0
        var bins = 0

        var i = 2 // start from bin 1 (skip DC)
        while (i + 1 < fft.size) {
            val re = fft[i].toInt()
            val im = fft[i + 1].toInt()
            val mag = sqrt((re * re + im * im).toDouble())
            sumMagnitude += mag
            bins++
            i += 2
        }

        val avgMagnitude = if (bins > 0) sumMagnitude / bins else 0.0

        // Normalize ~[0..1]. Each component is in [-128..127], so magnitude upper bound ~181.
        // 128 is a practical normalization for stable visuals; then clamp.
        val normalized = (avgMagnitude / 128.0).coerceIn(0.0, 1.0)

        // Perceptual easing to avoid flicker and give more dynamic range at lower levels.
        val eased = sqrt(normalized).toFloat()

        // Simple noise gate so tiny ambient levels don’t glow.
        val value = if (eased < NOISE_FLOOR) 0f else eased

        // Single, stable color – only brightness changes with the music.
        Color.hsv(BASE_HUE, BASE_SATURATION, value)
    }

    private companion object {
        // Tune these to your taste (degrees 0..360, 0..1).
        const val BASE_HUE = 0f          // cool blue
        const val BASE_SATURATION = 0.85f  // vivid
        const val NOISE_FLOOR = 0.05f      // ignore very low energy
    }
}
