import audio.AudioFilePlayer
import audio.AudioUtility
import audio.OpusStreamReceiver
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import somecodes.Codes.Companion.ValidString
import somecodes.configFile
import web.WsReply
import web.webApp
import zello.ZelloClient
import zello.ZelloEvent
import javafx.util.Pair
import org.slf4j.LoggerFactory
import somecodes.Codes.Companion.ValidFile
import web.WsCommand
import java.util.function.BiFunction

//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
fun main() {
    val logger = LoggerFactory.getLogger("Main")
    val objectMapper = ObjectMapper()

    val cfg = configFile()
    cfg.Load()
    val au = AudioUtility()
    var audioID = 0
    val preferedAudioDevice = "Speakers"
    au.DetectPlaybackDevices().forEach { pair ->
        println("Device ID: ${pair.first}, Name: ${pair.second}")
        if (pair.second.contains(preferedAudioDevice)) {
            audioID = pair.first
        }
    }
    if (audioID!=0){
        val initsuccess = au.InitDevice(audioID)
        println("Audio Device $audioID initialized: $initsuccess")
    }

    val o = OpusStreamReceiver(audioID)
    var afp: AudioFilePlayer? = null

    val w = webApp("0.0.0.0",3030, BiFunction {
            source: String, cmd: WsCommand ->
        when (source) {
            "setting" -> when(cmd.command){
                "getConfig" ->{
                    logger.info("Get Config")
                    WsReply(cmd.command,objectMapper.writeValueAsString(cfg) )
                }
                "setZelloConfig" -> {
                    try{
                        val xx = objectMapper.readValue(cmd.data, object: TypeReference<Map<String, String>>() {})
                        cfg.ZelloUsername = xx["ZelloUsername"]
                        cfg.ZelloPassword = xx["ZelloPassword"]
                        cfg.ZelloChannel = xx["ZelloChannel"]
                        cfg.ZelloServer = xx["ZelloServer"]
                        cfg.ZelloWorkNetworkName = xx["ZelloWorkNetworkName"]
                        cfg.ZelloEnterpriseServerDomain = xx["ZelloEnterpriseServerDomain"]

                        WsReply(cmd.command,"success")
                    } catch (e: Exception){
                        WsReply(cmd.command,"failed: ${e.message}")
                    }

                }
                "setMessageConfig"-> {
                    try{

                        val xx = objectMapper.readValue(cmd.data, object : TypeReference<Map<String, String>>() {})
                        cfg.M1 = xx["M1"]
                        cfg.M2 = xx["M2"]
                        cfg.M3 = xx["M3"]
                        cfg.M4 = xx["M4"]
                        cfg.M5 = xx["M5"]
                        cfg.M6 = xx["M6"]
                        cfg.M7 = xx["M7"]
                        cfg.M8 = xx["M8"]
                        WsReply(cmd.command,"success")
                    } catch (e: Exception){
                        WsReply(cmd.command,"failed: ${e.message}")
                    }

                }
                else -> WsReply(cmd.command,"Invalid command: ${cmd.command}")
            }

            "prerecordedbroadcast" -> when(cmd.command){
                "getMessageConfig" ->{
                    val data = mapOf(
                        "M1" to cfg.M1,
                        "M2" to cfg.M2,
                        "M3" to cfg.M3,
                        "M4" to cfg.M4,
                        "M5" to cfg.M5,
                        "M6" to cfg.M6,
                        "M7" to cfg.M7,
                        "M8" to cfg.M8
                    )
                    WsReply(cmd.command, objectMapper.writeValueAsString(data))
                }
                "getPlaybackStatus" ->{
                    if (afp!=null && true==afp?.isPlaying){
                        WsReply(cmd.command, "Playing: ${afp?.filename}")
                    } else {
                        WsReply(cmd.command, "Idle")
                    }
                }
                "playMessage" ->{
                    val filename = when(cmd.data){
                        "M1" -> cfg.M1
                        "M2" -> cfg.M2
                        "M3" -> cfg.M3
                        "M4" -> cfg.M4
                        "M5" -> cfg.M5
                        "M6" -> cfg.M6
                        "M7" -> cfg.M7
                        "M8" -> cfg.M8
                        else -> {
                            null
                        }
                    }
                    if (ValidFile(filename)){
                        try{
                            val player= AudioFilePlayer(audioID, filename)
                            player.Play { cb -> afp = null}
                            afp = player
                            WsReply(cmd.command,"success")

                        } catch (e: Exception){
                            WsReply(cmd.command, "failed: ${e.message}")
                        }
                    } else WsReply(cmd.command,"Invalid file : $filename")



                }
                "stopMessage" ->{
                    afp?.Stop()
                    afp = null
                    WsReply(cmd.command,"success")
                }
                else -> WsReply(cmd.command,"Invalid command: ${cmd.command}")
            }
            "pocreceiver" -> when(cmd.command){
                else -> WsReply(cmd.command,"Invalid command: ${cmd.command}")
            }
            else -> WsReply(cmd.command,"Invalid source: $source")
        }

    } , Pair("admin","admin1234"))
    w.Start()

    val z = ZelloClient.fromConsumerZello("gtcdevice01","GtcDev2025")
    z.Start(object : ZelloEvent {
        override fun onChannelStatus(
            channel: String,
            status: String,
            userOnline: Int,
            error: String?,
            errorType: String?
        ) {
            println("Channel Status: $channel is $status with $userOnline users online.")
            if (ValidString(error) && ValidString(errorType)) {
                println("Error: $error, Type: $errorType")
            }
        }

        override fun onAudioData(streamID: Int, from: String, For: String, channel: String, data: ByteArray) {
            println("Audio Data received from $from for $For on channel $channel with streamID $streamID ")
        }

        override fun onThumbnailImage(imageID: Int, from: String, For: String, channel: String, data: ByteArray, timestamp: Long) {
            println("Thumbnail Image received from $from for $For on channel $channel with imageID $imageID at timestamp $timestamp")
        }

        override fun onFullImage(imageID: Int, from: String, For: String, channel: String, data: ByteArray, timestamp: Long) {
            println("Full Image received from $from for $For on channel $channel with imageID $imageID at timestamp $timestamp")
        }

        override fun onTextMessage(messageID: Int, from: String, For: String, channel: String, text: String, timestamp: Long) {
            println("Text Message received from $from for $For on channel $channel with messageID $messageID at timestamp $timestamp. Text: $text")
        }

        override fun onLocation(messageID: Int, from: String, For: String, channel: String, latitude: Double, longitude: Double, address: String, accuracy: Double, timestamp: Long) {
            println("Location received from $from for $For on channel $channel with messageID $messageID at timestamp $timestamp. Location($latitude,$longitude), Address:$address, Accuracy:$accuracy")
        }

        override fun onConnected() {
            println("Connected to Zello server.")
        }

        override fun onDisconnected() {
            println("Disconnected from Zello server.")
        }

        override fun onError(errorMessage: String) {
            println("Error occurred in Zello client: $errorMessage")
        }

        override fun onStartStreaming(from: String, For: String, channel: String) {
            if (o.Start()){
                println("Opus Receiver ready for streaming from $from for $For on channel $channel")
            } else {
                println("Failed to start Opus Receiver for streaming from $from for $For on channel $channel")
            }
        }

        override fun onStopStreaming(from: String, For: String, channel: String) {
            o.Stop()
            println("Opus Receiver stopped streaming from $from for $For on channel $channel")
        }

        override fun onStreamingData(
            from: String,
            For: String,
            channel: String,
            data: ByteArray
        ) {
            if (o.isPlaying) o.PushData(data)
        }
    })

}


