package com.birdsounds.identify.presentation


import android.Manifest
import android.content.Context
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.util.Log
import androidx.annotation.RequiresPermission
import kotlinx.coroutines.suspendCancellableCoroutine
import java.nio.ByteBuffer
import java.time.Instant

/**
 * A helper class to provide methods to record audio input from the MIC to the internal storage.
 */
@Suppress("DEPRECATION")
class SoundRecorder(
    context_in: Context,
    outputFileName: String
) {

    private var state = State.IDLE
    private var context = context_in

    private enum class State {
        IDLE, RECORDING
    }


    @RequiresPermission(Manifest.permission.RECORD_AUDIO)
    suspend fun record() {


        suspendCancellableCoroutine<Unit> { cont ->
            var chunk_index: Int = 0
            val audioSource = MediaRecorder.AudioSource.DEFAULT
            val sampleRateInHz = 48000
            val channelConfig = AudioFormat.CHANNEL_IN_MONO
            val audioFormat = AudioFormat.ENCODING_PCM_16BIT
            val buffer_size =
                4 * AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat)
//            Log.w(TAG, buffer_size.toString())

            val bufferSizeInBytes =
                sampleRateInHz * 3 * 2 // 3 second sample, 2 bytes for each sample

            val chunk_size = 2 * sampleRateInHz / 4  // 250ms segments, 2 bytes for each sample
            val num_chunks: Int = bufferSizeInBytes / chunk_size
            val chunked_audio_bytes = Array(num_chunks) { ByteArray(chunk_size) }

            val audio_bytes_array = ByteArray(bufferSizeInBytes)
            val audioRecord = AudioRecord(
                /* audioSource = */ audioSource,
                /* sampleRateInHz = */ sampleRateInHz,
                /* channelConfig = */ channelConfig,
                /* audioFormat = */ audioFormat,
                /* bufferSizeInBytes = */ buffer_size
            )
            audioRecord.startRecording()


            val thread = Thread {
//                var sent_first: Boolean = false
                var ignore_warmup: Boolean = true

                var num_chunked_since_last_send = 0
                var last_tstamp: Long = Instant.now().toEpochMilli()
                var do_send_message: Boolean = false
                while (true) {
                    if (Thread.interrupted()) {
                        // check for the interrupted flag, reset it, and throw exception
                        Log.w(TAG, "Finished thread")
                        break
                    }
                    chunk_index = chunk_index.mod(num_chunks)
                    val out = audioRecord.read(
                        /* audioData = */ chunked_audio_bytes[chunk_index],
                        /* offsetInBytes = */ 0,
                        /* sizeInBytes = */ chunk_size,
                        /* readMode = */ AudioRecord.READ_BLOCKING
                    )
                    num_chunked_since_last_send += 1

                    do_send_message = false
                    if (num_chunked_since_last_send >= num_chunks) {
                        do_send_message = true
                        Log.w("MSG","sending message because full 3s have passed")
                    } else if ((last_tstamp in MessageSender.messageLog) && (num_chunked_since_last_send>4)) {
                        do_send_message = true
                        Log.w("MSG","Send message because the phone has finished")
                    } else if ((ignore_warmup) && (num_chunked_since_last_send > 2)) {
                        do_send_message = true
                        Log.w("MSG","Sent message because ignoring warmup")
                    }


                    chunk_index += 1
                    if ((do_send_message)) {
                        var tstamp: Long = Instant.now().toEpochMilli()
                        val tstamp_buffer = ByteBuffer.allocate(Long.SIZE_BYTES)
                        val tstamp_bytes = tstamp_buffer.putLong(tstamp).array()
                        var byte_send: ByteArray = ByteArray(0)
                        var strr: String = ""
                        for (i in 0..(num_chunks - 1)) {
                            var c_index = i + chunk_index
                            c_index = c_index.mod(num_chunks)
                            strr += c_index.toString()
                            strr += ' '
                            byte_send += chunked_audio_bytes[c_index]
                        }
//                        do_send_message = false;
                        num_chunked_since_last_send = 0
                        MessageSender.messageLog.clear()
                        MessageSender.sendMessage("/audio", tstamp_bytes + byte_send, context)
                        last_tstamp = tstamp

                    }
                }
            }
            thread.start()
//            thread.join();

            cont.invokeOnCancellation {
                thread.interrupt()
                audioRecord.stop()
                audioRecord.release()
                state = State.IDLE
            }
            state = State.IDLE
        }
    }

    companion object {
        private const val TAG = "SoundRecorder"
    }
}
