package commonsNet

import std.net.UdpSocket
import std.net.IPSocketAddress

public abstract class DatagramSocketClient {

    //所有 DatagramSocketClient 实例共享的默认 DatagramSocketFactory
    private static let DEFAULT_SOCKET_FACTORY: DefaultDatagramSocketFactory = DefaultDatagramSocketFactory.DEFAULT;

    /** 默认超时 */
    protected var _timeout_: Int64

    /** 用于连接的数据报套接字 */
    protected var _socket_: ?UdpSocket

    /** 客户端的套接字当前是否处于打开状态 */
    protected var _isOpen_: Bool

    /** 数据报套接字的 DatagramSocketFactory */
    protected var _socketFactory_: DatagramSocketFactory

    /**
     * 无参构造函数
     * _socket_=None;
     * _timeout_=0;
     * _isOpen_=false;
     * _socketFactory_= DEFAULT_SOCKET_FACTORY;
     */
    public init() {
        this._socket_ = None
        this._timeout_ = 0
        this._isOpen_ = false
        this._socketFactory_ = DEFAULT_SOCKET_FACTORY
    }

    /** 属性readTimeout,用于设置和获取readTimeout */
    public mut prop receiveTimeout: ?Duration {
        get() {
            this._socket_.getOrThrow().receiveTimeout
        }
        set(value) {
            this._socket_.getOrThrow().receiveTimeout = value
        }
    }

    /** 属性sendTimeout,用于设置和获取sendTimeout */
    public mut prop sendTimeout: ?Duration {
        get() {
            this._socket_.getOrThrow().sendTimeout
        }
        set(value) {
            this._socket_.getOrThrow().sendTimeout = value
        }
    }

    /**
     * 得到非空的UdpSocket或者抛出异常
     */
    protected func checkOpen(): UdpSocket {
        return this._socket_.getOrThrow {NoneValueException("DatagramSocket is None.")}
    }

    /**
     * 用于关闭套接字
     *
     * 应该在类实例使用完后以及再次调用{@link #open open() } 之前调用此方法
     * _isOpen_ 设置为false
     * _socket_ 设置为none
     */
    public func close() {
        if (let Some(s) <- this._socket_) {
            s.close();
        }

        this._socket_ = None
        this._isOpen_ = false
    }

    /**
     * 获取打开套接字时使用的默认超时(以毫秒为单位)
     */
    public func getDefaultTimeout(): Int64 {
        return this._timeout_;
    }

    /**
     * 获取客户端的套接字绑定到的本地地址。
     *
     * 如果在客户端套接字未打开时调用此方法抛出异常
     */
    public func getLocalAddress(): Array<UInt8> {
        if (this.isOpen()) {
            return checkOpen().localAddress.getAddressBytes();
        } else {
            throw NoneValueException("UdpSocket is not open")
        }
    }

    /**
     * 获取本地主机上打开的用于连接的套接字的端口号。
     *
     * 如果在客户端套接字未打开时调用此方法,则抛出异常
     */
    public func getLocalPort(): UInt16 {
        if (this.isOpen()) {
            return IPSocketAddress.parse(checkOpen().localAddress.toString()).port
        } else {
            throw NoneValueException("UdpSocket is not open")
        }
    }

    /**
     * 如果客户端具有当前打开的套接字,则为 true。
     */
    public func isOpen(): Bool {
        return this._isOpen_;
    }

    /**
     * 在本地主机上的第一个可用端口处打开 DatagramSocket。
     *
     * _socket_ 设置为新打开的套接字,Udpsocket需要调用bind()绑定
     * _isOpen_ 设置为 true,receiveTimeout和sendTimeout设置为默认超时
     */
    public func open() {
        this._socket_ = this._socketFactory_.createDatagramSocket()
        this._socket_.getOrThrow().bind();
        this.receiveTimeout = Duration.millisecond * this._timeout_
        this.sendTimeout = Duration.millisecond * this._timeout_
        _isOpen_ = true
    }

    /**
     * 在本地主机上的指定端口处打开 DatagramSocket
     *
     * _socket_ 设置为新打开的套接字,Udpsocket需要调用bind()绑定
     * _isOpen_ 设置为 true,receiveTimeout和sendTimeout设置为默认超时
     */
    public func open(port: UInt16) {
        this._socket_ = _socketFactory_.createDatagramSocket(port)
        this._socket_.getOrThrow().bind();
        this.receiveTimeout = Duration.millisecond * this._timeout_
        this.sendTimeout = Duration.millisecond * this._timeout_
        _isOpen_ = true
    }

    /**
     * 在指定端口的本地主机上的指定地址处打开 DatagramSocket
     *
     * _socket_ 设置为新打开的套接字,Udpsocket需要调用bind()绑定
     * _isOpen_ 设置为 true,receiveTimeout和sendTimeout设置为默认超时
     */
    public func open(IPSocketAddress: IPSocketAddress) {
        this._socket_ = _socketFactory_.createDatagramSocket(IPSocketAddress: IPSocketAddress)
        this._socket_.getOrThrow().bind();
        this.receiveTimeout = Duration.millisecond * this._timeout_
        this.sendTimeout = Duration.millisecond * this._timeout_
        _isOpen_ = true
    }

    /**
     * 设置DatagramSocketClient使用的DatagramSocketFactory来打开DatagramSockets
     *
     * 如果DatagramSocketFactory为none,则使用默认的DatagramSocketFactory
     */
    public func setDatagramSocketFactory(factory: ?DatagramSocketFactory) {
        _socketFactory_ = factory ?? DEFAULT_SOCKET_FACTORY
    }

    /**
     * 设置打开套接字时使用的默认超时
     *
     * 此方法应在调用 {@link #open open()} 之前使用
     */
    public func setDefaultTimeout(timeout: Duration) {
        _timeout_ = timeout.toMilliseconds()
    }

    /**
     * 设置打开套接字时使用的默认超时
     *
     * 此方法应在调用 {@link #open open()} 之前使用
     */
    public func setDefaultTimeout(timeout: Int64) {
        _timeout_ = timeout
    }
}