/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.

/**
 * @file The file declares several ffi functions and classes.
 */

package std.net

const SOCK_ADDR_IPV4_LEN: Int64 = 4
const SOCK_ADDR_IPV6_LEN: Int64 = 16
const SOCK_ADDR_DOMAIN_LEN: Int64 = 108
// XXX: TCP use this, while Unix/Udp should be 0xFFFF, but in write/send, we still use this to do checking.
const SOCK_READ_BUFFER_SIZE: Int32 = 4 * 1024
const SOCK_WRITE_BUFFER_SIZE: Int32 = 64 * 1024
const NULL_BYTE = "\0"
const IPV4_ADDR_LEN: Int64 = 16
const IPV6_ADDR_LEN: Int64 = 28
/**
 * CJ_SockCreate domain
 */
const AF_INET: Int16 = 2
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const AF_UNIX: Int16 = 1
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const AF_INET6: Int16 = 10
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const AF_NETLINK: Int16 = 16
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const AF_PACKET: Int16 = 17
@When[os == "Windows"]
const AF_UNIX: Int16 = 0
@When[os == "Windows"]
const AF_INET6: Int16 = 23
@When[os == "macOS" || os == "iOS"]
const AF_UNIX: Int16 = 1
@When[os == "macOS" || os == "iOS"]
const AF_INET6: Int16 = 30

/**
 * CJ_SockCreate type
 */
let SOCKET_STREAM: Int32 = 1
let SOCKET_DGRAM: Int32 = 2
let SOCKET_RAW: Int32 = 3
let SOCKET_SEQPACKET: Int32 = 5

/**
 * sock option level
 */
const IPPROTO_IP: Int32 = 0
const IPPROTO_ICMP: Int32 = 1
const IPPROTO_IPV4: Int32 = 4
const IPPROTO_TCP: Int32 = 6
const IPPROTO_UDP: Int32 = 17
const IPPROTO_IPV6: Int32 = 41
const IPPROTO_RAW: Int32 = 255
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOL_SOCKET: Int32 = 1
@When[os == "Windows" || os == "macOS" || os == "iOS"]
const SOL_SOCKET: Int32 = 0xFFFF

/**
 * sock option optname, If the value of an option is 0xFFFF, the option is not supported in the environment.
 */
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_DEBUG: Int32 = 0x0001
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_REUSEADDR: Int32 = 0x0002
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_ERROR: Int32 = 0x0004
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_DONTROUTE: Int32 = 0x0005
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_BROADCAST: Int32 = 0x0006
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_SNDBUF: Int32 = 0x0007
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_RCVBUF: Int32 = 0x0008
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_KEEPALIVE: Int32 = 0x0009
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_OOBINLINE: Int32 = 0x000A
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_LINGER: Int32 = 0x000D
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_REUSEPORT: Int32 = 0x000F
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_RCVTIMEO: Int32 = 0x0014
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_SNDTIMEO: Int32 = 0x0015
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_BINDTODEVICE: Int32 = 0x0019
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_ACCEPTCONN: Int32 = 0x001E
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_TCP_NODELAY: Int32 = 0x0001
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_TCP_QUICKACK: Int32 = 0x000C
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_TCP_KEEPIDLE: Int32 = 0x0004
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_TCP_KEEPINTVL: Int32 = 0x0005
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_TCP_KEEPCNT: Int32 = 0x0006
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_IP_TOS: Int32 = 0x0001
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_IP_TTL: Int32 = 0x0002
@When[os != "Windows" && os != "macOS" && os != "iOS"]
const SOCK_IP_HDRINCL: Int32 = 0x0003
@When[os == "Windows"]
const SOCK_DEBUG: Int32 = 0x0001
@When[os == "Windows"]
const SOCK_ACCEPTCONN: Int32 = 0x0002
@When[os == "Windows"]
const SOCK_REUSEADDR: Int32 = 0x0004
@When[os == "Windows"]
const SOCK_KEEPALIVE: Int32 = 0x0008
@When[os == "Windows"]
const SOCK_DONTROUTE: Int32 = 0x0010
@When[os == "Windows"]
const SOCK_BROADCAST: Int32 = 0x0020
@When[os == "Windows"]
const SOCK_OOBINLINE: Int32 = 0x0100
@When[os == "Windows"]
const SOCK_LINGER: Int32 = 0x0080
@When[os == "Windows"]
const SOCK_SNDBUF: Int32 = 0x1001
@When[os == "Windows"]
const SOCK_RCVBUF: Int32 = 0x1002
@When[os == "Windows"]
const SOCK_SNDTIMEO: Int32 = 0x1005
@When[os == "Windows"]
const SOCK_RCVTIMEO: Int32 = 0x1006
@When[os == "Windows"]
const SOCK_ERROR: Int32 = 0x1007
@When[os == "Windows"]
const SOCK_TYPE: Int32 = 0x1008
@When[os == "Windows"]
const SOCK_REUSEPORT: Int32 = 0xFFFF // Not on windows
@When[os == "Windows"]
const SOCK_BINDTODEVICE: Int32 = 0xFFFF // Not on windows

@When[os == "Windows"]
const SOCK_TCP_NODELAY: Int32 = 0x0001
@When[os == "Windows"]
const SOCK_TCP_KEEPIDLE: Int32 = 0x0003
@When[os == "Windows"]
const SOCK_TCP_KEEPCNT: Int32 = 0x0010
@When[os == "Windows"]
const SOCK_TCP_KEEPINTVL: Int32 = 0x0011
@When[os == "Windows"]
const SOCK_TCP_QUICKACK: Int32 = 0xFFFF // Not on windows

@When[os == "Windows"]
const SOCK_IP_HDRINCL: Int32 = 0x0002
@When[os == "Windows"]
const SOCK_IP_TOS: Int32 = 0x0003
@When[os == "Windows"]
const SOCK_IP_TTL: Int32 = 0x0004
@When[os == "macOS" || os == "iOS"]
const SOCK_DEBUG: Int32 = 0x0001
@When[os == "macOS" || os == "iOS"]
const SOCK_ACCEPTCONN: Int32 = 0x0002
@When[os == "macOS" || os == "iOS"]
const SOCK_REUSEADDR: Int32 = 0x0004
@When[os == "macOS" || os == "iOS"]
const SOCK_KEEPALIVE: Int32 = 0x0008
@When[os == "macOS" || os == "iOS"]
const SOCK_DONTROUTE: Int32 = 0x0010
@When[os == "macOS" || os == "iOS"]
const SOCK_BROADCAST: Int32 = 0x0020
@When[os == "macOS" || os == "iOS"]
const SOCK_OOBINLINE: Int32 = 0x0100
@When[os == "macOS" || os == "iOS"]
const SOCK_LINGER: Int32 = 0x0080
@When[os == "macOS" || os == "iOS"]
const SOCK_SNDBUF: Int32 = 0x1001
@When[os == "macOS" || os == "iOS"]
const SOCK_RCVBUF: Int32 = 0x1002
@When[os == "macOS" || os == "iOS"]
const SOCK_SNDTIMEO: Int32 = 0x1005
@When[os == "macOS" || os == "iOS"]
const SOCK_RCVTIMEO: Int32 = 0x1006
@When[os == "macOS" || os == "iOS"]
const SOCK_ERROR: Int32 = 0x1007
@When[os == "macOS" || os == "iOS"]
const SOCK_TYPE: Int32 = 0x1008
@When[os == "macOS" || os == "iOS"]
const SOCK_REUSEPORT: Int32 = 0x0200
@When[os == "macOS" || os == "iOS"]
const SOCK_BINDTODEVICE: Int32 = 0xFFFF // Not on macOS

@When[os == "macOS" || os == "iOS"]
const SOCK_TCP_NODELAY: Int32 = 0x0001
@When[os == "macOS" || os == "iOS"]
const SOCK_TCP_KEEPIDLE: Int32 = 0x0010
@When[os == "macOS" || os == "iOS"]
const SOCK_TCP_KEEPCNT: Int32 = 0x0102
@When[os == "macOS" || os == "iOS"]
const SOCK_TCP_KEEPINTVL: Int32 = 0x0101
@When[os == "macOS" || os == "iOS"]
const SOCK_TCP_QUICKACK: Int32 = 0xFFFF // Not on macOS

@When[os == "macOS" || os == "iOS"]
const SOCK_IP_HDRINCL: Int32 = 0x0002
@When[os == "macOS" || os == "iOS"]
const SOCK_IP_TOS: Int32 = 0x0003
@When[os == "macOS" || os == "iOS"]
const SOCK_IP_TTL: Int32 = 0x0004

/**
 * tcp socket default backlog
 */
const SOCKET_DEFAULT_BACKLOG: Int32 = 1024

/**
 * keep alive configuration struct
 */
@C
struct SockKeepAliveCfgS {
    /* Indicates whether to enable the keepalive function. The options are as follows: 0: disable 1: enable */
    var keep_alive: UInt32

    /* If no data is exchanged within the `idle` period (second), the probe is performed. */
    var idle: UInt32

    /* The interval for sending probe packets is `interval` (second). */
    var interval: UInt32

    /* Number of times probe packets are sent. If all times out, the connection is considered invalid. */
    var count: UInt32
    init(keep_alive!: UInt32, idle!: UInt32, interval!: UInt32, count!: UInt32) {
        this.keep_alive = keep_alive
        this.idle = idle
        this.interval = interval
        this.count = count
    }
}

foreign {
    func CJ_SockErrnoGet(): Int32

    func CJ_SockKeepAliveSet(sock: Int64, cfg: CPointer<SockKeepAliveCfgS>): Int32

    func CJ_SockOptionSet(sock: Int64, level: Int32, optname: Int32, optval: CPointer<Unit>, oplen: Int32): Int32

    func CJ_SockOptionGet(
        sock: Int64,
        level: Int32,
        optname: Int32,
        optval: CPointer<Unit>,
        oplen: CPointer<Int32>
    ): Int32

    func CJ_SOCKET_GetErrMessage(errno: Int32): CString

    func CJ_GetOptionInt(sock: Int64, level: Int32, optname: Int32): Int32

    func CJ_SetOptionInt(sock: Int64, level: Int32, optname: Int32, optval: Int32): Int32
    // raw socket
    func CJ_RawAddressCreate(arr: CPointer<Byte>, size: UInt32): CPointer<SockAddr>

    func CJ_RawAddressLenGet(addrPtr: CPointer<SockAddr>): UInt32

    func CJ_RawAddressDestroy(addrPtr: CPointer<SockAddr>): Unit
}

func getAddrPointer(addr: Array<Byte>): (CPointerHandle<Byte>, CPointer<SockAddr>) {
    unsafe {
        let handle = acquireArrayRawData(addr)
        let addrPtr = CJ_RawAddressCreate(handle.pointer, UInt32(addr.size))
        if (addrPtr.isNull()) {
            releaseArrayRawData(handle)
            throw SocketException("Failed to allocate memory.")
        }
        return (handle, addrPtr)
    }
}

func releaseAddrPointer(handle: CPointerHandle<Byte>, addrPtr: CPointer<SockAddr>): Unit {
    unsafe {
        releaseArrayRawData(handle)
        CJ_RawAddressDestroy(addrPtr)
    }
}