package com.radiola.service

import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.random.Random

/** Тип звука для засыпания (процедурная генерация цветного шума). */
enum class SleepSound(val key: String, val title: String) {
    WHITE("white", "Белый шум"),
    PINK("pink", "Розовый шум"),
    BROWN("brown", "Коричневый шум");

    companion object {
        fun fromKey(key: String?): SleepSound? = entries.firstOrNull { it.key == key }
    }
}

/**
 * Проигрыватель цветного шума для засыпания. Генерирует PCM на отдельном потоке и
 * пишет в [AudioTrack] (streaming). Громкость регулируется на лету (для fade/кроссфейда).
 * Розовый — фильтр Пола Келлета, коричневый — интегрированный белый (random walk).
 */
@Singleton
class SleepSoundPlayer @Inject constructor() {

    private val sampleRate = 44100
    private val bufSize = AudioTrack.getMinBufferSize(
        sampleRate,
        AudioFormat.CHANNEL_OUT_MONO,
        AudioFormat.ENCODING_PCM_16BIT,
    ).coerceAtLeast(4096)

    @Volatile private var track: AudioTrack? = null
    @Volatile private var thread: Thread? = null
    @Volatile private var running = false

    /** Запустить генерацию шума [sound]. Стартовая громкость 0 — нарастает кроссфейдом. */
    @Synchronized
    fun start(sound: SleepSound) {
        stop()
        val at = AudioTrack.Builder()
            .setAudioAttributes(
                AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build(),
            )
            .setAudioFormat(
                AudioFormat.Builder()
                    .setSampleRate(sampleRate)
                    .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                    .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                    .build(),
            )
            .setBufferSizeInBytes(bufSize * 2)
            .setTransferMode(AudioTrack.MODE_STREAM)
            .build()
        at.setVolume(0f)
        at.play()
        track = at
        running = true
        thread = Thread { generate(at, sound) }.apply { priority = Thread.MIN_PRIORITY; start() }
    }

    /** Громкость 0..1 (для плавного появления/затухания). */
    fun setVolume(v: Float) {
        track?.setVolume(v.coerceIn(0f, 1f))
    }

    @Synchronized
    fun stop() {
        running = false
        thread?.let { runCatching { it.join(300) } }
        thread = null
        track?.let { t ->
            runCatching { t.stop() }
            runCatching { t.release() }
        }
        track = null
    }

    private fun generate(at: AudioTrack, sound: SleepSound) {
        val n = 2048
        val buf = ShortArray(n)
        // Состояние фильтров розового шума (Пол Келлет)
        var b0 = 0f; var b1 = 0f; var b2 = 0f; var b3 = 0f; var b4 = 0f; var b5 = 0f; var b6 = 0f
        var lastBrown = 0f
        while (running) {
            for (i in 0 until n) {
                val white = Random.nextFloat() * 2f - 1f
                val sample = when (sound) {
                    SleepSound.WHITE -> white * 0.35f
                    SleepSound.PINK -> {
                        b0 = 0.99886f * b0 + white * 0.0555179f
                        b1 = 0.99332f * b1 + white * 0.0750759f
                        b2 = 0.96900f * b2 + white * 0.1538520f
                        b3 = 0.86650f * b3 + white * 0.3104856f
                        b4 = 0.55000f * b4 + white * 0.5329522f
                        b5 = -0.7616f * b5 - white * 0.0168980f
                        val pink = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362f
                        b6 = white * 0.115926f
                        pink * 0.11f
                    }
                    SleepSound.BROWN -> {
                        val brown = (lastBrown + 0.02f * white) / 1.02f
                        lastBrown = brown
                        brown * 3.5f
                    }
                }
                buf[i] = (sample.coerceIn(-1f, 1f) * Short.MAX_VALUE).toInt().toShort()
            }
            val written = at.write(buf, 0, n)
            if (written < 0) break
        }
    }
}
