package audio

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.tinylog.Logger
import java.net.ServerSocket
import java.util.function.Consumer

/**
 * TCPReceiver is a class that listens for TCP connections on a specified port.
 * for receiving PCMFILE from Android SAMI
 */
class TCPReceiver(val portnumber: Int = 5002){
    private lateinit var server: ServerSocket
    private var isRunning = false
    private val dataCallback = mutableMapOf<String, Consumer<ByteArray>>()
    private val isfinishd = mutableMapOf<String, Boolean>()

    /**
     * Start listening for TCP connections on the specified port.
     * @return true if successful, false otherwise
     */
    fun Start() : Boolean{
        try{
            server = ServerSocket(portnumber)
            isRunning = true
            Logger.info { "Server started at port $portnumber" }
            CoroutineScope(Dispatchers.IO).launch {
                while(isRunning){
                    try {
                        val client = server.accept()
                        val clientAddress = client.inetAddress.hostAddress
                        CoroutineScope(Dispatchers.IO).launch {

                            isfinishd[clientAddress] = false
                            var totalbytes = 0L
                            try{
                                val din = client.getInputStream()
                                Logger.info{ "Start receiving PCMFILE from Android with IP=${clientAddress}" }

                                do {
                                    val buffer = ByteArray(16384)
                                    val bytesRead = din.read(buffer)
                                    if (bytesRead>0){
                                        val data = ByteArray(bytesRead)
                                        System.arraycopy(buffer, 0, data, 0, bytesRead)
                                        //println("Received $bytesRead bytes from $clientAddress")
                                        totalbytes+=bytesRead
                                        dataCallback[clientAddress].let {
                                            it?.accept(data)
                                        }
                                    }
                                } while (bytesRead > 0)
                            } catch (e : Exception){
                                Logger.error { "Failed receiving data from $clientAddress, Message : ${e.message}" }
                            }
                            Logger.info { "Connection from $clientAddress ended, total bytesRead=$totalbytes" }
                            isfinishd[clientAddress] = true
                        }
                    } catch (e: Exception) {
                        Logger.error { "Failed to accept socket, Message : ${e.message}" }
                    }
                }
            }
            return true
        } catch (e : Exception){
            Logger.error { "Failed to Start Server at port $portnumber, Message : ${e.message}" }
            return false
        }
    }

    fun RequestDataFrom(ipaddress: String, cb: Consumer<ByteArray>){
        dataCallback[ipaddress] = cb
    }

    fun StopRequestDataFrom(ipaddress: String){
        if (isfinishd[ipaddress] != null){
            if (isfinishd[ipaddress]==false){
                // belum selesai
                //println("Waiting for receiving from $ipaddress to finish...")
                runBlocking {
                    while (isfinishd[ipaddress] == false){
                        kotlinx.coroutines.delay(100)
                    }
                }
            }
            //println("Removing callback for $ipaddress")
            dataCallback.remove(ipaddress)
        }

    }

    /**
     * Stop listening for TCP connections and close the server socket.
     */
    fun Stop(){
        if (isRunning){
            isRunning = false
            server.close()
        }
    }
}
