package com.birdsounds.identify


import android.graphics.Bitmap
import android.graphics.Color
import android.util.Log

import com.google.android.gms.wearable.MessageEvent
import com.google.android.gms.wearable.WearableListenerService
import com.theeasiestway.opus.Constants
import com.theeasiestway.opus.Opus
import decodeAACToPCM
import io.github.nailik.androidresampler.Resampler
import io.github.nailik.androidresampler.ResamplerConfiguration
import io.github.nailik.androidresampler.data.ResamplerChannel
import io.github.nailik.androidresampler.data.ResamplerQuality
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.time.Instant
import java.util.LinkedList
import java.util.Queue


var last_message_time = 0L;
fun ByteArray.toLong(): Long {
    require(size <= 8) { "ByteArray too large to fit in Long" }

    var result = 0L
    for (byte in this) {
        result = (result shl 8) or (byte.toLong() and 0xFF)
    }
    return result
}

fun ShortArray.chunked(size: Int): List<ShortArray> {
    return this.asList()
        .chunked(size)
        .map { it.toShortArray() }
}


fun FloatArray.chunked(size: Int): List<FloatArray> {
    return this.asList()
        .chunked(size)
        .map { it.toFloatArray() }
}


fun List<ShortArray>.mergeToShortArray(): ShortArray {
    // Calculate total size needed
    val totalSize = this.sumOf { it.size }

    // Create the output array
    val result = ShortArray(totalSize)

    // Copy each array into the result
    var position = 0
    for (array in this) {
        array.copyInto(result, position)
        position += array.size
    }

    return result
}


//var sound_buffer_48000hz = ShortArray(48000*10)  // Store last 10 seconds in buffer
//var sound_buffer_22050hz = ShortArray(22050*10)  // Store last 10 seconds in buffer

var sound_buffer_48000hz: Queue<ShortArray> = LinkedList<ShortArray>();
var sound_buffer_22050hz: Queue<ShortArray> = LinkedList<ShortArray>();

val configuration = ResamplerConfiguration(
    quality = ResamplerQuality.BEST,
    inputChannel = ResamplerChannel.MONO,
    inputSampleRate = 48000,
    outputChannel = ResamplerChannel.MONO,
    outputSampleRate = 22050
)
val resampler = Resampler(configuration);


fun getPixelValue(i: Int, is_invert: Boolean): Int {
    return if (is_invert) 255 - i else i
}

fun getPixelColor(i: Int, is_rgb: Boolean, is_invert: Boolean): Int {
    val pixelValue = getPixelValue(i, is_invert);
    if (is_rgb) {
        return toRgb(pixelValue);
    }
    return toGrayscale(pixelValue);
}

fun toGrayscale(i: Int): Int {
    return Color.rgb(i, i, i);
}

fun toRgb(i: Int): Int {
    return Color.argb(255, i, i, i);
}


class MessageListenerService : WearableListenerService() {


    //    fun placeSoundClassifier(soundClassifier: SoundClassifier)
    override fun onMessageReceived(p0: MessageEvent) {
        super.onMessageReceived(p0)
        val codec_opus = Opus()
        codec_opus.decoderInit(Constants.SampleRate._48000(), Constants.Channels.mono())
        Log.w(TAG, "Data recv: " + p0.data.size.toString() + " bytes")
        val soundclassifier = MainActivity.soundClassifier
        val soundClassifierSpectrogramBased = MainActivity.soundClassifierSpectrogramBased;

        if (soundclassifier == null) {
            Log.w(TAG, "Have invalid sound classifier")
            return
        } else {
            Log.w(TAG, "Have valid classifier")
        }

        if (soundClassifierSpectrogramBased == null) {
            Log.w(TAG, "Have invalid spectrogram classifier")
            return
        } else {
            Log.w(TAG, "Have valid spectrogram classifier")
        }


        var tstamp_bytes = p0.data.copyOfRange(0, Long.SIZE_BYTES)

        last_message_time = tstamp_bytes.toLong()


        var audio_bytes_og = p0.data.copyOfRange(Long.SIZE_BYTES, p0.data.size)


        val buffer = ByteBuffer.wrap(audio_bytes_og)
        val sound_a = ByteArrayOutputStream();
        val byteArrayList = mutableListOf<ByteArray>()
        while (buffer.hasRemaining()) {
            val num_to_read = buffer.get().toInt()
            val read_this = ByteArray(num_to_read)
            buffer.get(read_this)
            val decoded = codec_opus.decode(read_this, Constants.FrameSize._120())
            sound_a.write(decoded);
//            Log.e(TAG,"Decompressed ${read_this.size} to ${decoded?.size}")
        }
        codec_opus.decoderRelease();


        val audio_bytes = sound_a.toByteArray()
        val short_array = ShortArray(audio_bytes.size / 2)

        ByteBuffer.wrap(audio_bytes).order(
            ByteOrder.LITTLE_ENDIAN
        ).asShortBuffer().get(short_array)


        val audio_bytes_resampled = resampler.resample(audio_bytes)

        if (true) {
            val short_array_resampled = ShortArray(audio_bytes_resampled.size / 2)
            ByteBuffer.wrap(audio_bytes_resampled).order(
                ByteOrder.LITTLE_ENDIAN
            ).asShortBuffer().get(short_array_resampled)

            val float_array_resampled = short_array_resampled.map { it.toFloat() }.toFloatArray()
            val resampled_chunks: List<FloatArray> = float_array_resampled.chunked(512)


            val freq_results = mutableListOf<ShortArray>()
            for (seg in resampled_chunks) {
                val add_this: ShortArray = soundClassifierSpectrogramBased.generateSpectrogram(seg)
                freq_results.add(add_this);
//                val color_int: List<Int> = add_this.map { Color.rgb(it.toInt(), it.toInt(), it.toInt()) }
//                     val color_int: List<Int> = add_this.map { Color.argb(125,125, 200, 5) }
//                ret_val.add(color_int)
            }
            val total_points = freq_results.sumOf { it.size }
            val freq_array = ShortArray(total_points)
            var position = 0;
            for (arr in freq_results) {
                arr.copyInto(freq_array, position)
                position += arr.size
            }

            var bitmap_pixels = IntArray(freq_array.size);
            val createBitmap =
                Bitmap.createBitmap(resampled_chunks.size, 128, Bitmap.Config.ARGB_8888);

//            Log.d(TAG,"${freq_array.size.toString()} ${createBitmap.width.toString()} ${createBitmap.height.toString()}")

            val is_rgb = true;
            var is_invert = false;
            var height = createBitmap.height
            var i = 0
            for (i2 in 0 until height) {
                val width = createBitmap.getWidth();
                var i3 = 0;
                while (i3 < width) {
                    bitmap_pixels[i] =
                        getPixelColor(
                            freq_array[(createBitmap.height * i3) + i2].toInt(),
                            is_rgb,
                            is_invert
                        )
                    i3++;
                    i++;
                }
            }

            createBitmap.setPixels(
                bitmap_pixels,
                0,
                createBitmap.width,
                0,
                0,
                createBitmap.width,
                createBitmap.height
            );
            soundClassifierSpectrogramBased.run_interpreter(createBitmap)
        }
        //if (sound_buffer_48000hz.size >= 1) {
        var string_send: String = ""
        if (true) {

            if (true) {
                Log.e(TAG, "STARTING SCORING");


                val array_score = short_array;
                var sorted_list = soundclassifier.executeScoring(array_score)
                Log.w(TAG, "FINISHED SCORING");
                Log.w(TAG, "")
                val threshold = Settings.threshold_waveform / 100f
                for (i in 0 until 10) {
                    val score = sorted_list[i].value
                    if (score < threshold) {
                        continue
                    }
                    val index = sorted_list[i].index
                    val species_name = soundclassifier.labelList[index]
                    Log.w(TAG, species_name + ", " + score.toString())
                    string_send += species_name + " ~";
                    string_send += ','
                    string_send += score.toString()
                    string_send += ';'
                }
            }
                if (true) {
                string_send+=soundClassifierSpectrogramBased.score_string
            }


            Log.d(TAG, string_send)


            MessageSenderFromPhone.sendMessage(
                "/audio",
                tstamp_bytes + string_send.toByteArray(), b v
                this
            )
        }

        if (sound_buffer_22050hz.size >= 129) {

        }
    }

}