using log4net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VIZ.Framework.Domain;

namespace VIZ.Framework.Connection
{
    /// <summary>
    /// UDP连接
    /// </summary>
    public class UdpConnection : IDisposable
    {
        /// <summary>
        /// 日志
        /// </summary>
        private static ILog log = LogManager.GetLogger(typeof(UdpConnection));

        /// <summary>
        /// 最大数据长度
        /// </summary>
        public const int MAX_BUFFER_SIZE = 1024 * 10;

        /// <summary>
        /// UDP客户端
        /// </summary>
        public UdpClient UdpClient { get; private set; }

        /// <summary>
        /// IP地址
        /// </summary>
        public string IP { get; private set; }

        /// <summary>
        /// 端口
        /// </summary>
        public int Port { get; private set; }

        /// <summary>
        /// 包解析器
        /// </summary>
        public IConnPackageProvider PackageProvider { get; set; }

        /// <summary>
        /// 终结点管理器集合
        /// </summary>
        private Dictionary<string, UdpEndpointManager> endpointManagers = new Dictionary<string, UdpEndpointManager>();

        /// <summary>
        /// 数据接收线程
        /// </summary>
        private Thread recvTrhead;

        /// <summary>
        /// 是否已经开始接收数据
        /// </summary>
        private bool IsRecvStart;

        /// <summary>
        /// 绑定IP和端口
        /// </summary>
        /// <param name="ip">IP</param>
        /// <param name="port">端口</param>
        public void Binding(string ip, int port)
        {
            this.IP = ip;
            this.Port = port;

            IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ip), port);
            this.UdpClient = new UdpClient(ipep);
        }

        /// <summary>
        /// 连接
        /// </summary>
        public void Start()
        {
            this.IsRecvStart = true;

            this.recvTrhead = new Thread(this.ReceiveMessage);
            this.recvTrhead.Start();
        }

        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            try
            {
                this.IsRecvStart = false;
                this.UdpClient?.Close();
                this.UdpClient?.Dispose();

                this.recvTrhead = null;
            }
            catch (Exception ex)
            {
                log.Error(ex);
            }
        }

        /// <summary>
        /// 销毁
        /// </summary>
        public void Dispose()
        {
            this.Stop();
        }

        /// <summary>
        /// 获取UDP终结点管理器
        /// </summary>
        /// <param name="key">终结点管理器键</param>
        /// <returns>终结点管理器</returns>
        public UdpEndpointManager GetEndpointManager(string key)
        {
            this.endpointManagers.TryGetValue(key, out UdpEndpointManager manager);
            return manager;
        }

        /// <summary>
        /// 添加UDP终结点管理器
        /// </summary>
        /// <param name="manager">终结点管理器</param>
        public void AddEndpointManager(UdpEndpointManager manager)
        {
            manager.UdpClient = this.UdpClient;
            manager.UdpConnection = this;
            lock (this.endpointManagers)
            {
                this.endpointManagers[manager.Key] = manager;
            }
        }

        /// <summary>
        /// 移除UDP终结点管理器
        /// </summary>
        /// <param name="key">终结点管理器键</param>
        public void RemoveEndpointManager(string key)
        {
            lock (this.endpointManagers)
            {
                if (this.endpointManagers.ContainsKey(key))
                {
                    this.endpointManagers.Remove(key);
                }
            }
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        private void ReceiveMessage()
        {
            while (this.IsRecvStart)
            {
                try
                {
                    IPEndPoint endport = new IPEndPoint(IPAddress.Any, 0);
                    byte[] buffer = this.UdpClient.Receive(ref endport);

                    if (this.PackageProvider == null)
                        continue;

                    // 执行解释器
                    ConnPackageInfo info = new ConnPackageInfo();
                    info.LocalIP = this.IP;
                    info.LocalPort = this.Port;
                    info.RemoteIP = endport.Address.ToString();
                    info.RemotePort = endport.Port;
                    info.Data = buffer;
                    info.DataSize = buffer.Length;

                    this.PackageProvider.Execute(info);
                }
                catch (Exception ex)
                {
                    log.Error(ex);
                }
            }
        }
    }
}
