package com.jihe.punchnet.sdlan.network

import com.jihe.punchnet.sdlan.logs.TerminalLogger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ReceiveChannel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.Inet4Address
import java.net.SocketAddress

class SDLanSocket(val scope: CoroutineScope, val addr: String, val port: Int, val reuseAddress: Boolean = false) {
    private val connection: DatagramSocket = run {
        val sock = DatagramSocket(port, Inet4Address.getByName(addr))
        if (reuseAddress) {
            sock.reuseAddress = true
        }
        sock
    }

    private var job: Job? = null

    fun loop(): ReceiveChannel<DatagramPacket> {
        val channel = Channel<DatagramPacket>(100)
        scope.launch {
            while (true) {
                val msg = receive()
                channel.send(msg)
            }

        }
        return channel
    }

    suspend fun send_to(content: ByteArray, target: SocketAddress) {
        val packet = DatagramPacket(content, content.size, target)
        try {
            withContext(Dispatchers.IO) {
                connection.send(packet)
            }
        } catch(e: Exception) {
            TerminalLogger.errorf {"Failed to send to: $e"}
        }
    }

    suspend fun receive(): DatagramPacket {
        val buffer = ByteArray(1500)
        val packet = DatagramPacket(buffer, buffer.size)

        withContext(Dispatchers.IO) {
            connection.receive(packet)
        }
        return packet
    }

    suspend fun close() {
        job?.cancelAndJoin()
        connection.close()
    }
}
