using HslCommunication;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.Core.Net;
using HslCommunication.Enthernet.Redis;
using HslCommunication.MQTT;
using MWS.Data.InterFace;
using MWS.Data.Manager;
using MWS.Domain.Dto;
using MWS.Library;
using MWS.Library.Common;
using MWS.Model;
using MWS.Model.Model;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MWS.Domain.Tasks.Weighing
{
    public class ShunZhouSocketClient : NetworkXBase, IDisposable
    {
        public delegate void SocketDataArrivalDelegate(ReceviceDataModel data);
        public event SocketDataArrivalDelegate OnSocketDataArrival;
        public delegate void SocketConnectedDelegate();
        public event SocketConnectedDelegate OnSocketConnected;
        public delegate void SocketDisconnectedDelegate();
        public event SocketDisconnectedDelegate OnSocketDisconnected;
        public delegate void SocketCheckStateChangeDelegate(int id, bool state);
        public event SocketCheckStateChangeDelegate OnSocketCheckStateChanged;
        private bool disposedValue;
        private Socket socketCore = null;
        private bool connectState = true;
        public int _frequency = 100;
        private SystemConfig _systemConfig;
        private int deviceId = 0;
        public RedisClient _redisClient;
        private MqttClient _mqttClient;
        private Device device;
        private List<Zigbee> zigbees;
        private IZigbee zigbeeManager;

        /// <summary>
        /// 获取或设置当前的服务器连接是否成功，定时获取本属性可用于实时更新连接状态信息
        /// </summary>
        public bool IsConnected { get; private set; } = false;
        private byte[] buffer = new byte[2048];
        private ManualResetEvent TimeoutObject = new ManualResetEvent(false);
        private IPEndPoint ipEndPoint;
        private static IByteTransform byteTransform = new ReverseBytesTransform();
        private readonly int dataLength = 12;
        private Timer timerCheck; //定时器，用来心跳校验的
        private Timer timerFree;//定时器，用来校验是否空闲
        private List<FreeCheckModel> freeChecks = new List<FreeCheckModel>();
        public WeightDataPickModel dataPick;
        public WeightDataPickModel dataPickB;
        private IWeight weightManager;
        public ShunZhouSocketClient(int frequency, SystemConfig systemConfig, string ipAddress, ushort port, short dataLength, Device device)
        {
            this.device = device;
            deviceId = device.Id;
            zigbeeManager = new ZigbeeManager();
            zigbees = zigbeeManager.GetAll(deviceId);
            _frequency = frequency;
            _systemConfig = systemConfig;

            dataPick = new WeightDataPickModel { Weights = new List<WeightDataPickModel.WeightItem>() };
            dataPickB = new WeightDataPickModel { Weights = new List<WeightDataPickModel.WeightItem>() };
            weightManager = new WeightManager();
            ipEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
            this.dataLength = dataLength;
            //初始化发射端空闲检测计时器
            timerFree = new Timer(new TimerCallback(TimerFreeServer), null, 2000, 1000);
            //初始化心跳检测计时器
            timerCheck = new Timer(new TimerCallback(TimerCheckServer), null, 2000, 10 * 1000);
            OnSocketDisconnected += SocketClient_OnSocketDisconnected;
            #region Redis
            var redisAddress = ConfigurationManager.AppSettings["RedisAddress"];
            var redisPort = ConfigurationManager.AppSettings["RedisPort"];
            var redisTagDb = ConfigurationManager.AppSettings["RedisTagDb"];
            _redisClient = new RedisClient(redisAddress, int.Parse(redisPort), null);
            var operateResult = _redisClient.ConnectServer();
            _redisClient.SelectDB(int.Parse(redisTagDb));
            if (!operateResult.IsSuccess)
            {
                LogHelper.WriteLog("Redis服务连接失败！错误：" + operateResult.Message);
            }
            #endregion     
            #region MQTT
            var mqttPort = ConfigurationManager.AppSettings["MqttPort"];
            _mqttClient = new MqttClient(new MqttConnectionOptions
            {
                ClientId = Guid.NewGuid().ToString("N").ToUpper(),
                IpAddress = "127.0.0.1",
                Port = mqttPort.ToInt(),
            });
            operateResult = _mqttClient.ConnectServer();
            if (!operateResult.IsSuccess)
            {
                LogHelper.WriteLog("MQTT服务连接失败！错误：" + operateResult.Message);
            }
            _mqttClient.OnNetworkError += MqttClient_OnNetworkError;
            #endregion
            foreach (var item in zigbees)
            {              
                freeChecks.Add(new FreeCheckModel { LastReceviceTime = DateTime.Now, Id = item.Code });
            }
        }

        private void MqttClient_OnNetworkError(object sender, EventArgs e)
        {
            // 当网络异常的时候触发，可以在此处重连服务器
            if (sender is MqttClient client)
            {
                // 开始重连服务器，直到连接成功为止
                Console.WriteLine("网络异常，准备5秒后重新连接。");
                while (true)
                {
                    // 每隔5秒重连
                    Thread.Sleep(5000);
                    Console.WriteLine("准备重新连接服务器...");
                    OperateResult connect = client.ConnectServer();
                    if (connect.IsSuccess)
                    {
                        // 连接成功后，可以在下方break之前进行订阅，或是数据初始化操作
                        Console.WriteLine("连接服务器成功！");
                        break;
                    }
                    Console.WriteLine("连接失败，准备5秒后重新连接。");
                }
            }
        }
        private async void SocketClient_OnSocketDisconnected()
        {
            //Console.WriteLine("连接服务断开");
            await ConnectServer();
        }

        /// <summary>
        /// 连接Socket服务
        /// </summary>
        public async Task ConnectServer()
        {
            IsConnected = false;
            socketCore?.Close();
            //创建并连接Socket
            OperateResult<Socket> connect = await CreateSocketAndConnectAsync(ipEndPoint, 2000);
            if (connect.IsSuccess)
            {
                socketCore = connect?.Content;
                try
                {
                    //注册消息接收回调函数
                    socketCore.BeginReceive(buffer, 0, 2048, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socketCore);
                }
                catch
                {
                    //等待并重连Socket
                    SleepAndReconnect();
                }
                IsConnected = true;
                //Socket连接成功后调用连接成功委托事件
                OnSocketConnected?.Invoke();
            }
            else
            {
                SleepAndReconnect();
            }
        }
        /// <summary>
        /// 等待并重连Socket(递归，失败则一直循环调用，10秒一次)
        /// </summary>
        private void SleepAndReconnect()
        {
            ThreadPool.QueueUserWorkItem(async action =>
            {
                Console.WriteLine("连接服务失败，10秒后重连");
                Thread.Sleep(10000);
                if (!IsConnected)
                {
                    await ConnectServer();
                }
            });
        }
        public void ConnectClose()
        {
            IsConnected = false;
            Thread.Sleep(50);
            socketCore?.Close();
        }
        private void ReceiveCallBack(IAsyncResult ar)
        {
            if (ar.AsyncState is Socket peerSock)
            {
                try
                {
                    int bytesRead = peerSock.EndReceive(ar);
                    if (bytesRead >= 20)
                    {
                        int index = 0;
                        while (index < bytesRead)
                        {
                            //00 01 02 24 31 26 20 20 31 39 36 32 20 20 20 20 20 30 0D 6C
                            if (buffer[index] == 02)
                            {
                                byte[] tmpData = new byte[dataLength];
                                Array.Copy(buffer, index, tmpData, 0, dataLength);
                                var id = byteTransform.TransInt16(buffer, index - 2);

                                if (tmpData != null&CheckLRC(tmpData))
                                {
                                    //var temp = SeparatingHighAndLowBits(tmpData[1]);
                                    float weight = 0;
                                    var temp = TranByte.GetValue(tmpData[1]);//小数点位数
                                    var B = TranByte.byteToArray(tmpData[2]);
                                    var netWeight = Convert.ToInt32(Encoding.ASCII.GetString(tmpData, 5, 6));//净重
                                    var tareWeight = Convert.ToInt32(Encoding.ASCII.GetString(tmpData, 11, 6)); //皮重

                                    netWeight = B[7] == 1 ? netWeight : netWeight - tareWeight;
                                    netWeight = B[6] == 1 ? -netWeight : netWeight;
                                    float weightTemp =(float)(netWeight / Math.Pow(10, temp - 2));//计算几位小数点
                                    float tareWeightTemp = (float)(tareWeight / Math.Pow(10, temp - 2));//计算几位小数点
                                    bool overLoad = Convert.ToBoolean(B[5]);
                                    bool isSteady = !Convert.ToBoolean(B[4]);

                                    var freeCheck = freeChecks.FirstOrDefault(c => c.Id == id);
                                    if (freeCheck != null)
                                    {
                                        freeCheck.LastReceviceTime = DateTime.Now;
                                    }
                                    else
                                    {
                                        freeChecks.Add(new FreeCheckModel { LastReceviceTime = DateTime.Now, Id = id });
                                    }
                                    #region 发送实时数据
                                    var mqttMessage = new MqttApplicationMessage
                                    {
                                        Topic = "WeightRealTimeData"+id,
                                        Payload = Encoding.UTF8.GetBytes(new JObject(
                                                                            
                                                                            new JProperty("DeviceId", id),
                                                                            new JProperty("NetWeight", weightTemp),
                                                                            new JProperty("OverLoad", overLoad),
                                                                            new JProperty("IsSteady", isSteady)
                                                                            ).ToString())
                                    };
                                    var publishResult = _mqttClient.PublishMessage(mqttMessage);
                                    if (!publishResult.IsSuccess)
                                    {
                                        LogHelper.WriteLog("发布主题“WeightRealTimeData”时失败！错误：" + publishResult.Message);
                                    }
                                    #endregion

                                    if (id==zigbees[0].Code)
                                    {
                                        if (weightTemp > _systemConfig.WeightItem.Threshold)
                                        {
                                            if (dataPick.EntranceId == 0)
                                            {
                                                dataPick.EntranceId = 1;
                                            }
                                            if (isSteady && dataPick.Weights.Count <= 10)
                                            {
                                                dataPick.Weights.Add(new WeightDataPickModel.WeightItem { Weight = weightTemp, Time = DateTime.Now, IsSteady = isSteady });
                                            }
                                        }
                                        else
                                        {
                                            if (weightTemp < _systemConfig.WeightItem.PutThreshold)
                                            {
                                                var weights = dataPick.Weights.Take(6);
                                                if (dataPick.Weights.Any())
                                                {
                                                    if (weights.Count() > 3)
                                                    {
                                                        weight = weights.OrderBy(c => c.Weight).Skip(1).Take(4).Average(c => c.Weight);
                                                    }
                                                    else
                                                    {
                                                        weight = dataPick.Weights.Average(c => c.Weight);
                                                    }
                                                    //weight = weight - weightTemp;//做差
                                                    if (weight > _systemConfig.WeightItem.Threshold)
                                                    {
                                                        var teamId = GetCurrentTeam();
                                                        weightManager.Insert(new Record
                                                        {

                                                            Device = zigbees[0].Id,
                                                            Entrance = dataPick.EntranceId,
                                                            NetWeight = weight,
                                                            TareWeight = tareWeightTemp,
                                                            GrossWeight = weight + tareWeightTemp,
                                                            CreationTime = DateTime.Now,
                                                            Team = teamId
                                                        });
                                                        #region 发送称重数据
                                                        mqttMessage = new MqttApplicationMessage
                                                        {
                                                            Topic = "WeighingData",
                                                            Payload = Encoding.UTF8.GetBytes(new JObject(
                                                                                                new JProperty("DeviceId", zigbees[0].Id),
                                                                                                new JProperty("NetWeight", weight),
                                                                                                new JProperty("TeamId", teamId)
                                                                                                ).ToString())
                                                        };
                                                        publishResult = _mqttClient.PublishMessage(mqttMessage);
                                                        if (!publishResult.IsSuccess)
                                                        {
                                                            LogHelper.WriteLog("发布主题“WeighingData”时失败！错误：" + publishResult.Message);
                                                        }
                                                        #endregion

                                                    }
                                                    //清空开斗数据记录
                                                    dataPick.Weights.Clear();
                                                    //weightData.Records.Clear();
                                                }
                                                if (dataPick.EntranceId != 0)
                                                {
                                                    dataPick.EntranceId = 0;
                                                }
                                            }
                                           
                                        }
                                    }
                                    else
                                    {
                                        if (weightTemp > _systemConfig.WeightItem.Threshold)
                                        {
                                            if (dataPickB.EntranceId == 0)
                                            {
                                                dataPickB.EntranceId = 1;
                                            }
                                            if (isSteady && dataPickB.Weights.Count <= 10)
                                            {
                                                dataPickB.Weights.Add(new WeightDataPickModel.WeightItem { Weight = weightTemp, Time = DateTime.Now, IsSteady = isSteady });
                                            }
                                        }
                                        else
                                        {
                                            if (weightTemp < _systemConfig.WeightItem.PutThreshold)
                                            {
                                                var weights = dataPickB.Weights.Take(6);
                                                if (dataPickB.Weights.Any())
                                                {
                                                    if (weights.Count() > 3)
                                                    {
                                                        weight = weights.OrderBy(c => c.Weight).Skip(1).Take(4).Average(c => c.Weight);
                                                    }
                                                    else
                                                    {
                                                        weight = dataPickB.Weights.Average(c => c.Weight);
                                                    }
                                                    //weight = weight - weightTemp;//做差
                                                    //if (weight < 0 || weight > _systemConfig.WeightItem.Threshold)
                                                    if (weight > _systemConfig.WeightItem.Threshold)
                                                    {
                                                        var teamId = GetCurrentTeam();
                                                        weightManager.Insert(new Record
                                                        {

                                                            Device = zigbees[1].Id,
                                                            Entrance = dataPickB.EntranceId,
                                                            NetWeight = weight,
                                                            TareWeight = tareWeightTemp,
                                                            GrossWeight = weight + tareWeightTemp,
                                                            CreationTime = DateTime.Now,
                                                            Team = teamId
                                                        });
                                                        #region 发送称重数据
                                                        mqttMessage = new MqttApplicationMessage
                                                        {
                                                            Topic = "WeighingData",
                                                            Payload = Encoding.UTF8.GetBytes(new JObject(
                                                                                                new JProperty("DeviceId", zigbees[1].Id),
                                                                                                new JProperty("NetWeight", weight),
                                                                                                new JProperty("TeamId", teamId)
                                                                                                ).ToString())
                                                        };
                                                        publishResult = _mqttClient.PublishMessage(mqttMessage);
                                                        if (!publishResult.IsSuccess)
                                                        {
                                                            LogHelper.WriteLog("发布主题“WeighingData”时失败！错误：" + publishResult.Message);
                                                        }
                                                        #endregion

                                                    }
                                                    //清空开斗数据记录
                                                    dataPickB.Weights.Clear();
                                                    //weightData.Records.Clear();
                                                }
                                                if (dataPickB.EntranceId != 0)
                                                {
                                                    dataPickB.EntranceId = 0;
                                                }
                                            }
                                        }
                                    }
                                  

                                    break;
                                }
                            }
                            index++;
                        }
                    }
                    //Thread.Sleep(_frequency);
                    socketCore.BeginReceive(buffer, 0, 2048, SocketFlags.None, new AsyncCallback(ReceiveCallBack), socketCore);
                }
                catch (ObjectDisposedException)
                {
                    CoreSocket?.Close();
                    return;
                }
                catch (Exception ex)
                {
                    OnSocketDisconnected?.Invoke();
                    return;
                }
            }
        }
        private short GetCurrentTeam()
        {
            var exitResult = _redisClient.ExistsKey("CurrentTeam");
            if (exitResult.IsSuccess)
            {
                if (exitResult.Content == 1)
                {
                    OperateResult<string> readResult = _redisClient.ReadKey("CurrentTeam");
                    if (readResult.IsSuccess && !readResult.Content.IsNullOrEmptyOrWhiteSpace())
                    {
                        JObject jObject = JObject.Parse(readResult.Content);
                        return jObject.GetValue("Id").ToObject<short>();
                    }
                }
            }
            return 0;
        }

        private void TimerFreeServer(object obj)
        {
            if (IsConnected)
            {
                foreach (var freeCheck in freeChecks)
                {
                    bool isTimeout = freeCheck.IsTimeout(10);
                    if (isTimeout && freeCheck.LastState)
                    {

                        //OnSocketCheckStateChanged.Invoke(freeCheck.Id, false);
                    }
                    else if (!isTimeout && !freeCheck.LastState)
                    {
                        //OnSocketCheckStateChanged.Invoke(freeCheck.Id, true);
                    }
                    freeCheck.LastState = !isTimeout;
                    #region 发送实时数据
                    var mqttMessage = new MqttApplicationMessage
                    {
                        Topic = "ZigbeeState",
                        Payload = Encoding.UTF8.GetBytes(new JObject(
                                                            new JProperty("DeviceId", freeCheck.Id),
                                                            new JProperty("State", freeCheck.LastState)
                                                            ).ToString())
                    };
                    var publishResult = _mqttClient.PublishMessage(mqttMessage);
                    if (!publishResult.IsSuccess)
                    {
                        LogHelper.WriteLog("发布主题“ZigbeeState”时失败！错误：" + publishResult.Message);
                    }
                    #endregion
                }
            }
        }
      

        private async void TimerCheckServer(object state)
        {
            if (socketCore != null)
            {
                await SendKeepAliveData();
            }
        }
        private async Task SendKeepAliveData()
        {
            byte[] send = SoftBasic.HexStringToBytes("00 01 02 03 04 05 06 07 08 09");
            try
            {
                await SendAsync(socketCore, send);
            }
            catch
            {
                OnSocketDisconnected?.Invoke();
            }
        }
        private bool CheckLRC(byte[] buff)
        {
            byte[] check_sum = new byte[2] { 0, 0 };
            for (int i = 0; i < buff.Length - 1; i++)
                check_sum[0] += buff[i];
            check_sum[1] = (byte)(check_sum[0]);
            return buff.Last() == check_sum[1];
        }
        public void Dispose()
        {
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
        /// <summary>
        /// 释放当前的对象
        /// </summary>
        /// <param name="disposing"></param>
        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    timerFree?.Dispose();
                    timerCheck?.Dispose();
                    Thread.Sleep(50);
                    OnSocketDataArrival = null;
                    OnSocketDisconnected = null;
                    socketCore?.Close();
                    TimeoutObject.Close();
                }
                disposedValue = true;
            }
        }
    }
}
