using System;
using System.Collections.Concurrent;
using System.ComponentModel.Design;
using System.Text;
using TouchSocket.Core;
using TouchSocket.Sockets;
using WCS.BLL.Tool;


namespace WCS.BLL
{
    /// <summary>
    /// 对TouchSocket的封装 主要完成TCP的连接 状态更新 发送接收通信
    /// </summary>
    public class TCPClient
    {
        public string RemoteIPHost { get; set; } = "127.0.0.1:20002";

        public string BindIPHost { get; set; } = "127.0.0.1:20003";

        public string ShelfTypeName { get; set; }
        public bool IsOnline { get; set; } = false;

        //第一次连接是否已连接
        public bool IsFirstConnected { get; set; } = false;

        //can模块协议前缀
        public readonly byte[] Prefix = new byte[] { 0x08, 0x00, 0x00 };
        //can模块协议前缀长度
        public readonly int PreFixLength = 3;
        //协议数据部分长度
        public readonly int DataLength = 10;

        public ConcurrentDictionary<int, MessageDto> MessageList { get; set; } = new ConcurrentDictionary<int, MessageDto>();

        public TcpClient tcpClient { get; set; }

        public object receivdLockObject = new object();
        public object sendLockObject = new object();

        public bool Connect()
        {
            try
            {
                tcpClient.Connect();//调用连接，当连接不成功时，会抛出异常。
                return true;
            }
            catch (Exception e)
            {
                //连接失败
                return false;
            }
        }

        /// <summary>
        /// 初始化配置连接
        /// </summary>
        /// <param name="remoteIPHost"></param>
        public TCPClient(string remoteIPHost, string bindIPHost, string shelfTypeName)
        {
            try
            {
                RemoteIPHost = remoteIPHost;
                BindIPHost = bindIPHost;
                ShelfTypeName = shelfTypeName;

                tcpClient = new TcpClient();

                if (string.IsNullOrEmpty(BindIPHost))
                {
                    //载入配置
                    tcpClient.Setup(new TouchSocketConfig()
                        .SetRemoteIPHost(new IPHost(RemoteIPHost))
                        //.SetBindIPHost(BindIPHost)
                        .ConfigurePlugins(a =>
                        {
                            //配置断线重连
                            a.UseReconnection(-1, true, 1000);
                            a.Add<HeartbeatAndReceivePlugin>();
                        })
                        .ConfigureContainer(a =>
                        {
                            //添加控制台日志注入
                            a.AddConsoleLogger();
                        }));
                }
                else
                {
                    //载入配置
                    tcpClient.Setup(new TouchSocketConfig()
                        .SetRemoteIPHost(new IPHost(RemoteIPHost))
                        .SetBindIPHost(new IPHost(BindIPHost))
                        .ConfigurePlugins(a =>
                        {
                            //配置断线重连
                            a.UseReconnection(-1, true, 1000);
                            a.Add<HeartbeatAndReceivePlugin>();
                        })
                        .ConfigureContainer(a =>
                        {
                            //添加控制台日志注入
                            a.AddConsoleLogger();
                        }));
                }

                //添加接收事件 匹配已发送的指令
                tcpClient.Received += (client, e) =>
                {
                    var data = e.ByteBlock.Buffer.Take((int)e.ByteBlock.Length).ToArray();
                    Task.Run(() =>
                    {
                        Logs.Write($"【校验发送接收 开始】{ShelfTypeName}" + BitConverter.ToString(data), LogsType.InstructionResend);
                        if (ShelfTypeName == "信息化货架")
                        {
                            //协议拆包
                            var len = data.Length;
                            //灯控制统一返回的是 FF-00-00-0A-00-02-D7-B5
                            if (len == 8)
                            {
                                if (data[4] == 0x00 && data[0] == 0xFF && data[1] == 0x00 && (data[5] == 0x01 || data[5] == 0x02))
                                {
                                    //查询当前板子是否有待验证的指令
                                    var message = new MessageDto();
                                    var firstMessage = MessageList.Select(t => new { Id = t.Key, Value = t.Value })
                                                                  .OrderBy(t => t.Value.CreateTime)
                                                                  .First();
                                    if (firstMessage != null)
                                    {
                                        MessageList.TryRemove(firstMessage.Id, out message);
                                        Logs.Write($"【信息化货架】以下指令已不重发：{BitConverter.ToString(firstMessage.Value.Message)}", LogsType.InstructionResend);
                                    }
                                }
                            }
                        }
                        else if (ShelfTypeName == "液晶货架")
                        {
                            Logs.Write($"【液晶货架】接收到指令{BitConverter.ToString(data)}", LogsType.InstructionResend);
                        }
                        //智能货架
                        else
                        {
                            var len = data.Length;
                            for (int index = 0; index < data.Length - PreFixLength; index++)
                            {
                                //协议拆包 通过前缀校验是否为完整数据包
                                var prefixInData = data.Skip(index).Take(PreFixLength);
                                var isEqual = prefixInData.SequenceEqual(Prefix);
                                if (isEqual)
                                {
                                    var dataTemp = data.Skip(index).Take(PreFixLength + DataLength).ToArray();
                                    if (dataTemp.Length < PreFixLength + DataLength)//拆包后不满足一条指令的长度
                                    {
                                        continue;
                                    }
                                    //获取返回指令的板子ID
                                    var boardId = (dataTemp[PreFixLength + 0] << 8) + dataTemp[PreFixLength + 1];
                                    //查询当前板子是否有待验证的指令
                                    var message = new MessageDto();
                                    MessageList.TryGetValue(boardId, out message);
                                    //功能位校验 功能位相同视为已响应指令 删除对应的指令
                                    if (message?.Message[PreFixLength + 2] == dataTemp[PreFixLength + 2])
                                    {
                                        MessageList.TryRemove(boardId, out message);
                                    }
                                    index += (PreFixLength + DataLength - 2);//每次循环index会+1 所以这里-1
                                }
                            }
                        }
                        Logs.Write($"【校验发送接收 结束】{ShelfTypeName}" + BitConverter.ToString(data), LogsType.InstructionResend);
                    });
                    return null;
                };

                tcpClient.Connected += (client, e) =>
                {
                    this.IsOnline = true;
                    return EasyTask.CompletedTask;
                };

                tcpClient.Disconnected += (client, e) =>
                {
                    this.IsOnline = false;
                    return EasyTask.CompletedTask;
                };

                //配置数据重发机制
                Task.Run(async () =>
                {
                    while (true)
                    {
                        try
                        {
                            //TODO如果指令已发两次 则取消重发
                            await Task.Delay(100);
                            if (MessageList.Count > 0)
                            {
                                var failedMessage = MessageList.Where(t => t.Value.LastSendTime < DateTime.Now.AddSeconds(-1))
                                                               .ToList();
                                foreach (var message in failedMessage)
                                {
                                    Logs.Write("【指令重发】" + BitConverter.ToString(message.Value.Message) + "指令超时1s未响应", LogsType.InstructionResend);
                                }
                                MessageList.RemoveWhen(t => t.Value.SendTimes >= 2);
                                foreach (var item in MessageList)
                                {
                                    if (item.Value.LastSendTime < DateTime.Now.AddSeconds(-1))
                                    {

                                        Send(item.Value.Message);
                                        item.Value.SendTimes++;
                                        item.Value.LastSendTime = DateTime.Now;
                                        Logs.Write("【指令重发】" + BitConverter.ToString(item.Value.Message) + "已进行重发", LogsType.InstructionResend);
                                    }
                                }
                            }
                        }
                        catch
                        {
                        }
                    }
                });
            }
            catch (Exception ex)
            {
            }
        }

        public void ReConnectAsync()
        {
            tcpClient.TryConnectAsync();
        }


        public void Send(byte[] message, bool IsReSend = false)
        {
            try
            {
                var boardId = (message[3] << 8) + message[4];

                if (boardId != 2047 && IsReSend == false)
                {
                    MessageList.TryAdd(boardId, new MessageDto()
                    {
                        ID = boardId,
                        Message = message,
                        SendTimes = 1
                    });
                }

                lock (sendLockObject)
                {
                        tcpClient.Send(message);

                        var clientIpHost = tcpClient.IP + ":" + tcpClient.Port;
                        Logs.Write($"【发送{clientIpHost}】{BitConverter.ToString(message)}", LogsType.Instructions);

                        //发送自带15ms间隔
                        Thread.Sleep(18);
                }
            }
            catch (Exception ex)
            {
                Logs.Write("【发送指令时发生异常】" + ex.Message, LogsType.Instructions);
                //因异常断连时（网线已经被断了） 手动重连一次
                if (ex is NotConnectedException)
                {
                    Task.Run(() =>
                    {
                        ReConnectAsync();
                    });
                }
                throw ex;
            }
        }

        //生成协议明细
        public byte[] GenerateMessage(int boardId, byte[] data)
        {
            var message = new byte[Prefix.Length + 2 + data.Length];
            var boardIdData = BitConverter.GetBytes(unchecked((short)boardId));
            // 检查是否需要交换字节  
            if (BitConverter.IsLittleEndian)
            {
                // 如果是小端序系统，则交换字节  
                byte temp = boardIdData[0];
                boardIdData[0] = boardIdData[1];
                boardIdData[1] = temp;
            }

            Buffer.BlockCopy(Prefix, 0, message, 0, Prefix.Length);
            Buffer.BlockCopy(boardIdData, 0, message, Prefix.Length, boardIdData.Length);
            Buffer.BlockCopy(data, 0, message, Prefix.Length + boardIdData.Length, data.Length);
            return message;
        }

    }

    //发送指令的记录
    public class MessageDto
    {
        public int ID { get; set; }
        /// <summary>
        /// 最后一次发送时间
        /// </summary>
        public DateTime LastSendTime { get; set; } = DateTime.Now;

        public DateTime CreateTime { get; set; } = DateTime.Now;
        /// <summary>
        /// 发送内容
        /// </summary>
        public byte[] Message { get; set; }
        /// <summary>
        /// 发送次数
        /// </summary>
        public int SendTimes { get; set; } = 0;//第几次发送
    }

}
