/*
 * 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.

package std.net

import std.io.*

/**
 * Represents a socket that works in streaming mode. It is a duplex stream having both read and write operations.
 *
 * A streaming socket could be bound and connected. The remote and local address could be determined via the corresponding properties.
 * Read and write operations can optionally have completion time limit.
 *
 * A streaming socket does send bytes sequentially (possibly batching by packets), the order or incoming and outgoing bytes is always considered
 * and kept. The actuall streaming socket implementation usually has buffers for reading and writing.
 *
 * The read function copies some incoming bytes to the specified buffer, the actual amount of bytes per invocation is generally unpredictable
 * and depends on many factors like the buffer size, the underlying buffer size, timing and the operating system and it's implementation.
 * Every time read() function is invoked, a batch of bytes (at least one byte) is copied and the bytes are remembered as read.
 * So the next read invocation can only receive remaining bytes. If there are no unprocessed incoming bytes yet, the read operation blocks until
 * any bytes arrives (or timeout limit exceeded).
 * The write operation works in a similar way: writing buffer does copy and schedule bytes for sending. If the underlying buffer capacity is not enough
 * or we are writing bytes faster then they are transferred, then the write operation may block until some buffer space get freed or the timeout limit exceeded.
 *
 * The order of bytes and batches/packets is always preserved in both read and write direction.
 * However, the timing and the number of batches is not guaranteed to remain the same during transferring, only the total number of bytes and the content.
 * For example, a peer sends 10 bytes and then 15 more bytes. The remote peer could receive these bytes in two pieces (10 then 15 bytes) or
 * it may receive all bytes at once (25 bytes) depending on timing, network strucuture and many other reasons.
 *
 * A streaming socket is usually (but not necessarily) connected to a stream of unknown/unpredictable size
 * so the length property inherited from InputStream returns -1 for such sockets.
 */
public interface StreamingSocket <: IOStream & Resource & ToString {
    /**
     * Local address the socket will be or currently is bound at.
     *
     * @throws SocketException is the socket is already closed
     * or no local address is available (local address was not provided during creation and the socket is not connected).
     */
    prop localAddress: SocketAddress

    /**
     * Remote address the socket will be or is currently connected to.
     *
     * @throws SocketException is the socket is already closed.
     */
    prop remoteAddress: SocketAddress

    /**
     * Read operation time limit or `None` for infinite read attempts.
     * The value specified here is actually the minimal amount of time before a read operation cancelled.
     * The actual time is not guaranteed but it will be never cancelled earlier than the specified timeout value.
     * If the duration is too big than it can be bumped to the infinite. When it's too small then if will be bumped to the minimal clock granularity.
     *
     * The default value is None.
     *
     * @throws IllegalArgumentException if the specified timeout duration is negative.
     */
    mut prop readTimeout: ?Duration

    /**
     * Write operation time limit or `None` for infinite read attempts.
     *
     * The value specified here is actually the minimal amount of time before a write operation cancelled.
     * The actual time is not guaranteed but it will be never cancelled earlier than the specified timeout value.
     * If the duration is too big than it can be bumped to the infinite. When it's too small then if will be bumped to the minimal clock granularity.
     *
     * The default value is None.
     * @throws IllegalArgumentException if the specified timeout duration is negative.
     */
    mut prop writeTimeout: ?Duration
}

/**
 * Represents a socket that handles datagrams in both input and output directions.
 *
 * A datagram is a bytes message of a finite size (possibly empty).
 * Depending on the actual datagram socket kind, there is usually a maxmimal datagram size. For example, a UDP socket can handle up to 64KiB at once.
 *
 * A datagram is transferred at once while the order or datagrams is not guaranteed, only the order and content of bytes inside.
 * Unlike streaming socket, datagrams may arrive in different order or not arrive at all.
 * This is because datagrams are routed independently and possibly via different actual routes.
 * Also, datagram size is always preserved (if possible, see receive/receiveFrom). For example, a peer sends datagrams of 10 and 15 bytes. Then,
 * the remote peer receives bytes in batches of the same size, 10 bytes and 15 bytes.
 */
public interface DatagramSocket <: Resource & ToString {
    /**
     * Local address the socket will be or currently is bound at.
     *
     * @throws SocketException is the socket is already closed
     * or no local address is available (local address was not provided during creation and the socket is not connected).
     */
    prop localAddress: SocketAddress

    /**
     * Remote address the socket is connected to or `None` if the socket is unconnected.
     *
     * @throws SocketException is the socket is already closed.
     */
    prop remoteAddress: ?SocketAddress

    /**
     * Receive/ReceiveFrom operation time limit or `None` for infinite read attempts.
     * The value specified here is actually the minimal amount of time before a read operation cancelled.
     * The actual time is not guaranteed but it will be never cancelled earlier than the specified timeout value.
     * If the duration is too big than it can be bumped to the infinite. When it's too small then if will be bumped to the minimal clock granularity
     *
     * The default value is None.
     * @throws IllegalArgumentException if the specified timeout duration is negative.
     */
    mut prop receiveTimeout: ?Duration

    /**
     * Send/SendTo operation time limit or `None` for infinite read attempts.
     *
     * The value specified here is actually the minimal amount of time before a write operation cancelled.
     * The actual time is not guaranteed but it will be never cancelled earlier than the specified timeout value.
     * If the duration is too big than it can be bumped to the infinite. When it's too small then if will be bumped to the minimal clock granularity.
     *
     * The default value is None.
     * @throws IllegalArgumentException if the specified timeout duration is negative.
     */
    mut prop sendTimeout: ?Duration

    /**
     * Receive the next datagram into the specified buffer waiting for data if needed.
     *
     * Returns a pair of the datagram sender address and the actual size of received datagram, possibly zero
     * or a value greater than the passed buffer size.
     *
     * Unlike read in streams, this function requires a buffer of proper size (big enough),
     * otherwise a datagram that is bigger than the provided buffer will be
     * truncated and the returned datagram size will be greater that the buffer size.
     *
     * @throws SocketException if buffer is empty or if it is not possible to read the data.
     * @throws SocketTimeoutException if reading time has expired
     */
    func receiveFrom(buffer: Array<Byte>): (SocketAddress, Int64)

    /**
     * Send datagram to the specified remote peer.
     *
     * It also may block in this function invocation if there is not enough
     * output buffer space available for some reason. Depending on the underlying
     * implementation, it may also silently discard a datagram in this case.
     */
    func sendTo(address: SocketAddress, payload: Array<Byte>): Unit
}

public interface ServerSocket <: Resource & ToString {
    /**
     * Local address the server socket will be or currently is bound at.
     *
     * @throws SocketException is the socket is already closed.
     */
    prop localAddress: SocketAddress

    /**
     * Bind a streaming socket. Depending on reuse flags and environment state,
     * it may fail if the local port/address/path is already occupied
     * or when there are connections remaining from the previously bound socket.
     * This function also does listen just after binding creating an incoming connections queue that could be accessed via "accept()" function.
     */
    func bind(): Unit

    /**
     * Accept a client socket, waiting for one if there are no pending connection requests.
     *
     * The OS implementation usually provides an incoming connection requests queue,
     * so calling accept() does takes a candidate from the queue
     * or wait until we get some request if the queue is empty.
     *
     * @throws SocketTimeoutException if the specified timeout ellapsed before any connection request were made.
     * @throws IllegalArgumentException if the specified timeout duration is negative.
     */
    func accept(timeout!: ?Duration): StreamingSocket

    /**
     * Accept a client socket, waiting for one if there are no pending connection requests.
     *
     * The OS implementation usually provides an incoming connection requests queue,
     * so calling accept() does takes a candidate from the queue
     * or wait until we get some request if the queue is empty.
     */
    func accept(): StreamingSocket {
        accept(timeout: None)
    }
}