using Windows.Storage.Streams;
using Windows.Networking;
using Windows.Networking.Sockets;
using System;
using System.Threading.Tasks;
using System.Threading;

namespace Plane.Communication
{
    public partial class UdpThroughDtuServiceConnection
    {
        #region Fields
        private IOutputStream _outputStream;
        private DatagramSocket _socket;
        private DataWriter _writer;
        private readonly AutoResetEvent _loginResultSignal = new AutoResetEvent(false);
        private bool _isLogined;                        //是否已登录成功
        private byte[] loginResultDatagram;             //记录登陆时返回的报文数据
        #endregion

        /// <summary>
        /// 初始化 <see cref="UdpThroughDtuServiceConnection"/> 的实例。
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        public UdpThroughDtuServiceConnection(string ip, int port)
        {
            DtuServiceIP = ip;
            DtuServicePort = port;
        }

        /// <summary>
        /// 登陆到Dtu服务端。
        /// 登陆方式极其简单，只需发送一个数据包（内含希望连接到的飞机ID）
        /// </summary>
        /// <returns></returns>
        public virtual async Task OpenAsync()
        {
            if (IsOpen)
            {
                return;
            }

            await Task.Run(async () =>
            {
                var bytes = ConstructHandShakeDatagramToDTUService();
                if (_socket == null)
                {
                    var hostName = new HostName(DtuServiceIP);
                    _socket = new DatagramSocket();

                    //对于WP平台，若没有打开网络连接（比如WIFI），下面语句会报：
                    //“No such host is known.(Exception from HRESULT:0x80072AF9)”
                    try
                    {
                        await _socket.ConnectAsync(hostName, DtuServicePort.ToString());
                    }
                    catch (Exception ex)
                    {
                        _socket = null;
                        if ((uint)ex.HResult == 0x80072AF9)
                        {
                            throw new Exception("请检查是否已打开网络连接。");
                        }
                        else
                        {
                            throw ex;
                        }
                    }
                    _socket.MessageReceived += _socket_MessageReceived;
                    _outputStream = await _socket.GetOutputStreamAsync(hostName, DtuServicePort.ToString());
                    _writer = new DataWriter(_outputStream);
                }

                _writer.WriteBytes(bytes);
                await _writer.StoreAsync();

                //等待服务端的应答，获取是否连接成功飞机的结果
                if (_loginResultSignal.WaitOne(10000))
                {
                    var ret = AnalyzeHandShakeDatagramFromDTUService(loginResultDatagram);
                    if (ret == DTUHandShakeResult.Successful)
                    {
                        IsOpen = true;
                        _isLogined = true;
                    }
                    else if (ret == DTUHandShakeResult.Occupied)
                    {
                        throw new Exception("飞机已被占用！");
                    }
                    else if (ret == DTUHandShakeResult.NotExisted)
                    {
                        throw new Exception("飞机不存在！");
                    }
                    else
                    {
                        throw new Exception("未知状态！");
                    }
                }
                else
                {
                    IsOpen = false;
                    throw new Exception("连接超时！");
                }
            }).ConfigureAwait(false);
        }

        public virtual void Close()
        {
            IsOpen = false;
            _outputStream?.Dispose();
            _writer?.Dispose();
            if (_socket != null)
            {
                try
                {
                    _socket.MessageReceived -= _socket_MessageReceived;
                    _socket?.Dispose();
                    _socket = null;
                }
                catch (Exception) { }
            }
        }

        private async Task SendAsync(byte[] datagram, int bytes)
        {
            if (datagram.Length != bytes)
            {
                Array.Resize(ref datagram, bytes);
            }
            try
            {
                _writer.WriteBytes(datagram);
                await _writer.StoreAsync();
            }
            catch
            {
                //发送异常可以内部消化之，因为Udp发送失败也无办法，同时也不用冒泡异常
            }
        }

        private void _socket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
        {
            try
            {
                var reader = args.GetDataReader();
                if (!_isLogined)
                {
                    loginResultDatagram = new byte[reader.UnconsumedBufferLength];
                    reader.ReadBytes(loginResultDatagram);
                    _loginResultSignal.Set();
                }
                else
                {
                    //以后收到的直接透传
                    var data = new byte[reader.UnconsumedBufferLength];
                    reader.ReadBytes(data);
                    EnqueueDatagram(data);
                }
            }
            catch (Exception)
            {
            }
        }
    }
}
