package barix

import codes.Somecodes.Companion.ValidString
import kotlinx.coroutines.*
import org.tinylog.Logger
import java.io.DataInputStream
import java.net.ServerSocket
import java.net.Socket
import java.nio.ByteBuffer
import java.util.function.Consumer

@Suppress("unused")
class TCP_Barix_Command_Server {
    lateinit var tcpserver: ServerSocket
    lateinit var job: Job
    private val socketMap = mutableMapOf<String, Socket>()

    private val regex = """STATUSBARIX;(\d+);(\d+);?(\d)?"""
    private val pattern = Regex(regex)

    /**
     * Start TCP Command Server
     * @param port The port number to listen on (default is 5001)
     * @param cb A callback function that will be called when a valid command is received
     * @return true if successful
     */
    fun StartTcpServer(port: Int = 5001, cb: Consumer<BarixStatus>): Boolean {
        try {
            val tcp = ServerSocket(port)
            tcpserver = tcp
            job = CoroutineScope(Dispatchers.IO).launch {
                Logger.info { "TCP StreamerOutput server started on port $port" }
                while (isActive) {
                    if (tcpserver.isClosed) break
                    try {

                        val socket = tcpserver.accept()
                        CoroutineScope(Dispatchers.IO).launch {
                            val key : String = socket.inetAddress.hostAddress
                            socketMap[key] = socket
                            Logger.info { "Start communicating with Streamer Output with IP : $key" }
                            try{

                                val din = DataInputStream(socket.getInputStream())
                                while (isActive) {
                                    val length = ByteArray(4)
                                    din.readFully(length)
                                    val readlength = ByteBuffer.wrap(length).getInt()
                                    //println("Read Length : $readlength")
                                    val bb = ByteArray(readlength)
                                    din.readFully(bb)
                                    // B4A format, 4 bytes di depan adalah size
                                    val str = String(bb)
                                    //println("Received from $key : $str")
                                    if (ValidString(str)) {
                                        // Valid command from Barix is in format $"STATUSBARIX;VU;BuffRemain;StatusData"$
                                        pattern.find(str)?.let { matchResult ->
                                            val (vu, buffremain, statusdata) = matchResult.destructured
                                            val status = BarixStatus(
                                                socket.inetAddress.hostAddress,
                                                vu.toInt(),
                                                buffremain.toInt(),
                                                statusdata.toIntOrNull() ?: 0
                                            )
                                            //Logger.info { "Received valid command from $key : $status" }
                                            cb.accept(status)
                                        } ?: run {
                                            Logger.warn { "Invalid command format from $key : $str" }
                                        }
                                    }

                                }
                            } catch (ex:Exception){
                                Logger.error { "Error in communication with Streamer Output with IP $key, Message : ${ex.message}" }
                            }

                            Logger.info { "Finished communicating with Streamer Output with IP $key" }
                            socketMap.remove(key)

                        }

                    } catch (ex: Exception) {
                        Logger.error { "Failed accepting TCP Socket, Message : ${ex.message}" }
                    }

                }
                Logger.info { "TCP server stopped" }
            }
            return true
        } catch (e: Exception) {
            Logger.error { "Failed to StartTcpServer, Message : ${e.message}" }
        }
        return false
    }

    /**
     * Stop TCP Command Server
     * @return true if succesful
     */
    fun StopTcpCommand(): Boolean {
        try {
            tcpserver.close()
            runBlocking {
                socketMap.values.forEach {
                    it.close()
                }
                socketMap.clear()
                job.join()
            }
            Logger.info { "StopTcpCommand success" }
            return true
        } catch (e: Exception) {
            Logger.error { "Failed to StopTcpServer, Message : ${e.message}" }
        }
        return false
    }

    /**
     * Get Socket by IP address
     * @param ip The IP address of the client
     * @return Socket if found, null otherwise
     */
    fun getSocket(ip: String): Socket? {
        return socketMap[ip]
    }


}