using System;
using System.Net;
namespace Hazel.Udp.FewerThreads
{
    /// 
    /// Represents a servers's connection to a client that uses the UDP protocol.
    /// 
    /// 
    public sealed class ThreadLimitedUdpServerConnection : UdpConnection
    {
        public readonly DateTime CreationTime = DateTime.UtcNow;
        /// 
        ///     The connection listener that we use the socket of.
        /// 
        /// 
        ///     Udp server connections utilize the same socket in the listener for sends/receives, this is the listener that 
        ///     created this connection and is hence the listener this conenction sends and receives via.
        /// 
        public ThreadLimitedUdpConnectionListener Listener { get; private set; }
        public ThreadLimitedUdpConnectionListener.ConnectionId ConnectionId { get; private set; }
        /// 
        ///     Creates a UdpConnection for the virtual connection to the endpoint.
        /// 
        /// The listener that created this connection.
        /// The endpoint that we are connected to.
        /// The IPMode we are connected using.
        internal ThreadLimitedUdpServerConnection(ThreadLimitedUdpConnectionListener listener, ThreadLimitedUdpConnectionListener.ConnectionId connectionId, IPEndPoint endPoint, IPMode IPMode, ILogger logger)
            : base(logger)
        {
            this.Listener = listener;
            this.ConnectionId = connectionId;
            this.EndPoint = endPoint;
            this.IPMode = IPMode;
            State = ConnectionState.Connected;
            this.InitializeKeepAliveTimer();
        }
        /// 
        protected override void WriteBytesToConnection(byte[] bytes, int length)
        {
            if (bytes.Length != length) throw new ArgumentException("I made an assumption here. I hope you see this error.");
            // Hrm, well this is inaccurate for DTLS connections because the Listener does the encryption which may change the size.
            // but I don't want to have a bunch of client references in the send queue...
            // Does this perhaps mean the encryption is being done in the wrong class?
            this.Statistics.LogPacketSend(length);
            Listener.SendDataRaw(bytes, EndPoint);
        }
        /// 
        /// 
        ///     This will always throw a HazelException.
        /// 
        public override void Connect(byte[] bytes = null, int timeout = 5000)
        {
            throw new InvalidOperationException("Cannot manually connect a UdpServerConnection, did you mean to use UdpClientConnection?");
        }
        /// 
        /// 
        ///     This will always throw a HazelException.
        /// 
        public override void ConnectAsync(byte[] bytes = null)
        {
            throw new InvalidOperationException("Cannot manually connect a UdpServerConnection, did you mean to use UdpClientConnection?");
        }
        /// 
        ///     Sends a disconnect message to the end point.
        /// 
        protected override bool SendDisconnect(MessageWriter data = null)
        {
            if (!Listener.RemoveConnectionTo(this.ConnectionId)) return false;
            this._state = ConnectionState.NotConnected;
            
            var bytes = EmptyDisconnectBytes;
            if (data != null && data.Length > 0)
            {
                if (data.SendOption != SendOption.None) throw new ArgumentException("Disconnect messages can only be unreliable.");
                bytes = data.ToByteArray(true);
                bytes[0] = (byte)UdpSendOption.Disconnect;
            }
            try
            {
                this.WriteBytesToConnection(bytes, bytes.Length);
            }
            catch { }
            return true;
        }
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                SendDisconnect();
            }
            Listener.RemovePeerRecord(this.ConnectionId);
            base.Dispose(disposing);
        }
    }
}