using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HTCoreServiceApp.DriverInterface;
using HTCoreServiceApp.Models;
using System.Threading;
using HTCoreServiceApp.Common;
using System.Collections;
using System.Text.RegularExpressions;
using HTCoreServiceApp.DataHandle;
using System.Timers;
using Newtonsoft.Json.Linq;
using System.Net;
using static System.Runtime.InteropServices.JavaScript.JSType;
using Pipelines.Sockets.Unofficial.Arenas;
using System.Numerics;
using NModbus.Device;
using System.Net.Sockets;
using NModbus;

namespace HTCoreServiceApp.Communicate.Modbus
{
    public class ModbusClientExample : IDriverEntity
    {

        public string DeviceId { get; set; }

        private string DriverName { get; set; }

        public bool IsConnected { get; set; }

        public int Timeout { get; set; }

        public bool isScan { get; set; }

        public System.Timers.Timer ScanTimer { get ; set; }

		System.Timers.Timer timerCheck = new System.Timers.Timer();

		public string Address = "127.0.0.1";
        private int Port = 0;
        private byte SlaveID = 0;

        private string PLCType = "";

        public TcpClient TcpClient;
        public IModbusMaster Master;
        private byte[] Buffer = new byte[65536];
        private byte[] DB_A = new byte[1024];
        private byte[] DB_B = new byte[1024];
        private byte[] DB_C = new byte[1024];
        private readonly object _lock = new();


        private List<MetaGroup> MetaGroups;
        private List<MetaTag> MetaTags;
        //List<Argument> argument, List<MetaGroup> metaGroups,List<MetaTag> metaTags
        public ModbusClientExample(TagGroup tagGroup) 
        {
            var factory = new ModbusFactory();
			

            Address =tagGroup.Arguments.Where(x => x.PropertyName == "ServerName").First().PropertyValue;
            Port = int.Parse(tagGroup.Arguments.Where(x => x.PropertyName == "Port").First().PropertyValue);
            SlaveID = byte.Parse(tagGroup.Arguments.Where(x => x.PropertyName == "SlaveID").First().PropertyValue);
            PLCType = tagGroup.Arguments.Where(x => x.PropertyName == "PLCType").First().PropertyValue;
            Timeout = int.Parse(tagGroup.Arguments.Where(x => x.PropertyName == "TimeOut").First().PropertyValue);
            MetaGroups =tagGroup.Groups;
            MetaTags = tagGroup.Tags;//只有一组
            DriverName = tagGroup.DriverName;

            TcpClient = new TcpClient(Address,Port);

			//Client.SetConnectionType(0x3);
			TcpClient.ReceiveTimeout = Timeout;
            TcpClient.SendTimeout = Timeout;
			//Client.RecvTimeout = 30;

			Master = factory.CreateMaster(TcpClient);

			//Connect();
			timerCheck.Interval = 2000;
			StartConnectCheck();

            StartScan();
        }

        public void StartScan()
        {
            var groups = MetaGroups.GroupBy(x => new { x.GroupId });

            var scanTagsParas = new List<ScanTagsPara>();

            foreach (var metaGroup in groups)
            {
                ScanTagsPara scanTagsPara = new ScanTagsPara();
                scanTagsPara.GroupName = MetaGroups.Where(x=>x.GroupId == metaGroup.Key.GroupId).ToList().First().GroupName;
                scanTagsPara.UpdateRate = MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().UpdateRate;
                scanTagsPara.IsConsecutive = (bool)MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().IsActive;
                scanTagsPara.IsActive = MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().IsActive;
                scanTagsPara.ScanTags  = MetaTags.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList();
                short driverID = (short)MetaGroups.Where(x => x.GroupId == metaGroup.Key.GroupId).ToList().First().DriverId;
                scanTagsPara.DriverName = DriverName;
                scanTagsPara.AddressArg = PLCHelper.GetAddressArg_Modbus_Word(scanTagsPara.ScanTags);
                scanTagsParas.Add(scanTagsPara);
            }
            //if 添加连续或单变量读取标记
            //if (scanTagsPara.IsConsecutive == true)
            //{
            //    if (scanTagsPara.IsActive == true && scanTagsPara.ScanTags.Count > 0)
            //        Task.Run(() =>
            //        {
            //            ScanConsecutive(scanTagsPara);
            //        }) ;
            //}
            //else
            //{
            //    if (scanTagsPara.IsActive == true && scanTagsPara.ScanTags.Count > 0)
            //        Task.Run(() =>
            //        {
            //            Scan(scanTagsPara);
            //        });

            //}
            ScanConsecutive(scanTagsParas);
        }

        /// <summary>
        /// 启动连续读取
        /// </summary>
        /// <param name="scanTagsParas"></param>
        public void ScanConsecutive(List<ScanTagsPara> scanTagsParas)
        {
            ScanTimer timerScan = new ScanTimer();
            if (scanTagsParas[0].UpdateRate > 0)
            {
                timerScan.Interval = (double)scanTagsParas[0].UpdateRate;
            }
            else
            {
                timerScan.Interval = 1000;
            }
            timerScan.ScanTagsParas= scanTagsParas;

            //timerScan.AddressArg = PLCHelper.GetAddressArg(scanTagsParas.ScanTags);

            timerScan.Elapsed += new System.Timers.ElapsedEventHandler(TimerScanConsecutive_Elapsed);
            timerScan.Enabled = true;
        }
        /// <summary>
        /// 连续读取timer事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TimerScanConsecutive_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            ScanTimer timerScan = (ScanTimer)sender;
            List<ScanTagsPara> scanTagsParas = timerScan.ScanTagsParas;
            //AddressArg addressArg = timerScan.AddressArg;
            foreach (var scanTagsPara in scanTagsParas)
            {
                if (scanTagsPara.IsActive == false)
                    break;
                if (TcpClient.Connected)
                {

                    var addressArg = scanTagsPara.AddressArg;

                    //object _lock = new();

                    Task task = Task.Factory.StartNew(() =>
                    {
                        lock (_lock)
                        {
                            try
                            {
                                var tcollstart = DateTime.Now;
                                //CodingTools.StartTime = DateTime.Now.Millisecond;//de
                                //scanTagsPara.ScanTags.Min(x => x.Address);
                                //scanTagsPara.ScanTags.Min(x => x.Address);提高速度应放在timer事件前，测试后更改
                                int startAdress = addressArg.Start;
                                
                                int amount = addressArg.Length;
                                ushort[] buffer = new ushort[amount];
                                int pdu = 122;
                                string Result = "";
                                if (amount <= pdu)
                                {
                                    Result = ReadHoldingRegisters( startAdress, amount, ref buffer);
                                }
                                else
                                {
                                    //int additional = amount- pdu;
                                    int forCount = (int)Math.Ceiling(float.Parse(((float)amount / (float)pdu).ToString()));
                                    List<ushort[]> bytes= new List<ushort[]>();
                                    for (int i = 0; i < forCount; i++)
                                    {
                                        if ((amount - (i * pdu)) > pdu)
                                        {
                                            int start = startAdress + i * pdu;
                                            ushort[] bytes1 = new ushort[pdu];
                                            Result = ReadHoldingRegisters(start, pdu, ref bytes1);
                                            bytes.Add(bytes1);
                                        }
                                        else
                                        {
                                            int start = startAdress + i * pdu;
                                            ushort[] bytes1 = new ushort[amount - (i * pdu)];
                                            Result = ReadHoldingRegisters(start, amount - (i * pdu), ref bytes1);
                                            bytes.Add(bytes1);
                                        }
                                    }
                                    var byetstemp = Array.Empty<ushort>();
                                    foreach (var buff in bytes)
                                    {
                                        byetstemp = byetstemp.Concat(buff).ToArray();
                                    }
                                    buffer = byetstemp;
                                }
                                var tcollend = DateTime.Now;
                                var timecolldiff =  tcollend - tcollstart;
                                Console.WriteLine($"ModbusTcp读取耗时：{timecolldiff}");
                                //CodingTools.EndTime = DateTime.Now.Millisecond;//de
                                //var timess = (CodingTools.EndTime - CodingTools.StartTime).ToString();
                                //Console.WriteLine($"读取耗时：{timess}");
                                if (Result == "")
                                {
                                    var t1 = DateTime.Now.Millisecond;
                                    Console.WriteLine(string.Join(",", buffer) + "ModbusTcp读取完成：" + " 字节数：" + amount.ToString());
                                    //Task<int> saveResult = DataExtract.DataExtractCAsync(scanTagsPara.ScanTags, scanTagsPara.GroupName, scanTagsPara.DriverName, buffer, addressArg);
                                    DataExtractNMB.DataExtractCAsync(scanTagsPara.ScanTags, scanTagsPara.GroupName, scanTagsPara.DriverName, buffer, addressArg);
                                    var t2 = DateTime.Now.Millisecond;
                                    var timediff = t2 - t1;
                                    Console.WriteLine($"ModbusTcp缓存和储存耗时：{timediff}");
                                    {
                                        //if (saveResult.Result == 0)
                                        //{
                                        //    //logger
                                        //}
                                        //else
                                        //{
                                        //    //logger
                                        //}

                                    }
                                }
                                else if (Result != "")
                                {
                                    Console.WriteLine("MB读取失败："+" ERROR：" + Result.ToString());
								    LogHelper.LogWrite("MB读取失败："+" ERROR：" + Result.ToString());
								    //Close();
								    //Thread.Sleep(50);
								    //Connect();
							}

                            }
                            catch (Exception ex)
                            {
                                LogHelper.LogWrite($"{DriverName}读取失败，MB错误信息{ex}");
                            }
                        }

                    });
                }
                else
                {
					LogHelper.LogWrite($"{DriverName}读取失败，MB未连接");
					//Connect();
				}
                Thread.Sleep(50);
            }
        }
        /*
        /// <summary>
        /// 启动单个变量读取
        /// </summary>
        /// <param name="scanTagsPara"></param>
        public void Scan(ScanTagsPara scanTagsPara)
        {
            ScanTimer timerScan = new ScanTimer();
            if (scanTagsPara.UpdateRate > 0)
            {
                timerScan.Interval = (double)scanTagsPara.UpdateRate;
            }
            else
            {
                timerScan.Interval = 1000;
            }
            timerScan.ScanTagsPara = scanTagsPara;
            timerScan.Elapsed += new System.Timers.ElapsedEventHandler(TimerScan_Elapsed);
            timerScan.Enabled = true;
        }

        /// <summary>
        /// 单个变量读取timer事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TimerScan_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
            ScanTimer timerScan = (ScanTimer)sender;
            ScanTagsPara scanTagsPara = timerScan.ScanTagsPara;
            AddressArg addressArg = timerScan.AddressArg;
            if (IsConnected = true)
            {
                object _lock = new();

                Task task = Task.Run(() =>
                {
                    lock (_lock)
                    {
                        try
                        {
                            //CodingTools.StartTime = DateTime.Now.Millisecond;//de
                            //scanTagsPara.ScanTags.Min(x => x.Address);
                            //scanTagsPara.ScanTags.Min(x => x.Address);提高速度应放在timer事件前，测试后更改
                            int startAdress = addressArg.Start;
                            int Area = 132;
                            int DBNumber = 0;
                            if (scanTagsPara.GroupName.Contains("DB"))
                            {
                                DBNumber = int.Parse(Regex.Replace(scanTagsPara.GroupName, "[a-z]", "", RegexOptions.IgnoreCase));
                            }
                            else
                            {
                                DBNumber = int.Parse(scanTagsPara.GroupName);
                            }
                            int amount = addressArg.Length;
                            int WordLen = 2;
                            byte[] buffer = new byte[amount];
                            int Result = ReadArea(Area, DBNumber, startAdress, amount, WordLen, ref buffer);
                            //CodingTools.EndTime = DateTime.Now.Millisecond;//de
                            //var timess = (CodingTools.EndTime - CodingTools.StartTime).ToString();
                            //Console.WriteLine($"读取耗时：{timess}");
                            if (Result == 0)
                            {
                                //Console.WriteLine(string.Join(",", buffer) + "读取完成：DB" + DBNumber.ToString() + ",字节数：" + amount.ToString());
                                int saveResult = DataExtract.DataExtractConsecutive(timerScan.ScanTagsPara.ScanTags, scanTagsPara.GroupName, timerScan.ScanTagsPara.DriverName, buffer, addressArg);
                                {
                                    if (saveResult == 0)
                                    {
                                        //logger
                                    }
                                    else
                                    {
                                        //logger
                                    }

                                }
                            }
                            else if (Result != 0)
                            {
                                Console.WriteLine("读取失败：DB" + DBNumber.ToString() + ",ERRORCODE：" + Result.ToString());
                                Close();
                                Thread.Sleep(200);
                                Connect();
                            }

                        }
                        catch (Exception ex)
                        {

                        }
                    }

                });
            }
            else
            {
                Connect();
            }
        }
        */
        public string ReadHoldingRegisters(int start, int Amount,ref ushort[] buffer)
        {
            object _lock = new();
            var Result = "";
            lock (_lock)
            {
                try
                {
                    int reval = 0;
                    int SizeRead = 0;
                    
                    //buffer = new byte[65536];
                    buffer = Master.ReadHoldingRegisters(SlaveID, (ushort)start, (ushort)Amount);
                    return Result;
                }
                catch(Exception ex)
                {
                    return Result = ex.ToString();
                }
            }
        }

		public void StartConnectCheck()
		{
			
			timerCheck.Elapsed += new System.Timers.ElapsedEventHandler(TimerCheckConnect_Elapsed);
			timerCheck.Enabled = true;
		}

        private void TimerCheckConnect_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
        {
			timerCheck.Enabled = false;
			if (TcpClient.Connected)
            {
				timerCheck.Enabled = true;
				return;
            }
            else
            {
                Connect();
			}
        }


		public bool Connect()
        {
            var Result = "";
            try
            {

                TcpClient.Connect(Address, Port);
            }
            catch(Exception ex)
            {
                Result = ex.ToString();
            }
            if (Result == "" && TcpClient.Connected)
            {
                IsConnected = true;
                //log
                LogHelper.LogWrite($"MB连接成功，返回值:{Result}");
                timerCheck.Enabled = true;
                return true;
            }
            else
            {
                IsConnected = false;
                //log
                LogHelper.LogWrite($"MB连接失败，返回值:{Result}");
                timerCheck.Enabled = true;
                return false;
            }

        }

        public bool Close()
        {
            var Result = "";
            try
            {
                TcpClient.Dispose();
                Master.Dispose();
            }
            catch (Exception ex)
            {
                Result = ex.ToString(); 
            }
            if (Result == "")
            {
                IsConnected = false;
				//log
				LogHelper.LogWrite($"MB关闭连接成功，返回值:{Result}");
				return true;
            }
            else
            {
                IsConnected = false;
				LogHelper.LogWrite($"MB关闭连接失败，返回值:{Result}");
				//log
				return false;
            }
        }

        public void Dispose()
        {

            try
            {
                TcpClient.Dispose();
                Master?.Dispose();
                //log
            }
            catch (Exception ex)
            {
                //log
            }
            
        }

        public int Write(string writePara)
        {
            throw new NotImplementedException();
        }
        /*
        /// <summary>
        /// 写入方法
        /// </summary>
        /// <param name="writePara"></param>
        /// <returns></returns>
        public int Write(string writePara)
        {
           int reCode = 0;
           JObject valueJson = JObject.Parse(writePara);
           var srcValue = valueJson[propertyName: "Value"].ToString();
           var writeAddress = valueJson[propertyName: "WriteAddress"].ToString();
           var dataType =short.Parse(valueJson[propertyName: "DataType"].ToString());
           var groupName = valueJson[propertyName: "GroupName"].ToString();

           var array = writeAddress.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
           var DBNum_string = array[0].ToString();
           var DBNum = int.Parse(Regex.Replace(DBNum_string, "[a-z]", "", RegexOptions.IgnoreCase));

           var address = array[1].ToString();
           string startaddress = Regex.Replace(address, "[a-z]", "", RegexOptions.IgnoreCase);
           int address_dem = 0;
           int address_int = 0;
           if (startaddress.Contains(".") && startaddress.Length > 2)
           {
               try
               {
                   string[] address_arr = startaddress.Split('.');
                   address_int = int.Parse(address_arr[0]);
                   address_dem = int.Parse(address_arr[1]);
               }
               catch (Exception exp)
               {
                   //logger
               }
           }
           else
           {
               address_int = int.Parse(startaddress);
           }

           byte[] revbuffer_w;// = new byte[maxreg + 2];
           decimal value = decimal.Parse(srcValue);
           lock (_lock)
           {
               if (dataType == (short)DataType.BOOL)
               {
                   var boolbyte = new byte[1];
                   int rebooltempread = ReadHoldingRegisters(132, DBNum, address_int, 1, 2, ref boolbyte);
                   Thread.Sleep(50);
                   Console.WriteLine(rebooltempread);
                   var temval = (float)value;
                   bool bl = temval > 0.0f ? true : false;
                   //var rtbyte = revbuffer_w[address_int];
                   //var rtbuff = Sharp7.S7. rtbyte
                   Sharp7.S7.SetBitAt(ref boolbyte, 0, address_dem, bl);
                   reCode = Client.WriteArea(132, DBNum, address_int, 1, 2, boolbyte);
               }
               else if (dataType == (short)DataType.FLOAT)
               {
                   revbuffer_w = new byte[4];
                   Sharp7.S7.SetRealAt(revbuffer_w, 0, (float)value);
                   reCode = Client.WriteArea(132, DBNum, address_int, 4, 2, revbuffer_w);
               }
               else if (dataType == (short)DataType.WORD)
               {
                   revbuffer_w = new byte[2];
                   Sharp7.S7.SetWordAt(revbuffer_w, 0, (ushort)value);
                   reCode = Client.WriteArea(132, DBNum, address_int, 2, 2, revbuffer_w);
               }
               else if (dataType == (short)DataType.BYTE)
               {
                   revbuffer_w = new byte[1];
                   Sharp7.S7.SetByteAt(revbuffer_w, 0, (byte)value);
                   reCode = Client.WriteArea(132, DBNum, address_int, 1, 2, revbuffer_w);
               }
               else
               {

               }
           }
           Console.WriteLine(writePara);
           return reCode;
        }
        */
    }
}
