using ProjectManagementSystem.Device.Extenions;
using RRQMCore.ByteManager;
using RRQMSocket;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;

namespace ProjectManagementSystem.Device.Core
{
    public class CommunicationUdp : CommunicationBase, ICommunication
    {
        private string ipHost;
        private string defaultRemotePoint;
        private UdpSession udpSession;

        private List<IPAddress> HostIPAddressList;
        private List<EndPoint> endPoints = new List<EndPoint>();

        public bool IsStart { get { return udpSession?.ServerState == ServerState.Running; } }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ipAddress">监听的IP</param>
        /// <param name="port">监听的端口</param>
        /// <param name="remoteIpAddress">默认发送数据的IP</param>
        /// <param name="remotePort">默认发送数据的端口</param>
        public CommunicationUdp(string ipAddress, int port, string remoteIpAddress = null, int remotePort = 0)
        {
            ipHost = $"{ipAddress}:{port}";

            if(string.IsNullOrEmpty(remoteIpAddress))
            {
                //默认终结点为广播地址
                if (ipAddress == IPAddress.Any.ToString())
                {
                    remoteIpAddress = IPAddress.Loopback.ToString();
                }
                else
                {
                    var hostIpAddress = IPAddress.Parse(ipAddress);
                    var subnetMask = GetSubnetMask(hostIpAddress);
                    var broadcastIP = GetBroadcast(hostIpAddress, subnetMask);

                    remoteIpAddress = broadcastIP.ToString();

                    //string ipStr = ipAddress.Substring(0, ipAddress.LastIndexOf('.') + 1);
                    //remoteIpAddress = $"{ipStr}255";
                }
            }

            if(remotePort <= 0)
            {
                remotePort = port;
            }

            defaultRemotePoint = $"{remoteIpAddress}:{remotePort}";
        }

        /// <summary> 
        /// 获得广播地址 
        /// </summary> 
        /// <param name="ipAddress">IP地址</param> 
        /// <param name="subnetMask">子网掩码</param> 
        /// <returns>广播地址</returns> 
        private IPAddress GetBroadcast(IPAddress ipAddress, IPAddress subnetMask)
        {
            byte[] ipAdd = ipAddress.GetAddressBytes();
            byte[] subnet = subnetMask.GetAddressBytes();
            for (int i = 0; i < ipAdd.Length; i++)
            {
                ipAdd[i] = (byte)(~subnet[i] | ipAdd[i]);
            }
            return new IPAddress(ipAdd);
        }
        /// <summary>
        /// 获取子网掩码
        /// </summary>
        /// <param name="ipAdd">IP地址</param>
        /// <returns></returns>
        private IPAddress GetSubnetMask(IPAddress ipAdd)
        {
            foreach (NetworkInterface adapter in NetworkInterface.GetAllNetworkInterfaces())
            {
                foreach (UnicastIPAddressInformation unicastIPAddressInformation in adapter.GetIPProperties().UnicastAddresses)
                {
                    if (unicastIPAddressInformation.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        if (ipAdd.Equals(unicastIPAddressInformation.Address))
                        {
                            return unicastIPAddressInformation.IPv4Mask;
                        }
                    }
                }
            }
            return IPAddress.Parse("255.255.255.0");
            //throw new ArgumentException($"没有发现该{ipAdd}对应的子网掩码");
        }

        public void Start()
        {
            udpSession = new UdpSession();
            udpSession.Logger = new LogerBox(this.LogMsg);
            udpSession.Received += OnReceived;

            var config = new RRQMConfig();//UDP配置
            config.SetBindIPHost(new IPHost(ipHost));
            config.SetRemoteIPHost(new IPHost(defaultRemotePoint));

            udpSession.Setup(config);//加载配置

            try
            {
                //启动
                udpSession.Start();//启动
                LogMsg($"UDP服务端{ipHost}已启动");

                HostIPAddressList = Dns.GetHostAddresses(Dns.GetHostName()).ToList();
                var address = HostIPAddressList.FirstOrDefault(d => ipHost.Contains(d.ToString()));
                if (address == null)
                {
                    LogMsg($"注意：UDP监听IP不属于本机IP");
                }
            }
            catch (Exception ex)
            {
                LogMsg($"UDP服务端{ipHost}启动异常", ex);
                Stop();
            }

        }

        private void OnReceived(EndPoint client, ByteBlock byteBlock)
        {
            //过滤自身发出的数据
            var iPEndPoint = client as IPEndPoint;
            if (HostIPAddressList.FindIndex(d => d.Equals(iPEndPoint.Address)) >= 0)
            {
                return;
            }

            int index = endPoints.FindIndex(d => d.Equals(client));
            if (index < 0)
            {
                endPoints.Add(client);
            }

            byte[] data = new byte[byteBlock.Length];
            Buffer.BlockCopy(byteBlock.Buffer, 0, data, 0, data.Length);

            LogMsg($"{client}: UdpRecv: {data.Byte2HexString()}");

            DataReceived?.Invoke(client.ToString(), data);
        }

        public void SendCommand(byte[] data, string address)
        {
            if (string.IsNullOrEmpty(address))
            {
                SendTo(data, udpSession.RemoteIPHost.EndPoint);

                for (int i = 0; i < endPoints.Count; i++)
                {
                    SendTo(data, endPoints[i]);
                }
                return;
            }

            for (int i = 0; i < endPoints.Count; i++)
            {
                var endPoint = endPoints[i];
                if (endPoint.ToString().StartsWith(address))
                {
                    SendTo(data, endPoint);
                }
            }
        }

        private void SendTo(byte[] data, EndPoint endPoint)
        {
            try
            {
                udpSession.Send(endPoint, data, 0, data.Length);
                LogMsg($"{endPoint}: UdpSend: {data.Byte2HexString()}");
            }
            catch (Exception ex)
            {
                LogMsg($"{endPoint}: UdpSendErr: {data.Byte2HexString()}", ex);
            }
        }

        public void Stop()
        {
            if (udpSession != null)
            {
                udpSession.Stop();
            }
        }
    }
}
