// SPDX-FileCopyrightText: 2026 Reactorcoremeltdown
// SPDX-License-Identifier: GPL-3.0-or-later

package space.rcmd.android.sizzle.audio

import android.annotation.SuppressLint
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import kotlin.concurrent.thread

/**
 * Ad-hoc sample recorder. Captures audio from the microphone (or a connected USB
 * audio input, which the platform routes to the default source) into a mono float
 * buffer that becomes a [SampleStore.Sample].
 *
 * Requires the RECORD_AUDIO permission (requested at app launch). Recording runs
 * on its own thread so it never blocks the UI or the playback engine.
 */
class SampleRecorder(private val sampleRate: Int = 44_100) {

    @Volatile private var recording = false
    private var worker: Thread? = null
    private val chunks = ArrayList<FloatArray>()

    /** Preferred capture device (e.g. a USB audio input), or null for the default. */
    var preferredInput: android.media.AudioDeviceInfo? = null

    val isRecording: Boolean get() = recording

    @SuppressLint("MissingPermission") // caller ensures RECORD_AUDIO is granted
    fun start(): Boolean {
        if (recording) return true
        val minBuf = AudioRecord.getMinBufferSize(
            sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_FLOAT,
        )
        if (minBuf <= 0) return false
        val record = AudioRecord(
            MediaRecorder.AudioSource.MIC,
            sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_FLOAT,
            minBuf * 2,
        )
        if (record.state != AudioRecord.STATE_INITIALIZED) { record.release(); return false }
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
            record.setPreferredDevice(preferredInput)
        }

        chunks.clear()
        recording = true
        record.startRecording()
        worker = thread(name = "sizzle-rec") {
            val buf = FloatArray(minBuf)
            while (recording) {
                val n = record.read(buf, 0, buf.size, AudioRecord.READ_BLOCKING)
                if (n > 0) chunks.add(buf.copyOf(n))
                // Safety cap: stop after ~30 s to avoid unbounded memory.
                if (chunks.sumOf { it.size } > sampleRate * 30) recording = false
            }
            record.stop()
            record.release()
        }
        return true
    }

    /** Stop and return the recorded audio (null if nothing was captured). */
    fun stop(): SampleStore.Sample? {
        if (!recording && worker == null) return null
        recording = false
        worker?.join(1000)
        worker = null
        val total = chunks.sumOf { it.size }
        if (total == 0) return null
        val out = FloatArray(total)
        var pos = 0
        for (c in chunks) { c.copyInto(out, pos); pos += c.size }
        chunks.clear()
        return SampleStore.Sample(out, sampleRate)
    }
}
