package com.jihe.punchnet.sdlan.network

import com.google.protobuf.ByteString
import com.jihe.punchnet.sdlan.config.SDLanConfig
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import java.util.concurrent.atomic.AtomicLong

typealias Mac = ByteString
// typealias PeerMap = ConcurrentMap<Mac, EdgePeer>

class PeerMap private constructor (val inner: ConcurrentMap<Mac, EdgePeer>): ConcurrentMap<Mac, EdgePeer> by inner {
    constructor(): this(ConcurrentHashMap<Mac, EdgePeer>())
}


class IPSubnet(var netAddr: Int, var netBitLen: Byte) {
}

enum class NatType(val value: UByte) {
    Blocked(0u),
    NoNat(1u),
    FullCone(2u),
    PortRestricted(3u),
    ConeRestrict(4u),
    Symmetric(5u),
    Invalid(0xffu);

    companion object {
        fun fromUByte(code: UByte): NatType {
            return when(code) {
                0.toUByte() -> Blocked
                1.toUByte() -> NoNat
                2.toUByte() -> FullCone
                3.toUByte() -> PortRestricted
                4.toUByte() -> ConeRestrict
                5.toUByte() -> Symmetric
                else -> Invalid
            }
        }
    }
}

fun NatType.toByte(): UByte {
    return value
}

enum class IPFamily {
    IPV4, IPV6
}

class SDLanSock constructor(var family: IPFamily, var port: Int, var ip: ByteArray) {
    override fun toString(): String {
        when(family) {
            IPFamily.IPV6 -> {
                assert(ip.size == 16)
                val ipstr = ip.map {
                    it.toString(16)
                }.joinToString(":")
                return "[$ipstr]:${port}"
            }
            IPFamily.IPV4 -> {
                assert(ip.size == 4)
                val digit = ip[0]
                return "${ip[0].toUByte()}.${ip[1].toUByte()}.${ip[2].toUByte()}.${ip[3].toUByte()}:${port}"
            }
        }
    }

    fun is_equal(other: SDLanSock): Boolean {
        return (family==other.family)
                && (port == other.port)
                && (ip.contentEquals(other.ip))
    }
}



class EdgePeer private constructor (
    var devAddress: IPSubnet,
    var natType: NatType,
    var sock: SDLanSock,
    var ipv6Info: SDLanSock?,
    // timeout of the family
    val timeout: Byte,

    // 最近一次与该edge通信
    val lastSeen: AtomicLong,
    // 最近一次与该edge有P2P通信
    val lastP2P: AtomicLong,
    // 最近一次向服务器查询该edge信息
    val lastSentQuery: AtomicLong,
) {
    // var dev_addr: IPSubnet = IPSubnet(0, 0)

    constructor(netAddress: Int, netBitLen: Byte, sock: SDLanSock, v6Info: SDLanSock?, now: Long): this(
        IPSubnet(netAddress, netBitLen),
        NatType.Blocked,
        sock,
        v6Info,
        SDLanConfig.RegisterSuperInterval,
        AtomicLong(now),
        AtomicLong(now),
        AtomicLong(now),
    ) {
    }
}