package com.birdsounds.identify

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Bundle
import android.util.Log
import android.widget.ImageView
import android.widget.SeekBar
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.wearable.ChannelClient
import com.google.android.gms.wearable.Wearable
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 kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.time.Instant

private var updateJob: Job? = null
private var updateCounter = 0
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

val Any.TAG: String
    get() {
        val tag = javaClass.simpleName
        return if (tag.length <= 23) tag else tag.substring(0, 23)
    }


class SynchronousDataStore(private val context: Context) {

    // Define keys
    companion object {
        val THRESHOLD_SPECTROGRAM_KEY = intPreferencesKey("THRESHOLD_SPECTROGRAM")
        val THRESHOLD_WAVEFORM_KEY = intPreferencesKey("THRESHOLD_WAVEFORM")
        val THRESHOLD_BIRDHEARD_KEY = intPreferencesKey("THRESHOLD_BIRD_HEARD")
    }

    fun saveThresholdSpectrogram(value: Int) {
        runBlocking {
            context.dataStore.edit { preferences ->
                preferences[THRESHOLD_SPECTROGRAM_KEY] = value;
            }
        }
    }

    fun getThresholdSpectrogram(): Int {

        return runBlocking {
            context.dataStore.data.first()[THRESHOLD_SPECTROGRAM_KEY] ?: 50
        }
    }


    // Synchronous write operations
    fun saveThresholdWaveform(value: Int) {
        runBlocking {
            context.dataStore.edit { preferences ->
                preferences[THRESHOLD_WAVEFORM_KEY] = value;
            }
        }
    }

    fun getThresholdWaveform(): Int {

        return runBlocking {
            context.dataStore.data.first()[THRESHOLD_WAVEFORM_KEY] ?: 50
        }
    }


    // Synchronous write operations
    fun saveBirdHeardThreshold(value: Int) {
        runBlocking {
            context.dataStore.edit { preferences ->
                preferences[THRESHOLD_BIRDHEARD_KEY] = value;
            }
        }
    }

    fun getBirdHeardThreshold(): Int {

        return runBlocking {
            context.dataStore.data.first()[THRESHOLD_BIRDHEARD_KEY] ?: 50
        }
    }

}


class MainActivity : AppCompatActivity() {
    val REQUEST_PERMISSIONS = 1337

    private lateinit var dataStore: SynchronousDataStore

    private lateinit var last_message_delay: TextView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        Wearable.getChannelClient(this.applicationContext)
            .registerChannelCallback(object : ChannelClient.ChannelCallback() {
                override fun onChannelOpened(channel: ChannelClient.Channel) {
                    super.onChannelOpened(channel)
                    Log.d(TAG, "onChannelOpened")
                }
            })
        Downloader(this).prepareModelFiles();
        requestPermissions()


        soundClassifier = SoundClassifier(this, SoundClassifier.Options())
        soundClassifierSpectrogramBased = SoundClassifierSpectrogramBased(this);

        Location.requestLocation(this, soundClassifier!!, soundClassifierSpectrogramBased!!);

        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets

        }


        last_message_delay = findViewById<TextView>(R.id.last_message_delay)
        dataStore = SynchronousDataStore(applicationContext)


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


        val thresholdText_birdsinging = findViewById<TextView>(R.id.threshold_value_text_birdheard)
        val seekBar_bird_singing = findViewById<SeekBar>(R.id.threshold_set_scale_bar_birdheard);
        Settings.threshold_bird_singing = dataStore.getBirdHeardThreshold();
        seekBar_bird_singing.progress = Settings.threshold_bird_singing
        thresholdText_birdsinging.text =
            String.format("%.2f", Settings.threshold_bird_singing / 100f)
        seekBar_bird_singing.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            @SuppressLint("DefaultLocale")
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {

                var actualValue = progress
                dataStore.saveBirdHeardThreshold(actualValue);
                Settings.threshold_bird_singing = actualValue;
                thresholdText_birdsinging.text = String.format("%.2f", actualValue / 100f)
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}

            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })


        val thresholdText_waveform = findViewById<TextView>(R.id.threshold_value_text_waveform)
        val seekBar_Waveform = findViewById<SeekBar>(R.id.threshold_set_scale_bar_waveform);
        Settings.threshold_waveform = dataStore.getThresholdWaveform();
        seekBar_Waveform.progress = Settings.threshold_waveform
        thresholdText_waveform.text = String.format("%.2f", Settings.threshold_waveform / 100f)
        seekBar_Waveform.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            @SuppressLint("DefaultLocale")
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {

                var actualValue = progress
                dataStore.saveThresholdWaveform(actualValue);
                Settings.threshold_waveform = actualValue;
                thresholdText_waveform.text = String.format("%.2f", actualValue / 100f)
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}

            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })


        val thresholdText_spectrogram =
            findViewById<TextView>(R.id.threshold_value_text_spectrogram)
        val seekBar_Spectrogram = findViewById<SeekBar>(R.id.threshold_set_scale_bar_spectrogram);
        Settings.threshold_spectrogram = dataStore.getThresholdSpectrogram();
        seekBar_Spectrogram.progress = Settings.threshold_spectrogram
        thresholdText_spectrogram.text =
            String.format("%.2f", Settings.threshold_spectrogram / 100f)
        seekBar_Spectrogram.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            @SuppressLint("DefaultLocale")
            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {

                var actualValue = progress
                dataStore.saveThresholdSpectrogram(actualValue);
                Settings.threshold_spectrogram = actualValue;
                thresholdText_spectrogram.text = String.format("%.2f", actualValue / 100f)
            }

            override fun onStartTrackingTouch(seekBar: SeekBar) {}

            override fun onStopTrackingTouch(seekBar: SeekBar) {}
        })

    }

    fun set_image_view(bitmap: Bitmap) {
        val imageView = this.findViewById<ImageView>(R.id.imageView)
        imageView.setImageBitmap(bitmap)
    }

    companion object {

        var soundClassifier: SoundClassifier? = null
        var soundClassifierSpectrogramBased: SoundClassifierSpectrogramBased? = null

        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 resampler: Resampler? = null

    }


    @SuppressLint("SetTextI18n")
    override fun onResume() {
        super.onResume()
        // Start periodic updates using coroutines
        updateJob = lifecycleScope.launch {
            while (isActive) { // isActive is a property of the coroutine scope
                updateCounter++
                if (last_message_time == 0.toLong()) {
                    last_message_delay.text = "No messages received"
                } else {
                    last_message_delay.text = "Last message: ${
                        (Instant.now().toEpochMilli() - last_message_time) / 1000F.toInt()
                    } seconds ago"
                }

//                last_message_delay.text = "Update count: $updateCounter"
                delay(500) // Update every 1 second
            }
        }
    }

    override fun onPause() {
        super.onPause()
        // Cancel the coroutine when activity is not visible
        updateJob?.cancel()
    }

    private fun requestPermissions() {
        val perms = mutableListOf<String>()

        if (ContextCompat.checkSelfPermission(
                this, Manifest.permission.ACCESS_COARSE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            perms.add(Manifest.permission.ACCESS_COARSE_LOCATION)
            perms.add(Manifest.permission.ACCESS_FINE_LOCATION)
        }
        if (perms.isNotEmpty()) requestPermissions(perms.toTypedArray(), REQUEST_PERMISSIONS)
    }
}